1. 程式人生 > >淺談Android之Activity 視窗顯示流程介紹(一)

淺談Android之Activity 視窗顯示流程介紹(一)

7 Activity 視窗顯示流程介紹

Activity 視窗顯示,其實就是Décor View繪製並顯示的過程,但是在繪製之前,Décor View需要先做下面兩件事情:

1)  確定Décor View的大小

2)  對Décor View進行佈局操作,也就是確定DécorView所有child views的顯示位置

由於Décor View的LayoutParams中寬高預設設定的是MatchParent,所以Décor View初始狀態下,肯定是想要塞滿整個螢幕的,也就是寬高都是螢幕的尺寸,但是想歸想,實際上真正能拿到多大的區域,這個還是要看WMS這個大總管怎麼分配了

簡單點說就是,Décor View可以告知WMS視窗想要顯示的大小,但是WMS會根據狀態列,輸入法視窗是否顯示等等,來對這個大小做適當的裁剪,然後將最終結果告知到Décor View,

我們可以稱這個過程叫Décor View顯示大小確認,這個是通過呼叫WindowSession. Relayout來完成的

ViewRootImpl. performTraversals來觸發完成的,接下去通過程式碼做簡單介紹

7.1 Activity視窗大小獲取

ViewRootImpl.performTraverals函式完整的程式碼太長了,這裡就不全部貼出,接下去分析時會貼出區域性程式碼片段來介紹

首先看ViewRootImpl定義的三個變數:

final Rect mWinFrame; // frame given by window manager.

int mWidth;

int mHeight;

mWinFrame,mWidth和mHeight儲存的,都是WMS返回的視窗大小,如果是第一次執行(mFirst=True), 這三個值預設都為空,如果不是第一次執行,則為當前視窗的顯示大小

接下去設定Activity Window的desire rectangle:

  Rect frame = mWinFrame;

        if (mFirst) {

            mFullRedrawNeeded = true;

            mLayoutRequested = true;

            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL

                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {

                // NOTE -- system code, won't try to do compat mode.

                ……

            } else {

                DisplayMetrics packageMetrics =

                    mView.getContext().getResources().getDisplayMetrics();

                desiredWindowWidth = packageMetrics.widthPixels;

                desiredWindowHeight = packageMetrics.heightPixels;

            }

            ……

        } else {

            desiredWindowWidth = frame.width();

            desiredWindowHeight = frame.height();

            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {

                mFullRedrawNeeded = true;

                mLayoutRequested = true;

                windowSizeMayChange = true;

            }

        }

如果mFirst為true,將desiredWindowWidth和desiredWindowHeight設定為螢幕寬高,否則就將其設定為mWinFrame中儲存的當前視窗的大小值

接著基於desiredWindowWidth和desiredWindowHeight對Décor View做一次measure,確認Décor View的大小:

  boolean layoutRequested = mLayoutRequested && !mStopped;

        if (layoutRequested) {

            final Resources res = mView.getContext().getResources();

            ……

            // Ask host how big it wants to be

            windowSizeMayChange |= measureHierarchy(host, lp, res,

                    desiredWindowWidth, desiredWindowHeight);

}

Décor View的大小確認後,這個只不過是Activity希望請求到的視窗大小,接著還需要呼叫relayoutWindow-> mWindowSession.relayout並傳入請求的大小,然後WMS會根據當前視窗情況返回最終分配的視窗大小,比如,現在有一個輸入法視窗已經彈出,並且Activity設定的windowSoftInputMode為adjustResize,在這種情況下,Activity期望的高度是肯定無法滿足的,WMS會返回減去輸入法視窗的高度,並將結果返回到Activity

接著看mWindowSession.relayout的呼叫:

int relayoutResult = mWindowSession.relayout(

                mWindow, mSeq, params,

                (int) (mView.getMeasuredWidth() * appScale + 0.5f),

                (int) (mView.getMeasuredHeight() * appScale + 0.5f),

                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,

                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,

                mPendingStableInsets, mPendingConfiguration, mSurface);

mWindow是對應的視窗,第三和第四個引數就是視窗期望的寬和高,這裡加上0.5f用以實現四捨五入,WMS那邊調整後的寬高資料會返回到mWinFrame中

mWinFrame儲存的就是這次Activity視窗顯示的最終大小,接著儲存資料到mWidth和

