这几天在闲暇之余,继续研究了性能优化很重要的一个组成部分,UI绘制流程和UI性能优化,这一块还是蛮深的,我简单的跟着源码走了一遍,在此简单记录,以供以后继续深入研究。
看绘制流程就用Actiivty的setContentView(R.layout.activity_main);入手:
- 点进Activity.Java类
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID);// **①** initWindowDecorActionBar(); }
- 这里的getWindow()拿到的是Window的实现类PhoneWindow。找到PhoneWindow.Java,在 com.android.internal.policy包下。
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor();//**②加载DecorView** } …… mLayoutInflater.inflate(layoutResID, mContentParent);//**⑥最后把我们自己的view放进DecorView中** } private void installDecor() { if (mDecor == null) { mDecor = generateDecor();//**③生成一个DecorView(继承的FrameLayout)** .............. if (mContentParent == null) { mContentParent = generateLayout(mDecor);//**④根据主题使用不同的布局承载** } protected ViewGroup generateLayout(DecorView decor) { //**⑤省略一堆判断。** View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; }
看下图或许会更清楚。
类关系图
根据主题使用不同的布局
2.继续来看下measure、layout、draw的三个执行流程。这一块是重点也是难点。
- View.java类
- measure:测量,测量自己有多大,如果是ViewGroup的话会同时测量里面的子控件的大小
- layout:摆放里面的子控件bounds(left,top,right,bottom)
- draw:绘制 (如果直接继承了view一般都会重写onDraw)
- 这里主要关注一下View.Java源码
- view的requestLayout()方法开始,递归地不断往上找父容器,最终找到DecorView。ps:DecorView是PhoneWindow的内部类。
- 执行了DecorView的ViewRootImp类的performTranversal()方法 (ViewRootImp类:是PhoneWindow和DecorView的桥梁)
- performTranversal()方法里面分别调用:
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performLayout(lp, desiredWindowWidth, desiredWindowHeight); performDraw();
在这里分别调用view的measure,layout,draw方法。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
3.. measure 测试是一个复杂的过程
- 如何合同的去测量一棵View树 ?如果ViewGroup和View都是直接指定的宽高,那么就不会走测量,测量是因为谷歌设计的自适应尺寸机制(比如Match_parent,Wrap_content) ,造成了宽高不确定,所以就需要进行测量measure过程。measure过程会遍历整颗View树,然后依次测量每一个View的真实的尺寸。(树的遍历–先序遍历)。
这里说一下MeasureSpec,测量规格,int类型32位。然后拿前面两位当做mode,后面30位当做值。
- mode三种类型:
- EXACTLY: 精确的。比如给了一个确定的值 100dp
- AT_MOST: 根据父容器当前的大小,结合你指定的尺寸参考值来考虑你应该是多大尺寸,需要计算(Match_parent,wrap_content就是属于这种)
- UNSPECIFIED: 最多的意思。根据当前的情况,结合你制定的尺寸参考值来考虑,在不超过父容器给你限定的只存的前提下,来测量你的一个恰好的内容尺寸。
用的比较少,一般见于ScrollView,ListView(大小不确定,同时大小还是变的。会通过多次测量才能真正决定好宽高。)
- value:就是宽高的值。
- 从规格当中获取mode和value:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- 反过来将mode和value合成一个规格呢:
MeasureSpec.makeMeasureSpec(resultSize, resultMode);
- mode三种类型:
经过大量测量以后,最终确定了自己的宽高,需要调用:
setMeasuredDimension(w,h)
4.. 这几天一直在研究这一块的东西,时间精力有限,零零散散整理,稍微有些混乱,等有时间再系统的整理一下。这篇文章主要是对于UI绘制流程的理解,关于UI的优化,下次继续整理。
由于技术和文笔有限,很多东西没有说的很透彻,反正你也不能来打我~~
说明一下,文章中的图片是借用的,如果涉嫌侵权,请联系我删除~~~