天天看點

android view繪制原理

本文詳細介紹view的繪制過程,view的繪制過程由measure、layout、draw 三部分組成。
           

measure : 測量view的寬和高。

layout : 确定view 四個點(top、left、 right、bottom)在父容器的最終位置。

draw : 将view繪制到螢幕上面。

view的繪制首先調用ViewRootImpl類的performTraversales方法,裡面決定要不要measure、layout、draw 。具體過程如下:

android view繪制原理

會先周遊ViewGroup的子View,其實最後就是調用View的Measure、Layout、Draw三個方法。

1:measure

測量過程主要與MeasureSpec和LayoutParams有關系,MeasureSpec是一個32位int型數,高2位代表SpecMode ,低30位 表示 SpecSize。

SpecMode有3種:UNSPECIFIED、EXACTLY、AT_MOST。

UNSPECIFIED:父容器不對View做任何的限制,要多大給多大。

EXACTLY:父容器已經檢測出View所需的大小,其值就是SpecSize。

AT_MOST:父容器指定了一個可用大小,View不能超過這個值。

LayoutParams有3個:FILL_PARENT、WRAP_CONTENT 、MATCH_PARENT。

子View的MeasureSpec的值由父容器的MeasureSpec和子View的 LayoutParams決定。

具體來說:如果子View的LayoutParams為MATCH_PARENT,父容器的SpecMode為AT_MOST,那麼子View也是AT_MOST,且其大小不會超過父容器的剩餘空間。

2:layout

public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != ) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
            mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnLayoutChangeListeners != null) {
                ArrayList<OnLayoutChangeListener> listenersCopy =
                        (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
                int numListeners = listenersCopy.size();
                for (int i = ; i < numListeners; ++i) {
                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
                }
            }
        }

        mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
    }
           

在view的layout函數中,首先會通過setFrame擷取到 mLeft、mTop、mBottom、mRight4個點的值,然後調用onLayout函數。

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }
           

可以看出,在View的onLayout函數中并沒有具體的方法實作,onLayout跟具體的布局有關系,具體可以看LinearLayout的函數實作。

3:draw

Draw函數主要是将View繪制到螢幕上,可以從源碼上面看出主要包含以下幾步,這裡就不貼源碼了:

1:繪制背景

2:繪制自己

3:繪制children

4:繪制裝飾