概述
本篇主要面对有一定Android基础的同学,但是Android初学者的话,也不要急。在网上可以看到很多文章,会告诉你onMeaure是什么?里面的参数是什么意思?会教你怎么获取MeaSpec的size和mode(Unspecified、Exactly、AtMost)。也会告诉你怎么重写 onLayout,怎么使用layout方法去布局子元素,怎么实现流式布局,自动换行。还有会告诉你重写onDraw(canvas),去玩canvas去绘图,然后你会了解到PathMeasure、Bitmap、Drawable等。
但是我们还是很懵逼,为啥重写这些方法之后就行了呢?ViewGroup的measure和View的measure有啥区别呢?为啥我们有这些困惑?其实是我们的学习路线有点问题。
最好的学习方式都是top-bottom,自顶向下的,你考虑下如果我直接告诉你,onMeasure就是测量函数,里面的Mespec参数就是要通过getMode方法取模式,你是不是很懵逼、很排斥。但是如果我告诉你其实整个Activity视图元素都是在一个窗口里面的(PhoneWindow),窗口有个根View(DecorView),它的展示过程有测量、布局、绘制3个操作(measure, layout, draw),他会遍历他下面所有的View和ViewGroup,ViewGroup又遍历ViewGroup。如果把DecorView看成树的根节点的话,绘制过程就是一个树的深度遍历过程。你想要操作View(控制大小,控制布局,控制样式),其实就是操作onMeasure、onLayout、onDraw。这样讲解是不是就能懂了呀 呀呀!!!
前言
在上篇5分钟告诉你,Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)分析Android源码是怎么调用Activity的生命周期中,我们提到了ViewRootImpl的创建,然后通过它的performTraversal,分别执行了perfromMeasure、performLayout、performDraw,来进行遍历所有View树中View节点。
这篇我先提出几个问题,然后咱们带着问题来找答案。
- ViewRootImpl是什么,它是什么时候创建的?
- DecorView是什么?ViewRootImpl是怎样执行遍历View的过程的?
- onMeasure、onLayout、onDraw是什么时候调用的?他们的参数是谁给他们的?
篇幅比较长,通过几个问题查看源码来解读的,请耐心的看。想先大体了解一下的同学,可以直接滑到最后结论。
【转载请注明出处:5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释) CSDN 王智博】
正片
1.初识DecorView
上篇我们讲解Activity生命周期的时候讲解到,Activity的创建和onCreate的回调是在ActivityThread.performLauchActivity,Activity创建之后,调用attach方法
step1. activity.attach(appContext, this, getInstrumentation(), r.token,...)
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
我们进去看看attach的实现,可以看到实例了一个PhoneWindow
//创建PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mUiThread = Thread.currentThread();
mMainThread = aThread;
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
mWindowManager = mWindow.getWindowManager();
之后是调用Instrument.callActivityOnCreate,然后调用Activity的onCreate和setContentView,我们来看看setContentView
step2. mInstrumentation.callActivityOnCreate(activity, r.state)
mInstrumentation.callActivityOnCreate(activity, r.state);
看到调用的是Window的setContentView方法,这里的window是phonewindow,我们看下phoneWindow的setContentView实现.
step3. Activity.setContentView(LayoutRes int layoutResID)
public void setContentView(@LayoutRes int layoutResID) {
//WINDOW的setContentView
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
可以看到 如果没有根布局会installDecor,之后调用LayoutInflater的inflate(layoutResID)方法。
step4. PhoneWindow.setContentView(LayoutRes int layoutResID)
public void setContentView(int layoutResID) {
//如果没有根View,就安装DecorView
if (mContentParent == null) {
installDecor();
}
...
//把layout布局文件解析出来,addView到根View
mLayoutInflater.inflate(layoutResID, mContentParent);
}
我们看下installDecor方法,调用generateDecor(new 一个DecorView)。那么我们再看看DecorView是啥?
step5. PhoneWindow.installDecor()
private void installDecor() {
if (mDecor == null) {
//生成Decor
mDecor = generateDecor(-1);
mDecor.setIsRootNamespace(true);
} else {
//设置window
mDecor.setWindow(this);
}
}
小结:可以看到DecorView就是一个FrameLayout,他是在setContentView里面初始化的。
class DecorView extends FrameLayout
2.ViewRootImpl是什么?什么时候初始化的?
上篇中我们介绍了,ActivityStack在通知ActivityThread LauchActivity之后,之后会通知ActivityThread handleResumeActivity,本篇文章就不详细解读了,不明白的同学可以看上篇。我们可以看到先执行performResumeActivity通知Activity去onCreate和onResume,之后调用wm.addView(decor, l),把decorView装载给wm。
step6. ActivityThread.handleResumeActivity(IBinder token ...)
void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);
if (r.window == null && !a.mFinished && willBeVisible) {
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;
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//很重要。。
wm.addView(decor, l);
}
}
那么wm是什么呢?
wm是getWindowManager()获取的,getWindowManager()会最终调用PhoneWindow的setWindowManager。
step7. Window.setWindowManager(WindowManager wm, IBinder appToken, ...)
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
...
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
可以看到mWindowManager 是createLocalWindowManager(this)返回的,那我们看下createLocalWindowManager(this)方法。
step8. WindowManagerImpl.setWindowManager(WindowManager wm, IBinder appToken, ...)
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
看到了实际上wm是WindowManagerImpl对象。那step6中的wm是WindowManagerImpl类型,我们来看下addView方法。
step9. WindowManagerImpl.addView(View view, @NonNull ViewGroup.LayoutParams params)
public void addView(View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
看到实际是调用mGlobal的addView方法。
step9. WindowManagerGlobal.addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow)
从6行可以看到, WindowManagerGlobal.add方法,创建了一个viewRootImpl,所以viewRootImpl的创建是在AMS通知ActivityThread的handleResumeActivity里面创建的,再通俗点就是Activity onResume调用之后。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
}
}
小结:ViewRootImpl是DecorView的操作类,比如measure、layout、draw、requestLayout、invalidate等,它是在handleResume操作中,WindowManagerGlobal.addView 中初始化的。
3.onMeasure、onLayout、onDraw是什么时候调用的?他们的参数是谁给他们的?
从上面第2点我们最后看到addView()方法里面,生成了ViewRootImpl,然后调用了ViewRootImpl.setView(view,wparams, panelParentView),可以看到是把 DecorView 传递进去了,我们看下setView方法.
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...省略
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...省略
}
}
}
我把不重要的都省略了,可以看到第10行调用了 requestLayout(),我们看下英文解释。在添加到窗口管理器之前安排第一次布局,确保我们在接受系统事件的时候在重新布局。所以我们说明这个 requestLayout() 方法是绘制机制的起点,我们从这开始看。
总结
1. RequestLayout的过程:View类requestLayout() -> 父类的requestLayout()并且重置缓存标志 —ViewRootImpl类requestLayout()—scheduleTraversals()—doTraversal()—performTraversals();
2:performTraversals()方法做了三件事:1)调用performMeasure();2)调用performLayout();3)调用performDraw();
Measure过程:ViewRootImpl的performMeasure()—View的measure()—具体实现类的onMeasure();
layout过程:ViewRootImpl的performLayout()—View的layout()—具体子类的onLayout();
draw过程:ViewRootImpl的performDraw()—ViewRootImpl的drawSoftware()—View的draw()—具体实现类的onDraw(),ViewGrop的dispatchDraw();
完整Android学习路径 请戳我的Android学习之旅(持续更新中...)
从源码角度分析Activity的生命周期怎么触发的(onCreate onStart onResume onPause onStop onDestroy)(附测试代码)
基于AIDL的 Activity、Service跨进程观察者模式实现与源码解读
走进源码,Android面试最常见Handler、Looper、Message问题总结与解答
Android面试---ListView原理及fling分析
5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释)
参考
从ViewRootImpl类分析View绘制的流程
View的三次measure,两次layout和一次draw
理清Activity、View及Window之间关系
View的绘制过程
Android9.0 Activity启动原理差异解析
最后
以上就是细腻大神为你收集整理的5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释) 总结的全部内容,希望文章能够帮你解决5分钟告诉你,Activity的视图绘制流程(onMeasure、onLayout、onDraw的调用和参数解释) 总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复