講真,自我感受,個人水平真的是渣的一匹,好多東西都只停留在知道和會用的階段,也想去研究原理和底層的實現,但是一看到代碼就懵逼了,而後就看不下去了,html
說本身不着急都是騙人的,我本身都不信,前兩天買了本《Android 羣英傳》,江湖上都說這是一本初級過渡到中級不錯的進階書,因此準備看一下,纔看了兩天,今天android
看到了view的測量及繪製,還有自定義view(還沒看完),學到什麼就寫篇博客吧,算是對本身所學的一個總結和記錄吧,也能夠督促本身,若是有講的不對的地方或者canvas
有歧義的地方,歡迎你們吐槽批評我!app
轉載請註明出處:Android view的測量及繪製ide
正文開始(手動鼓掌)佈局
view的測量:spa
首先須要知道的是view的三種測量模式:rest
一、EXACTLY:精確值模式,當咱們對view的layout_width和layout_height屬性指定具體的數值的時候,好比layout_width=「100dp」或者指定爲match_parent時,系統code
進行測量的時候,使用的是這種模式。xml
二、AT_MOST:最大值模式,當咱們對view的layout_width和layout_height屬性指定爲wrap_content時,即view隨着內容的大小變化而變化,或viewgroup隨着view的
大小變化而變化,這個時候系統進行測量的時候,使用的是這種模式。
三、UNSPECIFIED:這個屬性下不用指定其大小,通常在自定義view時纔會使用(這種模式不是很理解,求指教)
在對view進行測量的時候,須要重寫onMeasure()方法,view默認的onMeasure()方法只支持EXACTLY模式,即指定具體的數值,因此在自定義view的時候必須重寫
onMeasure(),這裏留一個疑問:何時纔會調用Measure方法進行測量?
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
重寫後點super.onMeasure(widthMeasureSpec, heightMeasureSpec);進去看一下源碼,發現系統最終會調用這個方法:
setMeasuredDimension(int widthMeasureSpec,int heightMeasureSpec);
這個方法的做用是將咱們對view設置的寬和高設置進去,因此咱們最終重寫的onMeasure()方法就是這個樣子的,方法內的兩個入參widthMeasureSpec和heightMeasureSpec
就是咱們在xml裏引用這個view時設置的width和height,後面咱們須要根據這兩個值進行判斷,判斷系統要根據什麼測量模式進行測量。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec)); }
前文一直在說系統的測量模式,那麼咱們要怎麼樣才能獲取到系統的測量模式呢?獲取測量模式後還須要獲取具體的測量大小
int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec);
單獨對measureWidth(widthMeasureSpec)講解一下,由於height和width是同樣的。
private int measureWidth(int widthMeasureSpec) { int widthResult = 0; int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY){ widthResult = specSize; }else { widthResult = 400; if (specMode == MeasureSpec.AT_MOST){ widthResult = Math.min(widthResult,specSize); } } return widthResult; }
這部分的理解要聯繫到前面說的3種測量模式,若是是在EXACTLY if (specMode == MeasureSpec.EXACTLY) 這種模式下,咱們已經在xml裏面設置好了具體的數值,因此最後返回的值就是specSize
若是是AT_MOST和UNSPECIFIED這兩種測量模式下,咱們就須要給view一個默認的大小,由於若是沒有給默認的大小的話,系統不知道view的大小,因此view或默認充滿父佈局。這裏默認的大小是400,
你們會發現else裏面還有一個if,對AT_MOST這種模式又進行了判斷,這是由於在這種模式下,view不須要默認的大小,view的是根據內容的大小變換而變化的。
最後就是xml裏面進行引用和效果展現了:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.funny.myapplication.MainActivity"> <com.funny.myapplication.StudyView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View" android:textSize="20sp" android:background="#ccf" android:gravity="center"/> </RelativeLayout>
(1)android:layout_width="wrap_content"
(2)android:layout_width="match_parent"
(3)android:layout_width="400px"
view的繪製:
對view進行繪製須要重寫onDraw()方法,onDraw()方法中會有一個canvas,能夠把這個參數理解成畫板,咱們最終會借用這個畫板進行繪製。
有了畫板,要想繪畫的話固然還須要一支畫筆paint。
@Override protected void onDraw(Canvas canvas) { Paint paint1 = new Paint(); paint1.setColor(getResources().getColor(R.color.colorAccent)); paint1.setStyle(Paint.Style.FILL); Paint paint2 = new Paint(); paint2.setColor(getResources().getColor(R.color.colorPrimary)); paint2.setStyle(Paint.Style.FILL); //開始繪製 canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1); canvas.drawOval(0,0,getMeasuredWidth()-10,getMeasuredHeight()-10,paint2);
//平移30個像素 canvas.translate(30,0 ); super.onDraw(canvas); canvas.restore(); }
接下講解一下這段代碼的功能,首先實例了兩隻畫筆,而且對畫筆設置了顏色和風格,接下來開始繪製,這裏咱們畫的是兩個相互嵌套的橢圓,
canvas.drawOval(0,0,getMeasuredWidth(),getMeasuredHeight(),paint1);這個方法須要5個入參,分別是view相對於父佈局的左、上、右、下的座標,最後一個是進行繪製的畫筆。
系統給咱們提供的drawXXX方法有不少:
canvas繪製的經常使用方法有:
drawColor() 填充顏色
drawLine() 繪製線
drawLines() 繪製線條
drawOval() 繪製圓
drawPath() 繪製路徑
drawPicture() 繪製圖片
drawPoint() 繪製點
drawPoints() 繪製點
drawRGB() 填充顏色
drawRect() 繪製矩形
drawText() 繪製文本
drawTextOnPath() 在路徑上繪製文本
效果展現:
viewGroup的測量:
咱們知道viewGroup管理子view,那麼viewGroup的大小,除設置指定大小外,是根據子view來決定的,viewGroup在測量時會遍歷全部的子view,調用
子view的Measure方法來得到每個子view的測量結果,這樣文章開始留下的疑問就解決了,意不意外~關於viewGroup的繪製,通常狀況下若是不是指定
了viewGroup的背景顏色,viewGroup的onDraw()方法不會被調用,可是viewGroup會使用dispatchDraw()方法來繪製其子view,過程一樣是遍歷全部的子view
,並調用子view的繪製方法來完成繪製。
以上就是我對view的測量及繪製的所有理解,可能在不少人看來仍是處於初級階段,沒有怎麼涉及到源碼,可是仍是但願能幫助到一些人吧,對我本身也是一點點進步嘛,若是你們有什麼
好的理解,歡迎留言哈~~~醬!拜啦!