Measure 分析
这里的measure
方法为final
所以不可重写,该方法主要是用来计算出view自身的实际大小,并设置宽高。
view的实际大小是由父view与自身决定的,只能通过重写方法onMeasure
来获取
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int oWidth = insets.left insets.right;
int oHeight = insets.top insets.bottom;
widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
}
// Suppress sign extension for the low bytes
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
//如果当前flags不为forceLayou或者与上次测量规格相比没有改变,就不进行重新测量,直接从缓存中获取
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " getId() ": "
getClass().getName() "#onMeasure() did not set the"
" measured dimension by calling"
" setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
//存入measure缓存数组中
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
onMeasure
该方法的两个参数由两部分组成,高2位表示mode
也就是测量模式,低30位表示size
也就是测量大小。
这里模式对应EXACTLY
表示确定大小、AT_MOST
表示最大大小、UNSPECIFIED
表示不确定大小,并且它不受父布局控制。通过阅读源码得知在PhoneWindow
的DecorView
中对应的模式为EXACTLY
,大小size就是对应屏幕的尺寸,而view大小是由父view与子view共同决定
//这里的两个参数都是由父布局传进来的
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left insets.right;
int opticalHeight = insets.top insets.bottom;
measuredWidth = optical ? opticalWidth : -opticalWidth;
measuredHeight = optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
//如果当前mode为EXACTLY或AT_MOST,就返回specSize,这是系统默认的风格
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
建议的最小宽或高,是由背景的尺寸与设置view
最小宽高共同决定的。当背景为空时,就是自身最小宽高,否则就是与背景的最大值
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
public int getMinimumWidth() {
//这里是获取drawable的原始宽度,shapeDrawable无原始宽高,而BitmapDrawable有原始宽高
final int intrinsicWidth = getIntrinsicWidth();
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}
ViewGroup
的measure
绘制子view方法:
ViewGroup
类中定义了measureChildren,measureChild,measureChildWithMargins
实现对子view的测量measureChildren
内部循环调用measurechild
方法。measureChild
与measureChildWithMargins
方法区别:- 是否把padding与margin作为子视图大小
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; i) {
final View child = children[i];
//判断子view是否是GONE,GONE的话就不进行绘制
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
//让每一个子view去绘制它自己,必须传入父view的测量spec与子view自己的padding值
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
//获取子视图的LayoutParams
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//这里绘制子view不仅加入了padding,还加入了margin
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);
//调用view的measure方法,在该方法中回调onMeasure实现子视图的测量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//获取父view的mode与size
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
//获取父view剩余大小,小于0则返回0
int size = Math.max(0, specSize - padding);
//定义存储变量的值
int resultSize = 0;
int resultMode = 0;
//根据父view的mode值进行分别处理
switch (specMode) {
//默认mode为EXACTLY,如果父viewmode为EXACTLY
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
//子view中设置了大于0的具体数值,就直接将值赋值给size,并设置子view的mode为EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
//如果没有设置具体的数值,当前子view全部包裹父view,就设置子view大小为父view剩余空间,mode为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) {
//这里childView决定自身的大小,但不能超过父view分配给子view的剩余空间
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
//现在父view的测量模式改为AT_MOST
// 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) {
//这里子view的match_parent与wrap_content所展示的结果一样,于是就需要设置具体的数值
// 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;
//测量模式为UNSECIFIED,子view的大小就不再受父view的控制
// 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;
}
//根据最后得到的size与mode调用measureSpec方法转换成32位数值
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
决定view的大小是view的setMeasureDimession
方法。当通过setMeasuredDimension
方法最终设置完成,View的mMeasuredWidth
和mMeasuredHeight
成员才会有具体的数值。
getMeasuredWidth
方法返回的是测量后的宽度,这个宽度是当setMeasuredDimension方法被调用刷新
方法(measure方法最终会调用setMeasuredDimension
)被调用后刷新的。
所以getMeasuredWidth
返回值的大小,取决于setMeasuredDimension
。
Measure 分析总结
MeasureSpec
测量规格为int型,值由高2位规格模式specMode
和低30位具体尺寸specSize
组成。其中specMode
只有三种值:MeasureSpec.EXACTLY
:确定模式,子View的大小是确定的,由specSize
决定;MeasureSpec.AT_MOST:最多模式,子View的大小最多是specSize
指定的值,不能超过父view大小MeasureSpec.UNSPECIFIED
:没有指定模式,子view自己决定
- View的
measure
方法是final
的,不允许重载,View子类只能重写onMeasure
来完成自身的测量 - 顶层
DecorView
测量时的MeasureSpec
是由ViewRootImpl中getRootMeasureSpec
方法确定的(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小) ViewGroup
类中提供了measureChildren
,measureChild和measureChildWithMargins
方法,子view大小由父view及自身决定。- 当ViewGroup的子类需要使用layout_margin,那么就必须要求LayoutParams继承子MarginLayoutParams或者重写generateLayoutParams方法,否则无法使用layout_margin参数 如下: /** * 子视图需要其margin,必须重写该方法 */ override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { return MarginLayoutParams(context, attrs) }
- 使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须setMeasuredDimension方法被调用刷新后才行
onLayout 分析
代码语言:javascript复制public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//判断布局是否改变,确定是否要重新设置布局
boolean changed = isLayoutModeOptical(mParent) ?
//setFrame 设置4个点,即初始化mLeft,mTop,mRight,mBottom
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
//改变布局重新设置布局,这个方法是父容器用来确定子元素位置的,里面是空的实现,可以自己自定义设置布局
onLayout(changed, l, t, r, b);
if (shouldDrawRoundScrollbar()) {
if(mRoundScrollbarRenderer == null) {
mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
}
} else {
mRoundScrollbarRenderer = null;
}
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
notifyEnterOrExitForAutoFillIfNeeded(true);
}
}
ViewGroup源码查看采用LinearLayout的onLayout 代码进行分析
代码语言:javascript复制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;
//拿到子view的宽度
// Where right end of child should go
final int width = right - left;
//计算父窗口推荐子view的位置
int childRight = width - mPaddingRight;
//获取child的可用空间
// Space available for child
int childSpace = width - paddingLeft - mPaddingRight;
//通过ViewGroup的getChildCount获取Viewgroup的子view个数
final int count = getVirtualChildCount();
//获取Gravity属性值
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
//根据majorGravity来获取childTop值
switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
//childTop会逐渐增大,意味着后面的子元素会被放在越靠下的位置
childTop = mPaddingTop bottom - top - mTotalLength;
break;
// mTotalLength contains the padding already
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop (bottom - top - mTotalLength) / 2;
break;
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}
//循坏viewgroup的子view个数
for (int i = 0; i < count; i ) {
//获取到当前子view
final View child = getVirtualChildAt(i);
if (child == null) {
childTop = measureNullChild(i);
} else if (child.getVisibility() != GONE) {
//子view的宽高是由measure来决定的,因此measure是用于确定layout布局的视图范围
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) {
//设置不同的childLeft
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;
//根据垂直排列获取child的layout来重新设置child的位置
setChildFrame(child, childLeft, childTop getLocationOffset(child),
childWidth, childHeight);
childTop = childHeight lp.bottomMargin getNextLocationOffset(child);
i = getChildrenSkipCount(child, i);
}
}
}
private void setChildFrame(View child, int left, int top, int width, int height) {
//调用子view的layout布局设置
child.layout(left, top, left width, top height);
}
onLayout 分析总结
- 从源码中得知
View
的layout
方法可以重载,而ViewGroup的layout方法不可以重载,子类必须实现抽象方法onLayout来设置自己的位置 - View的测量宽高是在
measure
过程中获取,但是view的最终宽高是在layout
过程中获取 比如:getWidth
返回的是最终layout
出来的宽度,在View
代码中返回的是【mRight - mLeft】
,这个mRight
和mLeft
,是在setFrame
方法被调用后赋值的(layout
方法最终会调用setFrame
)
Draw 分析
由于ViewGroup中没有重写View的draw
方法,这里只分析View的draw
方法
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
//绘制背景
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {/绘制自己
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
//绘制子view,如果当前没有子view则不需要绘制,在view中是空实现,具体在viewgroup中实现方法
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
//绘制装饰
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
//绘制默认的焦点高亮显示
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
//绘制子view,遍历所有子元素,那么这个draw就一层一层传递下去
protected void dispatchDraw(Canvas canvas) {
boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i ) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
if (controller.willOverlap()) {
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
}
controller.start();
mGroupFlags &= ~FLAG_RUN_ANIMATION;
mGroupFlags &= ~FLAG_ANIMATION_DONE;
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart(controller.getAnimation());
}
}
int clipSaveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX mPaddingLeft, mScrollY mPaddingTop,
mScrollX mRight - mLeft - mPaddingRight,
mScrollY mBottom - mTop - mPaddingBottom);
}
// We will draw our child's animation, let's reset the flag
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
final long drawingTime = getDrawingTime();
if (usingRenderNodeProperties) canvas.insertReorderBarrier();
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
int transientIndex = transientCount != 0 ? 0 : -1;
// Only use the preordered list if not HW accelerated, since the HW pipeline will do the
// draw reordering internally
final ArrayList<View> preorderedList = usingRenderNodeProperties
? null : buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i ) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {/调用view的draw方法进行绘制子view
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex ;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
while (transientIndex >= 0) {
// there may be additional transient views after the normal views
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex ;
if (transientIndex >= transientCount) {
break;
}
}
if (preorderedList != null) preorderedList.clear();
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
if (usingRenderNodeProperties) canvas.insertInorderBarrier();
if (debugDraw()) {
onDebugDraw(canvas);
}
if (clipToPadding) {
canvas.restoreToCount(clipSaveCount);
}
// mGroupFlags might have been updated by drawChild()
flags = mGroupFlags;
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
mLayoutAnimationController.isDone() && !more) {
// We want to erase the drawing cache and notify the listener after the
// next frame is drawn because one extra invalidate() is caused by
// drawChild() after the animation is over
mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
final Runnable end = new Runnable() {
@Override
public void run() {
notifyAnimationListener();
}
};
post(end);
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
//调用view的draw方法
return child.draw(canvas, this, drawingTime);
}
View中的特殊方法setWillNotDraw
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
当一个view不需要绘制任何内容,那么设置这个标记为true后,系统就会做出相应的优化,默认情况,view没有启用这个优化,但是viewgroup启用了这个优化,当自定义控件继承自viewgroup时并且本身不具备绘制功能,就可以开启这个标记来让系统优化。但是当viewgroup需要通过onDraw方法来绘制内容时,于是就要显示的关闭WILL_NOT_DRAW方法
Draw 分析总结
- 当是一个viewgroup,那么要递归所有的子view已完成绘制
- 从源码中可知viewgroup不绘制任何内容,真正绘制在子view中进行,也就是调用子view的draw方法进行绘制
- View动画和ViewGroup布局动画区别:
- View动画指的是View自身的动画,可以通过setAnimation添加
- ViewGroup布局动画指的是对ViewGroup内部子视图时设置的动画,可以在xml布局文件中对ViewGroup设置layoutAnimation属性