View的绘制流程总结以及view.post()和handler
20181017154355554.png概览
- View的坐标图解
- View的绘制流程
- view.post()和handler.post()
- ViewGroup事件分发概述
View的绘制流程:
performResumeActivity() —> windowManager.addView() —> windowManagerImpl中addView() —> mGlobal.addView() —> viewRootImpl.setView() —> requestLayout() —> scheduleTraversals() —> TraversalRunnable的回调中doTraversal() —> performTraversals()
在performTraversals()中执行performMeasure()、performLayout()、performDraw(),并且回调getRunQueue().executeActions(mAttachInfo.mHandler);
在Activity的onResume生命周期执行完毕后,获取windowManager对象调用addView()将decorView添加到ViewRootImpl中,在WindowManagerGlobal中创建viewRootImpl调用setView(),然后依次调用requestLayout(),scheduleTraversals(),doTraversal(),performTraversals(),然后一次执行performMeasure()、performLayout()、performDraw()完成View的绘制。
performMeasure流程.png public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
子View自身的大小和模式是由父布局的模式和子View自身的大小综合确定的,以子View的width为例:伪代码
int childWidthDimension;
int size = Math.max(0 , parentWidth - parantPadding);
int childWidth = 0;
int childMode;
if (父布局的mode == MeasureSpec.EXACTLY){
if(childWidthDimension >= 0){
childWidth = childWidthDimension;
childMode = MeasureSpec.EXACTLY;
} else if(childWidthDimension == LayoutParams.MATCH_PARENT){
childWidth = size;
childMode = MeasureSpec.EXACTLY;
}else if(childWidthDimension == LayoutParams.WRAP_CONTENT){
childWidth = size;
childMode = MeasureSpec.AT_MOST;
}
}else if (父布局的mode == MeasureSpec.EXACTLY){
... 同理 ...
}
view.post()和handler.post():
view.post()添加一个任务到view的任务队列mRunQueue中,在view的绘制流程performTraversals()方法中调用getRunQueue().executeActions(mAttachInfo.mHandler)执行,拿到mAttachInfo.mHandler,mAttachInfo是在主线程,所以view.post()添加的任务会在主线程中执行。(Android是handler队列中事件驱动的,view.post()任务会在view的测量布局之后执行)。
Handler.post()添加一个任务到队列,排队执行,最终执行的线程不一定是主线程,取决于looper创建的线程。
ViewGroup事件分发概述
事件分发:
- ViewGroup不拦截的情况下,由ViewGroup下发到子View的onTouchEvent()方法,如果在子View的onTouchEvent()方法中返回true拦截了事件,那么该次事件序列的后续事件都由子View接收。
- 如果子View不消费传递到onTouchEvent()方法中的事件,那么事件序列将沿着事件分发链向上回传到ViewGroup的onTouchEvent()方法。
- ViewGroup拦截的情况下,该序列的事件将由ViewGroup的onTouchEvent()方法接收,并且事件不会向下传递给子View。
如果要让事件继续往下传递,必须调用super.dispatchTouchEvent()方法。
- ViewGroup的dispatchTouchEvent()返回true,事件不往下传递 < 事件往下传递必须调用super.dispatchTouchEvent()方法 >,但是会陆续调用自己dispatchTouchEvent()方法;ViewGroup的dispatchTouchEvent()false,事件不往下传递,但是只会调用一次自己的dispatchTouchEvent()方法。
- ViewGroup的dispatchEvent() super.dispatchTouchEvent(),方法onInterceptTouchEvent()返回true或者false,事件不往子View传递,但是会调用自己的onTouchEvent() < View调用自身的onTouchEvent()方法,必须先执行dispatchTouchEvent()方法 >。
- ViewGroup的dispatchTouchEvent()方法和onInterceptTouchEvent()方法不做处理,onTouchEvent()方法返回true,事件不会往下传递;onTouchEvent()方法返回false,事件会往下传递一次。
- ViewGroup的dispatchTouchEvent()方法、onInterceptTouchEvent()方法和onTouchEvent()方法不做处理,子View的dispatchTouchEvent()方法返回true,事件会往下传递,但是不回调用子View的onTouchEvent()方法;子View的dispatchTouchEvent()方法返回false,事件会往下传递一次,但是不回调用子View的onTouchEvent()方法。
- ViewGroup的dispatchTouchEvent()方法、onInterceptTouchEvent()方法和onTouchEvent()方法不做处理,子View的dispatchTouchEvent()方法调用super.dispatchTouchEvent()方法:onTouchEvent()方法返回true,事件往下传递,且依次会调用ViewGroup的dispatchTouchEvent()、onInterceptTouchEvent()、View的dispatchTouchEvent()、和View的onTouchEvent(),但是不会调用ViewGroup的onTouchEvent()方法;onTouchEvent()方法返回false,事件往下传递,且依次会调用ViewGroup的dispatchTouchEvent()、onInterceptTouchEvent()、View的dispatchTouchEvent()、和View的onTouchEvent(),以及ViewGroup的onTouchEvent()方法,但是事件只会往下传递一次。