View 的工作原理
ViewRoot 和DecorView
ViewRoot对应ViewRootImpl,是连接WindowManager 和DecorView的纽带。View的三大流程都是通过ViewRoot完成的,如图所示,
![ViewRootImpl 的代码结构图]
并且当系统一些常量发生改变时也是有它监听到了处理的
在ActivityThread的attach方法中有添加这个监听,源码如下
ViewRootImpl.addConfigurationChange(Configuration newConfig),通过mH 发送消息 what = 118 的值到H 中处理 调用了 ActivityThread 的 handleConfigurationChanged
复制代码
可以说ViewRootImpl控制这整个View的绘制流程,是非常重要的,当Activity被创建时,会将DecorView添加到Window中,同时会创建ViewRootImpl对象.
ActivityThread#handleResumeActivity wm.addView(decor,l),通过WindowManager 将decor添加到window中,l是LayoutParams,核心代码如下:api25的源码
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 进行了初始化。
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。
SpecMode
UNSPECIFIED 父容器不对View做任何限制,要多大给多大。
EXACTLY 父容器已经检测出了View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,对应与LayoutParams中的match_parent和具体数值两种模式。
AT_MOST 父容器指定了一个最大值,View不能超过这个最大值,对应wrap_content。
MeasureSpec 和 layoutParams 的对应关系
DecorView 其MeasureSpec是由自身的LayoutParams和窗口的尺寸来共同决定
普通的View 其MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams来共同决定
简单来说自定义Veiw到measure这一步的时候,需要测试自身的宽高,需要widthMeasureSpec, heightMeasureSpec,宽的测量规格和高的测量规格,两者的获取方式都是一样,需要根据父容器的MeasureSpec 和自身的 宽高来得到。
View的measure过程,到这一步肯定是经过了父控件的measure过程,得到了父控件传下来的measureSpec,然后调用onMeasure,通过setMeasuredDimension 来确定自身的宽高。当然中间需要经过一些计算得到widthMeasureSpec, heightMeasureSpec。
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的三大流程。
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[重温Android开发艺术探索之四 View工作原理]http://www.zyiz.net/tech/detail-140373.html