1. 程式人生 > >View的學習記錄(一)

View的學習記錄(一)

View的學習記錄

本篇目的

探討View的繪製過程以及分析View在繪製過程中我們可以參與改造的地方,為自定義View做基礎

View的呼叫

View檢視樹

Activity中檢視的結構

在Activity中,通過setContentView()來設定一個layout佈局,在呼叫後,佈局內容才會顯示出來
DecorView作為View的根佈局來管理所有的View,將檢視呈現在了phoneWindow中,DecorView負責View的所有監聽事件,通過WindowManagerService來接收,通過Activity物件來回調onClickListener

通過設定requestWindowFeature(Window.Feature.NO_TITLE)來取消appBar
在程式碼中,setContentView()之後,ActivityManagerService會回撥onResume()方法,此時,會把DecorView新增到PhoneWindow中,完成介面繪製.

View的測量

onMeasure()

所有的View都自帶一個onMeasure()函式,引數是MeasureSpec類
在onMeasure()方法中,最後,都是通過setMeasuredDimension()方法來儲存測量的結果
下面是ViewGroup的測量原始碼

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec)
, getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }

我們可以通過在onMeasure()方法的最後,手動呼叫setMeasuredDimension()來儲存我們自己測量的結果

MeasurSpec類

MeasurSpec是一個設計短小精悍的類
自身是一個32位int值
高2位位測量模式,可以MeasureSpec.getMode()獲取
低30位為測量的大小,在計算中,使用的是位計算,為了提高計算速率,可以通過MeasureSpec.getSize()獲取

測量模式分三類

  • EXACTLY
    精確測量模式,對應我們指定控制元件大小,例如"100dp"或者match_parent這兩種情況
  • AT_MOST
    最大值模式,對應指定控制元件大小為wrap_content時,隨控制元件內容的變化而變化
    還要不超過父控制元件允許的最大尺寸即可.
  • UNSPECIFIED
    自定義模式
    不會指定大小測量模式,View想多大就多大,通常製作自定義View時使用
    View類預設的onMeasure()方法只支援EXACTLY模式,自定義View繼承自View時,就需要重寫onMeasure()方法,拓展對wrap_content模式的支援(否則,預設match_parent)

重寫onMeasure()

	int MaxSize=200;
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

    private int measureHeight(int heightMeasureSpec) {
        int result=0;
        int specMode=MeasureSpec.getMode(heightMeasureSpec);
        int specSize=MeasureSpec.getSize(heightMeasureSpec);
        if (specMode==MeasureSpec.EXACTLY){
            result=specSize;
        }else {
            result=MaxSize;
            if (specMode==MeasureSpec.AT_MOST){
                result=Math.min(result,specSize);
            }
        }
        return result;
    }

    private int measureWidth(int widthMeasureSpec) {
        int result=0;
        int specMode=MeasureSpec.getMode(widthMeasureSpec);
        int specSize=MeasureSpec.getSize(widthMeasureSpec);
        if (specMode==MeasureSpec.EXACTLY){
            result=specSize;
        }else {
            result=MaxSize;
            if (specMode==MeasureSpec.AT_MOST){
                result=Math.min(result,specSize);
            }
        }
        return result;
    }

View的繪製

測量好View之後,會呼叫onDraw()方法來繪製View.我們可以重寫onDraw()方法,來實現我們想要額外實現的效果
這裡就需要了解下系統2D繪圖API
在onDraw()方法中,會提供一個Canvas物件,控制元件就繪製在Canvas上.
畫布的建立是通過Bitmap物件,

Canvas canvas=new Canvas(bitmap);

bitmap用來承載canvas上所有的畫素資訊.

在onDraw(canvas)中,在canva上繪製背景.然後通過Canvas.drawBitmap()來繪製兩張畫布,在第二張畫布上來繪製我們的View,通過改變bitmap來引起View重繪,從而顯示改變後的Vbitmap

canVas相當於畫布,繪製就需要畫筆,系統提供給我們的畫筆是Paint及其子類TextPaint等

ViewGrouop的測量和繪製

測量

ViewGroup會管理其子View
當ViewGroup的模式為wrap_content時,就需要遍歷所有子View,呼叫子View自身的Measure()方法後,獲得所有子View的大小,來決定自己的大小,其他模式時,會通過具體的指定值來設定自身大小

放置

測量後,需要把子view放置在合適的位置上,就是View的layout過程,同樣是遍歷子View,呼叫子View的layout方法,來指定子View的具體位置

繪製

ViewGroup通常情況下不需要繪製,如果背景指定了顏色或者圖層,才會觸發ViewGroup自身的onDraw()方法.
一般情況下,ViewGroup會呼叫dispatchDraw()方法,來繪製子View,同樣是遍歷所有子View,呼叫子View的Draw()方法.

資料來自於<Android群英傳>