赞
踩
由于学习的课程api 不一致 导致源码有些关键方法无法进入仔细阅读 采用截图的方式理解思路
进入到源码中可以发现 ,每个activity 默认生成的代码中都会有一个setContentView方法,这个方法用于加载输入路径当前的布局以及后续的操作
底层本质还是调用一个委托去设置资源
@Override
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
getDelegate().setContentView(layoutResID);
}
在继续走进这个委托类的接口实现方法
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
ensureSubDecor()
进入这个decor 方法 下图为部分代码
if (!mSubDecorInstalled)
mSubDecor = createSubDecor();
createSubDecor()
根据实装值 来判断是否创建对应的decor
这个方法中有大量的系统资源整合 ,都是一些系统主题的装饰器放入到对应需要创建的window中,比如
再继续往下看 会看到一个赋值操作 将 mWindow.setContentView(subDecor);
赋值,
PS: WINDOW 对象的主要实现都在phone window 中 所以我们去phone window 寻找这个主要的方法
这个方法有两个主要实现 这里我们主要看 layouResId为参数的实现内容
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. // 先判断上下文父对象是否存在 是否创建内容装饰器(用于拿到需要的基础主题资源) if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
这里我们点进 generateLayout
方法,见名知意 这个方法主要是组装布局需要的主题属性和样式属性 这里看到
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
加载资源的方法 根据不同的 feature 给layout赋不同的值,之后将布局信息添加到 mdecor
之后再 generateLayout() 返回一个 contentParent 也就是一个viewGroup
流程图
根据上图 我们可以先去查看ActivityThread
找到 H这个类 他继承自 handle 内部实现了一个handleMessage
方法 内部会有一个调用链路
需要注意的是在这个调用链路中 viewRootImpl performTraversal()
这个方法 ,绘制view的三个步骤就是在这个线程中完成的
从最底层源码的测量可以看出 执行测量其实就是不断调用到最低层的 setMeasureDimensionRow 中对两个成员变量标记赋值,保存测量控件的宽高。
在view 绘制之前 安卓底层会先去利用 measure
测量对应view 的模式和尺寸,安卓给我们提供了一个叫做 measurespec类 是一个三十二位int的存储值,前两位代表模式,后三十位代表尺寸
用于取值 mode 只需要取前两位 size 只需要取后三十位 组合成一个 MeasureSpec
同样 MeasureSpec 也有对应的getsize
和getmode
获取对应的值
参数
根据设置的布局属性 来设置对应的mode
帮助 performMeasure 是拿到需要的 measurespec
decor view 继承自 framelayout 查看他的 onMeasure 方法
通过递归的方式 判断并且设置不同规则父容器和子控件的测量方式
ps: 在自定义view 的时候 一定要重写 onmeasureDiemsion方法
在 view 设置测量自己之前会调用getdefaultsize 根据不同的模式获取到size 有趣的是 at 和 exactly 赋值都是父容器剩余的大小,所以在自定义view 需要重写一系列方法不然就可能出现问题
自定义容器和viwe 区别 :
view.setFrame()
对控件的上下左右赋值 设置完成 控件需要摆放的位置就确定了
view.onlayout()
如果此时是容器 则需要调用此方法 确定子控件的位置
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
* 7. If necessary, draw the default focus highlight
*/
绘制过程
ViewGroup
View
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。