view的绘制流程
一下内容均是从这个链接中来的:链接: [link](https://www.jianshu.com/p/1075d7d521ec).
这段时间学习自定义view,在这个过程中,碰到了一个问题:在onCreate()方法中获取view的宽高失败,没有获取到,返回的是0;
这是因为view的绘制还没有开始,只有在onResume()方法执行之后,才会开始准备绘制view,所以我们如果要获取view的宽高,就必须要在view绘制完后才能获取
以LinearLayout 布局为例,向里面添加三个textView
当onResume()方法执行完后,经过一系列的方法调用,最后会进入ViewRootImpl类的requestLayout()方法
下面是setContentView() 中的源码,里面有这样一行代码 mLayoutInflater.inflate(layoutResID, mContentParent); 会来到 ViewRootImpl 类的 requestLayout() 方法。View 的绘制流程就是从这个方法开始的。
@Override
public void requestLayout ( ) {
if ( ! mHandlingLayoutInLayoutRequest) {
checkThread ( ) ;
mLayoutRequested = true ;
scheduleTraversals ( ) ;
}
}
void scheduleTraversals ( ) {
if ( ! mTraversalScheduled) {
mTraversalScheduled = true ;
mTraversalBarrier = mHandler. getLooper ( ) . getQueue ( ) . postSyncBarrier ( ) ;
mChoreographer. postCallback (
Choreographer. CALLBACK_TRAVERSAL, mTraversalRunnable, null) ;
if ( ! mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput ( ) ;
}
notifyRendererOfFramePending ( ) ;
pokeDrawLockIfNeeded ( ) ;
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run ( ) {
doTraversal ( ) ;
}
}
void doTraversal ( ) {
if ( mTraversalScheduled) {
mTraversalScheduled = false ;
mHandler. getLooper ( ) . getQueue ( ) . removeSyncBarrier ( mTraversalBarrier) ;
if ( mProfile) {
Debug. startMethodTracing ( "ViewAncestor" ) ;
}
performTraversals ( ) ;
if ( mProfile) {
Debug. stopMethodTracing ( ) ;
mProfile = false ;
}
}
}
performTraversals(重点)
经过一系列的方法调用,最后会进入这个方法中来,这时候view的绘制才真正开始,这个方法中主要调用了以下三个方法来实现view的绘制:
View绘制流程第一步:
1、performMeasure 这个方法主要是对view进行测量,如果是viewGroup的话,比如LinearLayout,LinearLayout在调用onMeasure()方法的时候,会不断的循环测量子View,如果是垂直方向,高度是子View的高度叠加,我们现在来看看是怎么测量子View的。
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) ;
}
}
public final void measure ( int widthMeasureSpec, int heightMeasureSpec) {
onMeasure ( widthMeasureSpec, heightMeasureSpec) ;
}
@Override
protected void onMeasure ( int widthMeasureSpec, int heightMeasureSpec) {
if ( mOrientation == VERTICAL) {
measureVertical ( widthMeasureSpec, heightMeasureSpec) ;
} else {
measureHorizontal ( widthMeasureSpec, heightMeasureSpec) ;
}
}
void measureVertical ( int widthMeasureSpec, int heightMeasureSpec) {
for ( int i = 0 ; i < count; ++ i) {
final View child = getVirtualChildAt ( i) ;
measureChildBeforeLayout ( child, i, widthMeasureSpec, 0 ,
heightMeasureSpec, usedHeight) ;
final int childHeight = child. getMeasuredHeight ( ) ;
final int totalLength = mTotalLength;
mTotalLength = Math. max ( totalLength, totalLength + childHeight + lp. topMargin +
lp. bottomMargin + getNextLocationOffset ( child) ) ;
}
int heightSize = mTotalLength;
heightSize = Math. max ( heightSize, getSuggestedMinimumHeight ( ) ) ;
int heightSizeAndState = resolveSizeAndState ( heightSize, heightMeasureSpec, 0 ) ;
setMeasuredDimension ( resolveSizeAndState ( maxWidth, widthMeasureSpec, childState) ,
heightSizeAndState) ;
}
LinearLayout在调用onMeasure()方法的时候,会不断的循环测量子View,如果是垂直方向,高度是子View的高度叠加,我们现在来看看是怎么测量子View的。
void measureChildBeforeLayout ( View child, int childIndex,
int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
int totalHeight) {
measureChildWithMargins ( child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight) ;
}
protected void measureChildWithMargins ( View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = ( MarginLayoutParams) child. getLayoutParams ( ) ;
final int childWidthMeasureSpec = getChildMeasureSpec ( parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp. leftMargin + lp. rightMargin
+ widthUsed, lp. width) ;
final int childHeightMeasureSpec = getChildMeasureSpec ( parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp. topMargin + lp. bottomMargin
+ heightUsed, lp. height) ;
child. measure ( childWidthMeasureSpec, childHeightMeasureSpec) ;
}
getChildMeasureSpec()这个方法可以说很重要
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) {
case MeasureSpec. EXACTLY:
if ( childDimension >= 0 ) {
resultSize = childDimension;
resultMode = MeasureSpec. EXACTLY;
} else if ( childDimension == LayoutParams. MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec. EXACTLY;
} else if ( childDimension == LayoutParams. WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec. AT_MOST;
}
break ;
case MeasureSpec. AT_MOST:
if ( childDimension >= 0 ) {
} else if ( childDimension == LayoutParams. MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec. AT_MOST;
} else if ( childDimension == LayoutParams. WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec. AT_MOST;
}
break ;
}
return MeasureSpec. makeMeasureSpec ( resultSize, resultMode) ;
}
View的绘制流程第一步是onMeasure(),该方法用来测量和指定布局到底占多大的宽高,因为控件的宽高是由父布局和控件本身来决定的,所以测量是不断的往内走,而最终确定宽高是由内不断的往外走,是递归的方式。
View绘制流程第二步:performLayout
private void performLayout ( WindowManager. LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
host. layout ( 0 , 0 , host. getMeasuredWidth ( ) , host. getMeasuredHeight ( ) ) ;
}
public void layout ( int l, int t, int r, int b) {
onLayout ( changed, l, t, r, b) ;
}
接下来如果按照我们写的布局小示例,将会调用LinearLayout中的onLayout()方法
@Override
protected void onLayout ( boolean changed, int l, int t, int r, int b) {
if ( mOrientation == VERTICAL) {
layoutVertical ( l, t, r, b) ;
} else {
layoutHorizontal ( l, t, r, b) ;
}
}
void layoutVertical ( int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
final int width = right - left;
int childRight = width - mPaddingRight;
int childSpace = width - paddingLeft - mPaddingRight;
final int count = getVirtualChildCount ( ) ;
final int majorGravity = mGravity & Gravity. VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity. RELATIVE_HORIZONTAL_GRAVITY_MASK;
switch ( majorGravity) {
case Gravity. BOTTOM:
childTop = mPaddingTop + bottom - top - mTotalLength;
break ;
case Gravity. CENTER_VERTICAL:
childTop = mPaddingTop + ( bottom - top - mTotalLength) / 2 ;
break ;
case Gravity. TOP:
default :
childTop = mPaddingTop;
break ;
}
for ( int i = 0 ; i < count; i++ ) {
final View child = getVirtualChildAt ( i) ;
if ( child == null) {
childTop += measureNullChild ( i) ;
} else if ( child. getVisibility ( ) != GONE) {
final int childWidth = child. getMeasuredWidth ( ) ;
final int childHeight = child. getMeasuredHeight ( ) ;
final LinearLayout. LayoutParams lp =
( LinearLayout. LayoutParams) child. getLayoutParams ( ) ;
int gravity = lp. gravity;
if ( gravity < 0 ) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection ( ) ;
final int absoluteGravity = Gravity. getAbsoluteGravity ( gravity, layoutDirection) ;
switch ( absoluteGravity & Gravity. HORIZONTAL_GRAVITY_MASK) {
case Gravity. CENTER_HORIZONTAL:
childLeft = paddingLeft + ( ( childSpace - childWidth) / 2 )
+ lp. leftMargin - lp. rightMargin;
break ;
case Gravity. RIGHT:
childLeft = childRight - childWidth - lp. rightMargin;
break ;
case Gravity. LEFT:
default :
childLeft = paddingLeft + lp. leftMargin;
break ;
}
if ( hasDividerBeforeChildAt ( i) ) {
childTop += mDividerHeight;
}
childTop += lp. topMargin;
setChildFrame ( child, childLeft, childTop + getLocationOffset ( child) ,
childWidth, childHeight) ;
childTop += childHeight + lp. bottomMargin + getNextLocationOffset ( child) ;
i += getChildrenSkipCount ( child, i) ;
}
}
}
总结一下,从上面分析可以看出layout也是从顶层父View向子View的递归调用view.layout方法的过程,即父View根据第一步performMeasure,来获取子View所的布局大小和布局参数,将子View放在合适的位置上,不过这个方法没有再往外走,只是不断的往里面走
View绘制流程第三步:performDraw
这个方法主要是画,比如画文本,画圆,画线等等,但是对于继承ViewGroup的布局来说,不会用这个方法
private void performDraw ( ) {
try {
draw ( fullRedrawNeeded) ;
} finally {
mIsDrawing = false ;
Trace. traceEnd ( Trace. TRACE_TAG_VIEW) ;
}
}
private void draw ( boolean fullRedrawNeeded) {
Surface surface = mSurface;
if ( ! surface. isValid ( ) ) {
return ;
}
if ( ! drawSoftware ( surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty) ) {
return ;
}
}
private boolean drawSoftware ( Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
final int left = dirty. left;
final int top = dirty. top;
final int right = dirty. right;
final int bottom = dirty. bottom;
canvas = mSurface. lockCanvas ( dirty) ;
mView. draw ( canvas) ;
}