重温Android开发艺术探索之四 View工做原理

View 的工做原理

ViewRoot 和DecorView

ViewRoot对应ViewRootImpl,是链接WindowManager 和DecorView的纽带。View的三大流程都是经过ViewRoot完成的,如图所示, ![ViewRootImpl 的代码结构图] java

而且当系统一些常量发生改变时也是有它监听到了处理的 在ActivityThread的attach方法中有添加这个监听,源码以下

ViewRootImpl.addConfigurationChange(Configuration newConfig),经过mH 发送消息 what = 118 的值到H 中处理 调用了 ActivityThread 的 handleConfigurationChanged
复制代码

能够说ViewRootImpl控制这整个View的绘制流程,是很是重要的,当Activity被建立时,会将DecorView添加到Window中,同时会建立ViewRootImpl对象.api

ActivityThread#handleResumeActivity wm.addView(decor,l),经过WindowManager 将decor添加到window中,l是LayoutParams,核心代码以下:api25的源码ide

WindowManager wm;
                if (r.window == null && !a.mFinished && willBeVisible) {
                    r.window = r.activity.getWindow();
                    View decor = r.window.getDecorView();
                    decor.setVisibility(4);
                    wm = a.getWindowManager();
                    LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor;
                    l.type = 1;
                    l.softInputMode |= forwardBit;
                    if (r.mPreserveWindow) {
                        a.mWindowAdded = true;
                        r.mPreserveWindow = false;
                        ViewRootImpl impl = decor.getViewRootImpl();
                        if (impl != null) {
                            impl.notifyChildRebuilt();
                        }
                    }

                    if (a.mVisibleFromClient && !a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    }
                } else if (!willBeVisible) {
                    r.hideForNow = true;
                }
复制代码

wm.addView(decor, l)通过 WindowManagerImpl再到WindowManagerGlobal 中的 addView方法,在此方法里面对ViewRootImpl 进行了初始化。post

ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
 root.setView(view, wparams, panelParentView);
复制代码

View的绘制流程是从performTraversals执行遍历开始的,通过measure,layout,draw,三个过程才能最终将一个View绘制出来。测试

MeasureSpec 测量规格

MeasureSpec

一个32为的int值,高2位表明SpecMode 低30位表明SpecSize。ui

SpecMode

  • UNSPECIFIED 父容器不对View作任何限制,要多大给多大。
  • EXACTLY 父容器已经检测出了View所须要的精确大小,这时候View的最终大小就是SpecSize所指定的值,对应与LayoutParams中的match_parent和具体数值两种模式。
  • AT_MOST 父容器指定了一个最大值,View不能超过这个最大值,对应wrap_content。

MeasureSpec 和 layoutParams 的对应关系

  • DecorView 其MeasureSpec是由自身的LayoutParams和窗口的尺寸来共同决定spa

  • 普通的View 其MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams来共同决定code

  • 简单来讲自定义Veiw到measure这一步的时候,须要测试自身的宽高,须要widthMeasureSpec, heightMeasureSpec,宽的测量规格和高的测量规格,二者的获取方式都是同样,须要根据父容器的MeasureSpec 和自身的 宽高来获得。orm

  • View的measure过程,到这一步确定是通过了父控件的measure过程,获得了父控件传下来的measureSpec,而后调用onMeasure,经过setMeasuredDimension 来肯定自身的宽高。固然中间须要通过一些计算获得widthMeasureSpec, heightMeasureSpec。cdn

Activity启动时去获取某一View的宽高

  • Activity/View#onWindowFocusChanged。

  • ViewTreeObserver.addOnGlobalLayoutListener()。

总结

从ViewRootImpl的构造方法中调用 loadSystemProperties()

->profileRendering()

->scheduleTraversals()

-> mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

->doTraversal();

->performTraversals();

->performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

-> performLayout(lp, mWidth, mHeight);

-> performDraw();

完成View的三大流程。

相关文章
相关标签/搜索