美文网首页
FrameWork学习之二-Android UI绘制流程详解

FrameWork学习之二-Android UI绘制流程详解

作者: 夏木友人 | 来源:发表于2022-04-27 14:17 被阅读0次

    一、 从ActivityThread到View绘制流程图,如下

    UI的具体绘制流程.jpg

    二、 Activity oncreate setContent加载xml布局过程

    1.Activity.class --> setContentView
    2.PhoneWindow.class --> setContentView -->installDecor() 476行

    1. PhoneWindow.class -->generateDecor 2338行
      4.PhoneWindow.class -->generateLayout 2359行
      5.PhoneWindow.class -->mDecor.onResourcesLoaded 2630行
      最外层DecorView,然后添加根布局screen_simple.xml, 返回@android:id/content 根布局
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
        <ViewStub android:id="@+id/action_mode_bar_stub"
                  android:inflatedId="@+id/action_mode_bar"
                  android:layout="@layout/action_mode_bar"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:theme="?attr/actionBarTheme" />
        <FrameLayout
             android:id="@android:id/content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:foregroundInsidePadding="false"
             android:foregroundGravity="fill_horizontal|top"
             android:foreground="?android:attr/windowContentOverlay" />
    </LinearLayout>
    

    6.初始化完父容器mContentParent,在PhoneWindow setContent()里mLayoutInflater.inflate(layoutResID, mContentParent);
    加载解析xml布局,然后添加到mContentParent里。
    7.LayoutInflater.class -->inflate

    三、 Activity UI开始绘制是在onResume,并不是onCreate时

    1. ActivityTread --> handleResumeActivity 4468行
    2. handleResumeActivity --> wm.addView(decor, l); 4535行
      3.WindowManagerImpl --> addView 107行
      4.WindowManagerGlobal --> addView 331行
    3. addView --> root.setView(view, wparams, panelParentView, userId); 409行
      6.ViewRootImpl --> setView 919行
    4. ViewRootImpl --> requestLayout 1604行
    5. ViewRootImpl --> scheduleTraversals 1923行
    6. ViewRootImpl --> doTraversal 1943行
    7. ViewRootImpl --> performTraversals 2332行
    8. performTraversals --> performMeasure 2906行 --> mView.measure(childWidthMeasureSpec, childHeightMeasureSpec) --> onMeasure();
    9. performTraversals --> performLayout 2938行 --> host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()) --> onLayout();
    10. performTraversals --> performDraw 3099行 --> onDraw();

    四、 View测量measure

    1.在学习view测量之前,我们先了解下MeasureSpec对象, 通过MeasureSpec.getMode()可获取测量模式, MeasureSpec.getSize()可获取测量大小

    2.MeasureSpec的三种测量模式


    image.png

    3.为了深入了解,接下来我们讲解下MeasureSpec是 如何运算
    Java中定义MeasureSpec 是一个int类型,4个字节 4x8 =32位,前两位为MeasureSpec的Mode类型,后面30放置MeasureSpec的size大小

    我们以MeasureSpec Mode = AT_MOST, MeasureSpec size = 1080为例

    public static final int AT_MOST     = 2 << MODE_SHIFT
    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT
    

    AT_MOST值为2,十进制2转成二进制 为10, 然后左移MODE_SHIFT 30位
    结果为 10 0000000000 0000000000 0000000000
    MeasureSpec size 是1080,转成二进制10000111000,补全32位得到结果
    00 0000000000 0000000001 0000111000

    int MeasureSpec = makeMeasureSpec(size, mode);

            public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                              @MeasureSpecMode int mode) {
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                    return (size & ~MODE_MASK) | (mode & MODE_MASK);
                }
            }
    

    MODE_MASK = 11 0000000000 0000000000 0000000000
    ~MODE_MASK =00 1111111111 1111111111 1111111111

    size & ~MODE_MASK
    00 0000000000 0000000001 0000111000 &
    00 1111111111 1111111111 1111111111
    =
    00 0000000000 0000000001 0000111000

    mode & MODE_MASK
    10 0000000000 0000000000 0000000000 &
    11 0000000000 0000000000 0000000000
    =
    10 0000000000 0000000000 0000000000

    (size & ~MODE_MASK) | (mode & MODE_MASK)
    00 0000000000 0000000001 0000111000 |
    10 0000000000 0000000000 0000000000
    =
    10 0000000000 0000000001 0000111000
    int MeasureSpec = makeMeasureSpec(1080, MeasureSpec.AT_MOST) =
    10 0000000000 0000000001 0000111000

    java二进制运算符


    image.png

    4.View测量onMeasure,不同view测量规则不一样,我们以FrameLayout为例
    FrameLayout是帧布局,他的子view是一个个重叠放置,所以它的大小取解于最大子View的大小maxHeight,maxWidth

    
            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);
                        }
                    }
                }
            }
    

    measureChildWithMargins测量出子view大小-->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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = MeasureSpec.UNSPECIFIED;
                }
                break;
            }
            //noinspection ResourceType
            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
        }
    

    如果父类mode是EXACTLY精确
    1.如果子类childDimension>0,则子类大小resultSize = childDimension,并且类型是精确
    2.如果子类childDimension为LayoutParams.MATCH_PARENT,则子类大小resultSize=父类大小size,并且类型是精确
    3.如果子类childDimension为LayoutParams.WRAP_CONTENT,则子类大小resultSize最大为父类大小size,类型是At_MOST

    后面几种类型依此类推,不再详解

    五、 View测量onLayout

    我们还是以简单的FrameLayout为例

    1.布局子view

    image.png

    childView左起点childLeft = parentLeft + lp.leftMargin childRight=childLeft+width
    childView顶部起点childTop = parentTop + lp.topMargin childBottom= childTop +height


    image.png

    六、 View测量onDraw

        public void draw(Canvas canvas) {
            final int privateFlags = mPrivateFlags;
            mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
    
            /*
             * Draw traversal performs several drawing steps which must be executed
             * in the appropriate order:
             *
             *      1. Draw the background
             *      2. If necessary, save the canvas' layers to prepare for fading
             *      3. Draw view's content
             *      4. Draw children
             *      5. If necessary, draw the fading edges and restore layers
             *      6. Draw decorations (scrollbars for instance)
             *      7. If necessary, draw the default focus highlight
             */
    
            // Step 1, draw the background, if needed
            int saveCount;
    
            drawBackground(canvas);
    
            // skip step 2 & 5 if possible (common case)
            final int viewFlags = mViewFlags;
            boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
            boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
            if (!verticalEdges && !horizontalEdges) {
                // Step 3, draw the content
                onDraw(canvas);
    
                // Step 4, draw the children
                dispatchDraw(canvas);
    
                drawAutofilledHighlight(canvas);
    
                // Overlay is part of the content and draws beneath Foreground
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.getOverlayView().dispatchDraw(canvas);
                }
    
                // Step 6, draw decorations (foreground, scrollbars)
                onDrawForeground(canvas);
    
                // Step 7, draw the default focus highlight
                drawDefaultFocusHighlight(canvas);
    
                if (isShowingLayoutBounds()) {
                    debugDrawFocus(canvas);
                }
    
                // we're done...
                return;
            }
    

    ① 绘制 View 的背景
    ② 绘制 View 的内容
    ③ 绘制子 View
    ④ 绘制装饰(渐变框、滑动条、前景等)

    相关文章

      网友评论

          本文标题:FrameWork学习之二-Android UI绘制流程详解

          本文链接:https://www.haomeiwen.com/subject/vziryrtx.html