赞
踩
转载请标明出处:【顾林海的博客】
个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
##前言
自定义View在Android中占据着非常重要的地位,因此了解View的绘制流程对自定义View来说尤其重要,View的绘制流程总的来说包含测量、布局和绘制三个流程,本篇会对这三个流程进行详细的讲解,力求对View的绘制流程有清晰的认识。
##视图绘制
Activity代表着一个用于与用户进行交互的窗口,通常启动一个Activity时,会通过setContentView方法来加载需要显示的布局文件,这个布局会被添加到内容视图中,就像下图一样:
布局被添加到ContentView中,ContentView是一个FrameLayout,图中PhoneWindow是Activity与View进行交互的接口,创建一个Activity时都会创建一个PhoneWindow,PhoneWindow会创建一个DecorView,这个DecorView就是根视图,在DecorView中通常会包含一个TitleView用于显示ActionBar,以及ContentView,ContentView就是内容视图,平时创建的Layout会被添加到这个ContentView。
启动Activity时会对布局进行绘制,绘制从ViewRoot(或是ViewRootImpl)的performTraversals方法开始,代码简化如下:
private void performTraversals(){
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout();
...
performDraw();
...
}
performTraversals方法中performMeasure方法执行的是测量流程,performLayout方法执行的是布局流程,performDraw方法执行的是绘制流程。
##MeasureSpec
MeasureSpec是View内部的一个静态内部类,表示的是一个32位的整型值,高2位表示的是测量模式SpecMode,低30位表示的是测量大小SpecSize,可以通过MeasureSpec获取View的测量模式和测量大小,从而更合理的设置View的大小。
public static class MeasureSpec {
...
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
...
}
MeasureSpec提供了三种测量模式,分别是UNSPECIFIED、EXACTLY和AT_MOST,三种测量模式的含义如下:
##绘制流程
在上面ViewRoot(或是ViewRootImpl)的performTraversals方法中了解到了在根视图中绘制分为三个步骤,分别是测量、布局和绘制,在具体的测量阶段会将测量操作分发给View来执行,如下代码
//ViewRootImpl.java
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);
}
}
在ViewGroup中会将测量操作交由measureChildren,如下图:
在measureChildren方法中会通过遍历调用子View的measure方法进行测量,View的measure方法如下:
在measure方法中测量操作最终通过回调onMeasure方法实现的,由于measure方法是final类型的所以子类就不能重写这个方法,那如何在自定义View时重新测量大小呢?这时可以重写onMeasure方法,最终通过调用View的setMeasureDimension方法设置View的尺寸。如果自定义View时没有重写onMeasure方法,这时View的大小默认会通过getDefaultSize方法来获取。
其中getSuggestedMinimumWidth和getSuggestedMinimumHeight这两个方法分别获取的是当前View的最小宽高。
View的测量完毕后的下一个流程是布局,在ViewRootImpl中将布局操作交由View来实现,如下图(ViewRootImpl):
如果是ViewGroup,会重写View的layout方法来实现所有View控件的布局流程:
ViewGroup中的layout方法会调用它的基类也就是View的layout进行View的布局。由于layout方法使用final修饰,因此自定义View时可以通过重写onLayout方法来实现View的布局,默认在ViewGroup和View中的onLayout方法是空实现的,并且在ViewGroup中onLayout方法是一个抽象方法,子类继承ViewGroup时,必须实现这个方法。
这这里举个LinearLayout的布局的例子,LinearLayout大家肯定熟悉,它是一个线性布局,内部元素按水平或垂直排列,并且LinearLayout是继承自ViewGroup,因此LinearLayout的布局肯定是实现onLayout方法来实现的:
以上就是View绘制流程中的布局流程,最后来分析绘制流程,查看ViewRootImpl中的绘制操作:
最终调用到每个View的draw方法:
其中onDraw方法是用于绘制内容,在View中onDraw方法是空实现的:
protected void onDraw(Canvas canvas) {
}
需要子类实现,并通过Canvas进行绘制。
View的绘制流程已经讲解完毕,希望对大家有所帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。