mHeight:

  if (mWidth != frame.width() || mHeight != frame.height()) {

                mWidth = frame.width();

                mHeight = frame.height();

  }

接著重新設定ViewRootImpl關聯的Surface的frame size,以便調整graphic buffer size:

  if (mSurfaceHolder != null) {

                // The app owns the surface; tell it about what is going on.

                if (mSurface.isValid()) {

                    // XXX .copyFrom() doesn't work!

                    //mSurfaceHolder.mSurface.copyFrom(mSurface);

                    mSurfaceHolder.mSurface = mSurface;

                }

               mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);

}

最後,如果WMS返回的最終視窗大小和之前通過desire Width和desire height測量出來的décor view的mesure width和height不一致,那décor view需要基於最終的視窗大小再次做測量操作:

if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()

                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {

     int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);

     int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

     ……

     // Ask host how big it wants to be

     performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

     ……

}

Activity視窗大小的獲取介紹結束,接下去介紹Décor View是怎麼測量自身以及childviews.

7.2 Activity Décorview大小測量(measure)

上頭呼叫performMeasure觸發measure操作,對每個View的寬高來說,必須設定 Measure Mode,主要有三個mode:

Mode name

Desc

MeasureSpec.AT_MOST

對應WRAP_CONTENT

MeasureSpec.EXACTLY

對應MATCH_PARENT

MeasureSpec .UNSPECIFIED

未定義

由於layout預設必須要配置View的寬高屬性,所以MeasureSpec .UNSPECIFIED基本是用不到的

如果要呼叫view.measure對某個view執行measure操作,必須要傳入寬高以及對應的measure mode值,android將寬/高的值以及measure mode合併儲存到int裡頭,具體的資料分佈是這樣的:


Int的0-30位儲存的是寬高的值,30-32位儲存measure mode

對此,Android專門定義了MeasureSpec輔助類用於將measuremode和value合併成int,或者從int裡頭取出value或者measure mode:

Method name

Desc

MeasureSpec. makeMeasureSpec

基於value和measure mode合成measure spec

MeasureSpec.getMode

從measure spec取出高2位的值,也就是mode的值

MeasureSpec.getSize

從measure spec中取出低30位的值,也是就寬/高的值

先來看下ViewRootImpl中getRootMeasureSpec的實現:

  private static int getRootMeasureSpec(int windowSize, int rootDimension) {

        int measureSpec;

        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:

            // Window can't resize. Force root view to be windowSize.

         measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);

            break;

        case ViewGroup.LayoutParams.WRAP_CONTENT:

            // Window can resize. Set max size for root view.

       measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);

            break;

        default:

            // Window wants to be an exact size. Force root view to be that size.

            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);

            break;

        }

        return measureSpec;

 }

從函式中可以看出,MATCH_PARENT對應MeasureSpec.EXACTLY,WRAP_CONTENT則對應

MeasureSpec.AT_MOST,並且,不管是MeasureSpec.EXACTLY還是MeasureSpec.AT_MOST,其對應的value都是the max size of parentview.

接下去看performMeasure:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {

        try {

            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

        } finally {

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);

        }

}

直接呼叫décor view的measure函式,接下去看viewmeasure的完整流程:

由於DecorView是一個FrameLayout,所以接下去專門分析下FrameLayout的measure程式碼,它先呼叫DecorView.measure,由於DecorView沒有實現這個函式,所以最終呼叫View.measure:

  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

        ……

        // Suppress sign extension for the low bytes

        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;

        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;

        final boolean isExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY &&

                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;

        final boolean matchingSize = isExactly &&

                getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) &&

                getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);

        if (forceLayout || !matchingSize &&

(widthMeasureSpec != mOldWidthMeasureSpec ||

heightMeasureSpec != mOldHeightMeasureSpec)) {

            // first clears the measured dimension flag

            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

            resolveRtlPropertiesIfNeeded();

            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);

            if (cacheIndex < 0 || sIgnoreMeasureCache) {

                // measure ourselves, this should set the measured dimension flag back

                onMeasure(widthMeasureSpec, heightMeasureSpec);

                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

            } else {

                long value = mMeasureCache.valueAt(cacheIndex);

                // Casting a long to int drops the high 32 bits, no mask needed

                setMeasuredDimensionRaw((int) (value >> 32), (int) value);

                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

            }

            ……

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;

        }

        mOldWidthMeasureSpec = widthMeasureSpec;

        mOldHeightMeasureSpec = heightMeasureSpec;

        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |

                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension

    }

