源码基于8.0
oncreate()获取view的宽高为0
activity启动流程(https://www.jianshu.com/p/4a20d9d68482
),最后一步,ams通过binder机制向app进程发送attachapplication请求,然后app收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY,然后执行handleLaunchActivity()。
1.performLaunchActivity(调用oncreate onstart)
2.handleResumeActivity(调用onresume-->window.addview())
经过一系列调用,最后走到ViewRootImpl的performTraversals()方法(之后流程见图view的绘制流程.png),开始view的measure,layout,draw流程。(来到这顺便也可以通过源码发现requestLayout,invalidate的区别)所以,在oncreate方法中,view是还没有测量的,获取的宽高为0。下面代码段注释1-2处。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
//===注释1:调用activity的声名周期onresume()====
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
//===注释2:获取activity的window,decoerview为window的内部类,
//也是view的根部局。通过wm.addview方法,将ViewRootImpl对象,
//然后调用其setView()方法。其中setView()方法经过一些列折腾,最终
//调用了performTraversals()方法。开始view的测量布局绘制流程。
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
//以下代码略
}
measure流程
measure,测量。确定view的大小。
调用measure()方法,进行一些逻辑处理,然后调用onMeasure()方法,在其中调用setMeasuredDimension()设定View的宽高信息,完成View的测量操作。
(注 MeasureSpec如何确定,这里不就提了,需要注意的是,当自定义view时,需重写measure(),以区别wrap_content和match_parent)
layout流程
layout流程中,viewgroup需要根据布局的特性来重写onlayout方法。
draw流程
public void draw(Canvas canvas) {
...
int saveCount;
if (!dirtyOpaque) {
// 步骤1: 绘制本身View背景
drawBackground(canvas);
}
// 如果有必要,就保存图层(还有一个复原图层)
// 优化技巧:
// 当不需要绘制 Layer 时,“保存图层“和“复原图层“这两步会跳过
// 因此在绘制的时候,节省 layer 可以提高绘制效率
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque)
// 步骤2:绘制本身View内容 默认为空实现, 自定义View时需要进行复写
onDraw(canvas);
......
// 步骤3:绘制子View 默认为空实现 单一View中不需要实现,ViewGroup中已经实现该方法
dispatchDraw(canvas);
........
// 步骤4:绘制滑动条和前景色等等
onDrawScrollBars(canvas);
..........
return;
}
...
}
view和viewgroup都必须重写ondraw方法来完成view的绘制流程。
总结
实现自定义view的话,根据具体需要实现的效果,正确重写onMeasure,onlayout,ondraw方法即可。
最后,附上开发过程中偶尔上传过github的demo https://github.com/liujiyi/UIDemo
参考:android开发艺术探索第四章
博客:https://www.jianshu.com/p/3d2c49315d68