Android view的測量及繪製

  講真,自我感受,個人水平真的是渣的一匹,好多東西都只停留在知道和會用的階段,也想去研究原理和底層的實現,但是一看到代碼就懵逼了,而後就看不下去了,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_MOSTUNSPECIFIED這兩種測量模式下,咱們就須要給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的測量及繪製的所有理解,可能在不少人看來仍是處於初級階段,沒有怎麼涉及到源碼,可是仍是但願能幫助到一些人吧,對我本身也是一點點進步嘛,若是你們有什麼

好的理解,歡迎留言哈~~~醬!拜啦!

相關文章
相關標籤/搜索