mPrivateFlags變數基於位運算儲存各種標籤值,比如PFLAG_MEASURED_DIMENSION_SET,用於標識view是否已經measure;mOldWidthMeasureSpec和mOldHeightMeasureSpec儲存上一次請求完成的measure spec;forceLayout標明這次measure是否是由View.requestLayout觸發的,如果為true,則強制執行onMeasure

如果measure行為不是由View.requestLayout觸發的,也就是forceLayout為false,那只有如下兩種情況,會觸發View呼叫onMeasure做重新測量:

1)  Measure mode不全是MeasureSpec.EXACTLY,也就是說,寬和高有一個或者全部被設定成WRAP_CONTENT,這種情況下,isExactly就會為false,還有就是widthMeasureSpec !=mOldWidthMeasureSpec ||

heightMeasureSpec!= mOldHeightMeasureSpec 為true,最後就是mMeasureCache不存在快取資料

2)  Measure mode全是MeasureSpec.EXACTLY,也就是時候,寬和高都被設定成了

MATCH_PARENT,然後measuare spec裡設定的寬高跟getMeasuredWidth()和

getMeasuredHeight()的不一致,這個時候matchingSize肯定為false,

widthMeasureSpec!= mOldWidthMeasureSpec和

heightMeasureSpec!= mOldHeightMeasureSpec肯定也為true,最後同樣的,mMeasureCache不存在快取資料

總結下,如果反過來說呢,如果measure被重複呼叫,什麼情況下onMeasure不會被重複執行:

1)  不是通過requestLayout觸發的

2)  如果widht和height mode都是MATCH_PARENT,並且請求高度和寬度無變化,那這次請求就被會忽略

3)  如果width或者height存在一個mode為WRAP_CONTENT,那就要看請求measure spec是否是一致的,如果一致,那這次請求會被忽略

上面介紹的是onMeasure的入口控制,避免不必要的measure被重複執行進而影響系統執行效率,接下去介紹onMeasure的內部實現

我們都知道整個View是一個樹形結構,這個結構的頂端是Décor View,每一個樹節點只有兩種型別,一個是leaf view,還有就是ViewGroup,leaf view是末端節點,由於其不包含childview,其內部只處理自身邏輯就好了,相對來說會比較簡單;ViewGroup則不一樣,因為它是View的群組,那它肯定就包含child view,child view可能是leaf view或者View Group,相對來說就會複雜點。

所以,如果要從Décor View遍歷整個樹形資料,最重要就是ViewGroup的處理,因為它起到承上啟下的作用。

Android定義的ViewGroup有很多,比如RelativeLayout,LinearLayout,FrameLayout,每個ViewGroup的最大的不同之處,表現在對childview的measure,layout的處理

由於Décor View是一個FrameLayout,所以接下去重點介紹FrameLayout.onMeasure實現,其餘幾個ViewGroup大家可自行研究

由於FrameLayout所有的child view的位置都是相對於FrameLayout的左上角座標原點來放置的,如果FrameLayout定義的寬高為MATCH_PARENT,那所有child view所能達到的最大寬高就是FrameLayout的寬高,反之,如果FrameLayout的寬高定義為WRAP_CONTENT,那FrameLayout的寬高則會被設定成所有child view中size最大的那個。

接下去看程式碼:

