自定義View筆記——基礎瞭解

 View的繪製流程是從ViewRoot的PerformTraversals方法開始的,它經過measur.layout和draw三個過程才能將一個view繪製出來,其中measure用來測量View的寬度,layout用來確定View在父容器中放置的位置,draw負責將View繪製在屏幕上面。


  • onMeasure()
        MeasureSpec

              通過MeasureSpec可以幫助我們測量View
            MeasureSpec是一個32位的Int值,高2位是測量的模式,低30位爲測量的大小。在計算中使用位運算原因是爲了提高並優化效率。
            
            測量模式

        EXACTLY 精準模式 當控件的width或height爲具體數值的時候或者match_parent 時系統用的是EXACTLY模式
        
            AT_MOST 即最大模式 控件的width或height爲wrap_content 隨着控件的子控件或者內容變化而變化
            
            UNSPECIFIED    不指定其大小測量的模式,通常在繪製自定義 View時纔會使用
        
            默認 EXACTLY 如果想使用 wrap_content 必須重寫 onMeasure();

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
在suprer.onMeasure()方法點擊進去看後可以發現最後調用了  setMeasuredDimensionRaw()方法把寬和高設了進去
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;

measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

所以可以這麼重寫onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
width = MeasureWidth(widthMeasureSpec);
height = MeasureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}

private int MeasureHeight(int heightMeasureSpec) {
int result;
int size = MeasureSpec.getSize(heightMeasureSpec);
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 200;//默認的值
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(size, result);
}
}
return result;
}

private int MeasureWidth(int widthMeasureSpec) {
int result;
int size = MeasureSpec.getSize(widthMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 200;
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(size, result);
}
}
return result;
}

       
  • onLayout()

    onLayout方法是ViewGroup中子View的佈局方法,用於放置子View的位置。
    放置子View很簡單,只需在重寫onLayout方法,然後獲取子View的實例,調用子View的layout方法實現佈局。
    在實際開發中,一般要配合onMeasure測量方法一起使用。
    下面是一個自定義ViewGroup的Demo,用onLayout和layout實現子View的水平放置,間隔是20px
//子View的水平間隔
     privatefinalstaticint padding= 20;
@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
// 動態獲取子View實例
for (int i = 0, size = getChildCount(); i < size; i++) {
View view = getChildAt(i)
;
// 放置子View,寬高都是100
view.layout(l, t, l + 100, t + 100);
l += 100 + padding;
}

}
  • onDraw()
根據名字你就能夠判斷出,在這裏才真正地開始對視圖進行繪製。
ViewRoot中的代碼會繼續執行並創建出一個Canvas對象,然後調用View的draw()方法來執行具體的繪製工作。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
  • 在回調父類方法前實現自己的邏輯,對View來說既是在繪製文本內容前
  • 在回調父類方法後實現自己的邏輯,對View來說既是繪製文本內容後

  • View中其他重要的方法

  •     onFinishInflate()    從Xml中加載組件後回調

  •     onSizeChanger();   組件大小改變時回調

  •     onMeasure();         回調該方法進行測量

  •     onLayout();            回調該方法來確定顯示的位置

  •     onTouchEvent();     監聽觸摸事件時回調
發佈了38 篇原創文章 · 獲贊 7 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章