戏说Android view 工作流程《下》

本文详细介绍了Android View的工作流程,包括测量(measure)、布局(layout)和绘制(draw)三个关键步骤。通过对ViewRoot的performTraversals()函数的分析,揭示了视图大小计算、位置安排以及屏幕绘制的详细过程。文中还特别提到了ViewGroup的测量和布局策略,如FrameLayout和LinearLayout,并建议读者研究这两个布局的onMeasure方法以加深理解。
摘要由CSDN通过智能技术生成

遍历View树performTraversals()执行过程

view树遍历概述

还是回到ViewRoot.java,我们直接看performTraversals(),该函数就是android系统View树遍历工作的核心。一眼看去,发现这个函数挺长的,但是逻辑是非常清晰的,其执行过程可简单概括为根据之前所有设置好的状态,判断是否需要计算视图大小(measure)、是否需要重新安置视图的位置(layout),以及是否需要重绘(draw)视图,可以用以下图来表示该流程。

  

  private void performTraversals() {
        // cache mView since it is used so much below...
//1 处理mAttachInfo的初始化,并根据resize、visibility改变的情况,给相应的变量赋值。
        final View host = mView;
        final View.AttachInfo attachInfo = mAttachInfo;
        final int viewVisibility = getHostVisibility();
        boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                || mNewSurfaceNeeded;
        float appScale = mAttachInfo.mApplicationScale;
        WindowManager.LayoutParams params = null;
        if (mWindowAttributesChanged) {
            mWindowAttributesChanged = false;
            surfaceChanged = true;
            params = lp;
        }
        Rect frame = mWinFrame;
        if (mFirst) {

            // For the very first time, tell the view hierarchy that it
            // is attached to the window.  Note that at this point the surface
            // object is not initialized to its backing store, but soon it
            // will be (assuming the window is visible).
            attachInfo.mSurface = mSurface;
            attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
                    lp.format == PixelFormat.RGBX_8888;
            attachInfo.mHasWindowFocus = false;
            attachInfo.mWindowVisibility = viewVisibility;
            ......
        } 
//2 如果mLayoutRequested判断为true,那么说明需要重新layout,不过在此之前那必须重新measure。
        if (mLayoutRequested) {
            // Execute enqueued actions on every layout in case a view that was detached
            // enqueued an action after being detached
            getRunQueue().executeActions(attachInfo.mHandler);

            if (mFirst) {
                ......
            } 
        }
//3 判断是否有子视图的属性发生变化,ViewRoot需要获取这些变化。
        if (attachInfo.mRecomputeGlobalAttributes) {
            ......
        }

        if (mFirst || attachInfo.mViewVisibilityChanged) {
            ......
        }

    
//4 根据上面得到的变量数值,确定我们的view需要多大尺寸才能装下。于是就得measure了,有viewgroup的weight属性还得再做些处理
                 // Ask host how big it wants to be
                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                mLayoutRequested = true;
            }
        }
//5 measure完毕,接下来可以layout了。
        final boolean didLayout = mLayoutRequested;
        boolean triggerGlobalLayoutListener = didLayout
                || attachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);

        }

//6 如果mFirst为true,那么会进行view获取焦点的动作。
        if (mFirst) {
            mRealFocusedView = mView.findFocus();
        }

        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
//7 终于,来到最后一步,前面的工作可以说都是铺垫,都是为了draw而准备的。
        if (!cancelDraw && !newSurface) {
            mFullRedrawNeeded = false;
            draw(fullRedrawNeeded);
}

下面我们就来会会view那三部曲吧。

计算视图大小(measure)的过程

整个view视图的Measure过程就是一个量体裁衣,按需分配的过程。看一下以下的递归过程:


从上图可以看出,measure过程始于ViewRoot的host.measure(),调的就是view类的measure()函数,该函数然后回调onMeasure。如果host对象是一个ViewGroup实例,一般会重载onMeasure,如果没有的话,则会执行view类中默认的onMeasure。合理的情况是编程人员重载onMeasure并逐一对里面的子view进行measure。我们可以看一下view的measure方法:

   

  /**
     * 该方法在需要确定view所需尺寸大小时调用,父视图会提供宽和高的属性约束。
     * 具体视图完全可以在onMeasure中改变这些。
     * @see #onMeasure(int, int)
     */
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        if ((mPrivateFlags
  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值