//FrameLayout.java

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int count = getChildCount();

        final boolean measureMatchParentChildren =

                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||

                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;

        mMatchParentChildren.clear();

        int maxHeight = 0;

        int maxWidth = 0;

        int childState = 0;

        for (int i = 0; i < count; i++) {

            final View child = getChildAt(i);

            if (mMeasureAllChildren || child.getVisibility() != GONE) {

                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                maxWidth = Math.max(maxWidth,

                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);

                maxHeight = Math.max(maxHeight,

                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);

                childState = combineMeasuredStates(childState, child.getMeasuredState());

                if (measureMatchParentChildren) {

                    if (lp.width == LayoutParams.MATCH_PARENT ||

                            lp.height == LayoutParams.MATCH_PARENT) {

                        mMatchParentChildren.add(child);

                    }

                }

            }

        }

        // Account for padding too

        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();

        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

        // Check against our minimum height and width

        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());

        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Check against our foreground's minimum height and width

        final Drawable drawable = getForeground();

        if (drawable != null) {

            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());

            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());

        }

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),

                resolveSizeAndState(maxHeight, heightMeasureSpec,

                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        count = mMatchParentChildren.size();

        if (count > 1) {

            for (int i = 0; i < count; i++) {

                final View child = mMatchParentChildren.get(i);

                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                int childWidthMeasureSpec;

                int childHeightMeasureSpec;

                if (lp.width == LayoutParams.MATCH_PARENT) {

                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -

                            getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin,

                            MeasureSpec.EXACTLY);

                } else {

                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,

                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin,

                            lp.width);

                }

                if (lp.height == LayoutParams.MATCH_PARENT) {

                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -

                            getPaddingTopWithForeground() - getPaddingBottomWithForeground() -

                            lp.topMargin - lp.bottomMargin,

                            MeasureSpec.EXACTLY);

                } else {

                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,

                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +lp.topMargin + lp.bottomMargin,

                            lp.height);

                }

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            }

        }

}

measureMatchParentChildren如果為true,說明FrameLayout存在寬/高定義為

WRAP_CONTENT,對於同樣是WRAP_CONTENT的child view沒問題,但是對於那些

MATCH_PARENT的child view,則需要在FrameLayout的寬/高確定後,需要再一次執行measure操作

函式先迴圈遍歷all child views,然後呼叫measureChildWithMargins測量child view的寬高,然後通過view的measure width加上對應的margin size得到其在FrameLayout中需要佔用的實際大小,然後將allchild views中,寬度和高度最大的儲存到maxWidth和maxHeight中

如果measureMatchParentChildren為true,還需要將寬高為MATCH_PARENT的child view儲存到mMatchParentChildren中

接著將得到的maxWidth和maxHeight再加上FrameLayout的padding數值,然後再通過

getForeGround拿到fore ground drawable,如果drawable的最小寬高比maxWidth和maxHeight要大,則將其作為maxWidth和maxHeight的大小

maxWidth和maxHeight到目前為止,都是基於childview的資料加上frameLayout的padding和foreground資料算出的,這個可以做為frameLayout的measure result?當然不行,因為child view給出的資料,有可能會超過framelayout對應measure spec的值,所以還需要呼叫

resolveSizeAndState做調整:

//View.java

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {

        int result = size;

        int specMode = MeasureSpec.getMode(measureSpec);

        int specSize =  MeasureSpec.getSize(measureSpec);

        switch (specMode) {

        case MeasureSpec.UNSPECIFIED:

            result = size;

            break;

        case MeasureSpec.AT_MOST:

            if (specSize < size) {

                result = specSize | MEASURED_STATE_TOO_SMALL;

            } else {

                result = size;

            }

            break;

        case MeasureSpec.EXACTLY:

            result = specSize;

            break;

        }

        return result | (childMeasuredState&MEASURED_STATE_MASK);

 }

這個函式很簡單,如果FrameLayout設定的是AT_MOST,即WRAP_CONTENT,如果size比specSize小,那就用size,反之則用specSize;如果FrameLayout設定的是EXACTLY,也就是MATCH_PARENT,則直接設定成specSize

也就是說maxWidth和maxHeight的值,只在寬高為WRAP_CONTENT並且maxWidth和

maxHeight值比對應measure spec的value值小時才有意義

接著呼叫setMeasuredDimension設定resolveSizeAndState返回的寬高結果

到這裡,FrameLayout的measure width/height確定後,對寬高為MATCH_PARENT的child view再一次執行measure操作,由於FrameLayout的measure width/height已定,這裡再得到child view可佔用的最大寬高就很簡單,直接減去FrameLayout的padding和child view的margin值就可以了。

接下去回過頭來分析measureChildWithMargins的實現,由於FrameLayout沒有實現該函式,所以呼叫ViewGroup中的實現:

//ViewGroup.java 

protected void measureChildWithMargins(View child,

            int parentWidthMeasureSpec, int widthUsed,

            int parentHeightMeasureSpec, int heightUsed) {

        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,

                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin

                        + widthUsed, lp.width);

        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,

                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin

                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

    }

通過呼叫getChildMeasureSpec得到child view的measure spec,這個函式傳入是第一個引數是parent measure spec,第二個引數是padding,對應FrameLayout的padding值加上child view的margin值再加上已經使用的寬/高度值,由於FrameLayout所有child view都是基於左上角座標的,所以這裡widhtUsed和heightUsed都是0,最後一個引數是layout中設定的widht和height值

