ViewRootImpl简介(二)

2020-04-30  本文已影响0人  remax1

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完成。

上一篇下一篇

猜你喜欢

热点阅读