美文网首页
ViewRootImpl简介(二)

ViewRootImpl简介(二)

作者: remax1 | 来源:发表于2020-04-30 16:09 被阅读0次

    performTraversals()

    上文提过,在setView时会调用requestLayout()触发遍历。遍历操作指的就是performTraversals()方法。ViewRootImpl中接受到的各种变化如重绘等请求都引发performTraversals()方法调用,并处理。View类及子类中的onMeasure(),onLayout(),onDraw()方法将会在performTraversals()中被回调。
    话不多说,上代码

    private void performTraversals() {
        ``````
        //主角,下面都是为这两个变量赋值
        intdesiredWindowWidth;
        intdesiredWindowHeight;
        ``````
     
        if(mFirst) {
            /*第一次遍历,此时窗口刚刚被添加到WMS */
            if(lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
                    Point size = new Point();
                    mDisplay.getRealSize(size);
                    desiredWindowWidth = size.x;
                    desiredWindowHeight = size.y;
               // desiredWindowWidth/Height,其取值是屏幕尺寸
            }else {
                //应用可以使用的最大尺寸作为SPEC_SIZE的候选
               DisplayMetrics packageMetrics =
                   mView.getContext().getResources().getDisplayMetrics();
               desiredWindowWidth = packageMetrics.widthPixels;
               desiredWindowHeight = packageMetrics.heightPixels;
            }
            /* 控件树即将第一次被显示在窗口上,
            mAttachInfo一些字段被赋值
             然后通过mView发起了dispatchAttachedToWindow()的调用
              之后每一个位于控件树中的控件都会回调onAttachedToWindow() */
            ......
        } else {
            //  在非第一次遍历的情况下,会采用窗口的最新尺寸作为SPEC_SIZE的候选
           desiredWindowWidth = frame.width();
           desiredWindowHeight = frame.height();
            // 如果窗口的最新尺寸与ViewRootImpl中的现有尺寸不同
            if(desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                // 需要进行完整的重绘以适应新的窗口尺寸
               mFullRedrawNeeded = true;
               // 需要对控件树进行重新布局
               mLayoutRequested = true;
               windowSizeMayChange = true;
            }
        }
        ···
        /* 执行位于RunQueue中的回调。RunQueue是ViewRootImpl的一个静态成员,进程唯一,调用view.post(Runable) 时,也将交由attachInfo的handler处理。
       getRunQueue().executeActions(attachInfo.mHandler);
        booleanlayoutRequested = mLayoutRequested && !mStopped;
        /* 仅当layoutRequested为true时才进行预测量。
         layoutRequested为true表示在进行“遍历”之前requestLayout()方法被调用过。
         requestLayout()方法用于要求ViewRootImpl进行一次“遍历”并对控件树重新进行测量与布局 */
        if(layoutRequested) {
           final Resources res = mView.getContext().getResources();
            if(mFirst) {
             mAttachInfo.mInTouchMode = !mAddedTouchMode;
              ensureTouchModeLocally(mAddedTouchMode);// 确定控件树是否需要进入TouchMode,下一章节将会详细说。
            }else {
    ···
        }
        // 其他阶段的处理,对主角进行测量
       // Ask host how big it wants to be
                windowSizeMayChange |= measureHierarchy(host, lp, res,
                        desiredWindowWidth, desiredWindowHeight);
        ......
        //对主角进行布局
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
         ·······
        //最后调用
        performDraw();
    
    }
    

    其中,measureHierarchy()中主要调用了

      childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    

    完成测量过程.

    小结

    ·performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的,另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。
    ·RunQueue是ViewRootImpl的一个静态成员,当我们在子线程更新ui时会调用view.post()或者view.postDelay()都会将runable交由attachInfo中的handler来处理,从而触发performTraversals()完成ui更新。
    ·事件分发也是由ViewRootImpl完成。

    相关文章

      网友评论

          本文标题:ViewRootImpl简介(二)

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