接著看getChildMeasureSpec的實現:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

        int specMode = MeasureSpec.getMode(spec);

        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;

        int resultMode = 0;

        switch (specMode) {

        // Parent has imposed an exact size on us

        case MeasureSpec.EXACTLY:

            if (childDimension >= 0) {

                resultSize = childDimension;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {

                // Child wants to be our size. So be it.

                resultSize = size;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {

                // Child wants to determine its own size. It can't be

                // bigger than us.

                resultSize = size;

                resultMode = MeasureSpec.AT_MOST;

            }

            break;

        // Parent has imposed a maximum size on us

        case MeasureSpec.AT_MOST:

            if (childDimension >= 0) {

                // Child wants a specific size... so be it

                resultSize = childDimension;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {

                // Child wants to be our size, but our size is not fixed.

                // Constrain child to not be bigger than us.

                resultSize = size;

                resultMode = MeasureSpec.AT_MOST;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {

                // Child wants to determine its own size. It can't be

                // bigger than us.

                resultSize = size;

                resultMode = MeasureSpec.AT_MOST;

            }

            break;

        // Parent asked to see how big we want to be

        case MeasureSpec.UNSPECIFIED:

            if (childDimension >= 0) {

                // Child wants a specific size... let him have it

                resultSize = childDimension;

                resultMode = MeasureSpec.EXACTLY;

            } else if (childDimension == LayoutParams.MATCH_PARENT) {

                // Child wants to be our size... find out how big it should

                // be

                resultSize = 0;

                resultMode = MeasureSpec.UNSPECIFIED;

            } else if (childDimension == LayoutParams.WRAP_CONTENT) {

                // Child wants to determine its own size.... find out how

                // big it should be

                resultSize = 0;

                resultMode = MeasureSpec.UNSPECIFIED;

            }

            break;

        }

        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

    }

這個函式先拿到FrameLayout的measure spec,然後再將measurespec的value減去padding儲存到size中,其即為child view的desire size

這個函式其實就處理兩種情況

1)  如果FrameLayout的measure spec為EXACTLY,接著判斷child view的width/height值;如果值大於0,說明其是一個確定的值,直接將其值儲存到resultSize並且將resultMode儲存為EXACTLY就可以了;如果值為MATCH_PARENT或者WRAP_CONTENT,則將result設定為size然後對應的值儲存到resultMode就好了

2)  如果FrameLayout的measure spec為AT_MOST,對於child view的width/height值大於0或者WRAP_CONTENT,處理跟上面都是一樣的;唯一不同的是,當值為MATCH_PARENT時,由於FrameLayout這個parent view在目前measure width/height都未確定,作為child view,你這裡MATCH_PARENT就沒任何意義,所以這裡會把result mode改成AT_MOST

函式中呼叫child.measure執行child view的measure操作,也就是本節介紹的內容,如此反覆,直到所有child view都measure完畢為止

相關推薦

AndroidActivity 視窗顯示流程介紹

7 Activity 視窗顯示流程介紹 Activity 視窗顯示,其實就是Décor View繪製並顯示的過程,但是在繪製之前,Décor View需要先做下面兩件事情: 1)  確定Décor View的大小 2)  對Décor View進行佈局操作,也就是確定Déc

AndroidActivity 視窗顯示流程介紹

7.3 Activity Décorview佈局(layout) Measure確定Décor View以及child views的大小,layout則是確定child view在其parent view中的顯示區域,只有layout結束,view的left,right,t

AndroidActivity Decor View建立流程介紹

6 Activity DecorView建立流程介紹 上頭已經完整的介紹了Activity的啟動流程,Activity是如何繫結Window,Window的décor view是如何通過ViewRootImpl與WMS建立關聯的,也就是說,整個框架已經有了,唯一缺的就是Ac

AndroidActivity觸控事件傳輸機制介紹

8 Activity觸控事件傳輸機制介紹 當我們觸控式螢幕幕的時候,程式會收到對應的觸控事件,這個事件是在app端去讀取的嗎?肯定不是,如果app能讀取,那會亂套的,所以app不會有這個許可權,系統按鍵的讀取以及分發都是通過WindowManagerService來完成

AndroidApp視窗檢視管理

5 App視窗檢視管理 WindowManagerGlobal負責管理App所有要新增到WMS的視窗,介面即為上頭的addView 首先,對於App本地視窗來說,其最核心的資料無非就兩個,一個是Window Parameters,另一個就是視窗的DécorView,一個負

Android使用Font Awesome顯示小圖示

Android中傳統的顯示圖示的方式 在平常的開發中,如果我們需要在介面上顯示某個小圖示,比如搜尋按鈕,返回按鈕,這時我們需要美工給我們切對應的png圖片,並放進對應的drawable資料夾中,這樣隨著圖示的越來越多,APK體積也會越來越大。 什麼是Fo

Android OTA升級原理和流程分析

這篇及以後的篇幅將通過分析update.zip包在具體Android系統升級的過程,來理解Android系統中Recovery模式服務的工作原理。我們先從update.zip包的製作開始,然後是Android系統的啟動模式分析,Recovery工作原理,如何從

Atlassian產品搭建的敏捷管理體系

Dream big, work smart, deliver fast 使用Atlassian的產品已經有三年多,但是大部分主要以JIRA和Confluence為主,今年年初加入一創業團隊負責技術團隊的搭建,從零開始通過部署Atlassian產品、制

Android用surface直接顯示yuv資料

轉自:http://blog.csdn.net/tung214/article/details/36887041 研究了一段時間Android的surface系統,一直執著地認為所有在surface或者螢幕上顯示的畫面,必須要轉換成RGB才能顯示,yuv資料也要通過顏色

詳解androidAnimation監聽方法AnimationListener

先寫一個類繼承AnimationListener,看看具體方法: 具體方法大家也已經從圖片中也有些瞭解了,那接下來就看看實戰中,又怎麼使用呢: 1. 先看看佈局檔案和效果圖:     

C++效能系列靜態程式碼檢查工具介紹

FxCop Integrator允許將獨立的FxCop(1.36或10.0)和Code Metrics PowerTool 10.0整合到VS2010中。 最新版本(2.0.0 RTW)包含以下新功能: 支援使用程式碼度量PowerTool的計算程式碼

Android AdapterView及子類的介紹

AdapterView是一組重要的元件,AdapterView本身是一個抽象基類,它派生的子類在用法上十分相似,只是顯示介面有一定的區別,因此把它們歸為一類,針對它們的共性集中講解,並突出介紹它們的區別。 AdapterView具有如下特徵: (一)AdapterView繼

SpringMVC架構與工作流程

MVC模式是在Java的Web應用開發中非常常用的模式。MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將

AndroidSurfaceFlinger相關介紹

3.3 Surface Java層相關封裝 主要介紹三個類,對應如下: Java C++ SurfaceSession.java SurfaceComposeClient 對應JNI檔案為: android_view_surfacesession.cpp

Android 端天氣預報APP的實現天氣顯示介面上下滑動

最近參加了一個小比賽,選了天氣預報APP這個題目,最初選擇它,是想練練網路資料獲取和資料解析方面的知識,後來發現倒是學到了不少介面的東西。下面我來一步步講解一下我是怎麼完成的吧~ 首先,天氣預報最直觀的部分應該就是天氣顯示介面了,這裡,我想做成可以有上下滑動的

Android開發實現滑動RecyclerView,浮動按鈕的顯示和隱藏

本篇部落格,主要講解了滑動RecyclerView實現FloatingActionButton的顯示和隱藏的動畫。 -------------------------------分割線----------------------------- 效果圖展示: -------

HTTP中Get與Post的區別

     Http定義了與伺服器互動的不同方法,最基本的方法有4種,分別是GET,POST,PUT,DELETE。URL全稱是資源描述符,我們可以這樣認為:一個URL地址,它用於描述一個網路上的資源,而HTTP中的GET,POST,PUT,DELETE就對應著對這個

Android9.0 Activity啟動流程分析

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹   本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的   在分析Activity啟動的原始碼之前先介紹一下Act

HBase原始碼分析HRegion上compact流程分析

  2016年03月03日 21:38:04 辰辰爸的技術部落格 閱讀數:2767 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lipeng_bigdata/article/details/50791205

linux學習筆記流程控制if分支語句

流程控制 if語句 單分支if語句 程式 fi``` ```if [ 判斷 ] then 程式 fi``` 例項程式碼指令碼: #!/bin/bash