/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
void scheduleTraversals() {
if (!mTraversalScheduled) { // 判断当前是否已经在做遍历
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
// 这里是“Project Butter”的产物,一旦有VSYNC信号来临,mTraversalRunnable就会被调用
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
1、mTraversalRunnable:
/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
try {
// 最终实现View树遍历的函数
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
可以看到遍历View树最终的入口函数为调用ViewRootImpl的performTraversals函数;
2、ViewRootImpl#performTraversals:
/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
private void performTraversals() {
// 记录ViewRootImpl管理的View树的根节点,final修饰,避免运行过程中修改
final View host = mView;
/** View树遍历的主要三个步骤measure、layout、draw **/
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
}
其实这三个函数仅是做了一层简单封装:
/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 进而调用View的measure函数
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performDraw() {
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
draw(fullRedrawNeeded);
即总共分为三个主要过程:measure过程,layout过程,draw过程;
/** \frameworks\base\core\java\android\view\View.java **/
// @params 用于确定视图的宽度和高度的规格和大小
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
onMeasure(widthMeasureSpec, heightMeasureSpec);
......
}
可以看到最终是调用onMeasure来实现;而传入的参数:widthMeasureSpec,heightMeasureSpec;是用来确定视图的宽度与高度的规格和大小;
/** \frameworks\base\core\java\android\view\View.java **/
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
private static final int UNSPECIFIED = 0 << MODE_SHIFT;
private static final int EXACTLY = 1 << MODE_SHIFT;
private static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); }
public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK);}
static int adjust(int measureSpec, int delta) {
final int mode = getMode(measureSpec);
......
return makeMeasureSpec(size, mode);
}
public static String toString(int measureSpec) { ...... }
}
其中specMode一共有三种类型,如下所示:
1. EXACTLY
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
2. AT_MOST
表示子视图最多只能是specSize中指定的大小,开发人员应该尽可能小得去设置这个视图,并且保证不会超过specSize。系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。
3. UNSPECIFIED
表示开发人员可以将视图按照自己的意愿设置成任意的大小,没有任何限制。这种情况比较少见,不太会用到。
/** \frameworks\base\core\java\android\view\View.java **/
// 这里仅是View提供的默认的onMeasure实现,具体的View需要根据自身情况进行重写
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
// AT_MOST、AT_MOST情况返回measureSpec对应的specSize,否则返回getSuggestedMinimumHeight等
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.AT_MOST:
result = specSize;
break;
}
return result;
}
/** 计算并设置视图界面大小 **/
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;
}
在onMeasure()方法中调用setMeasuredDimension()方法来设定测量出的大小,这样一次measure过程就结束了。
/** \frameworks\base\core\java\android\widget\FrameLayout.java **/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount(); // 由于ViewGroup是多个View的组合,获取子View的个数
/** 判断父对象的Mode请求 **/
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
// @value final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
mMatchParentChildren.clear();
int maxHeight = 0; // 所有子对象中测量到的最大高度
int maxWidth = 0; // 所有子对象中测量到的最大宽度
int childState = 0;
for (int i = 0; i < count; i++) { // 循环处理所有子对象
final View child = getChildAt(i); // ViewGroup中的方法,获取子View
if (mMeasureAllChildren || child.getVisibility() != GONE) { // 当可见性为GONE时,不需要测量
// 递归调用此函数,对子View进行measure过程
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
/***measure过程,这里用以获取视图的最大高度与宽度 **/
maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
// 可以看到MATCH_PARENT的View只能显示一个
mMatchParentChildren.add(child);
}
}
}
}
// ViewGroup本身的Padding要计算在内
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// 计算适应当前View的最小高度值
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// 将背景图片的大小也要计算在内
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
/** 设置测量好的结果 **/
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
......
}
可以看到重写的onMeasure方法流程也大致相同,都是综合考虑父控件的尺寸大小,以及Padding,Margin等得到视图的最大Size,通过setMeasureDeimenson最后保存设置计算出来的结果。
/** \frameworks\base\core\java\android\view\ViewGroup.java **/
// 这里需要综合考虑Padding与Margins
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 测量子View的MeasureSpce值
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函数;如果子View是ViewGroup,则会依然按照前面的步骤再递归调用其子View的measure一直遍历下去
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
/** 可以看到measureChildWithMargins就是 measureChildren +measureChild函数,加上考虑Margins的版本 **/
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];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
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);
}
Measure过程:由上面可见视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
因此我们在自定义View时可以重写onMeasure函数,通过setMeasureDeimenson设置最终的视图大小。
/** \frameworks\base\core\java\android\widget\View.java **/
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
// 同样形式调用onLayout来实现
onLayout(changed, l, t, r, b);
......
}
}
l,t,r,b分别表示该View对象与父对象的左上右下边框的距离。
/** \frameworks\base\core\java\android\view\View.java **/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
/** \frameworks\base\core\java\android\view\ViewGroup.java **/
@Override
protected abstract void onLayout(boolean changed,int l, int t, int r, int b);
/** \frameworks\base\core\java\android\widget\FrameLayout.java **/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false/* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount(); // 获取子对象个数
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
mForegroundBoundsChanged = true;
for (int i = 0; i < count; i++) { // 同样遍历所有子对象
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
// 根据对应的Layout中设置Gravity属性进行布局
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
// 递归调用子View的layout过程
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
Layout过程也是一个自View树根从上往下遍历进行layout布局的过程,需要根据具体的Layout以及Gravity等属性完成子View在父View中的布局。
/** \frameworks\base\core\java\android\view\View.java **/
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, 如果需要的话,绘制背景
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 对于一些常见情况,可以跳过Step2与Step5直接进行绘制
finalint 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);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// we're done...
return;
}
/** 在一些对速度要求不高的情况(不常见),使用完整的绘制过程*/
}
/** 可以看到这两个重要的函数,并未给出具体的实现 **/
/** 重写该函数即可绘制自己自定义的View
* Implement this to do your drawing.
*/
protected void onDraw(Canvas canvas) {}
/** 重写该函数用以绘制子View
* Called by draw to draw the child views. This may be overridden
* by derived classes to gain control just before its children are drawn
* (but after its own view has been drawn).
* @param canvas the canvas on which to draw the view
*/
protected void dispatchDraw(Canvas canvas) {}
}
/** \frameworks\base\core\java\android\view\View.java **/
public void requestLayout() {
......
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
// @value protected ViewParent mParent;
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
可以看到当需要进行Layout时,会主动调用ViewParent(ViewParent仅是一个Interface,其具体运行时类型为ViewRootImpl)的requestLayout函数:
/**\frameworks\base\core\java\android\view\ViewRootImpl **/
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 该函数用来判断当前线程是否是主线程,因为只有主线程才能操作UI对象
checkThread();
mLayoutRequested = true;
// 可以看到这里调用了前面提到的View树的遍历入口函数
scheduleTraversals();
}
}
可以看到这里最终调用上面提到的ViewTree遍历的入口函数scheduleTraversals函数开始View树的遍历。
/** \frameworks\base\core\java\android\view\View.java **/
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
// 这里申请调用requestLayout来遍历View树
requestLayout();
}
最终调用上面的requestLayout来遍历View树。
请求View树的draw()过程,但只绘制“需要重绘”的视图。
/** \frameworks\base\core\java\android\view\View.java **/
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return requestFocusNoSearch(direction, previouslyFocusedRect);
}
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
(mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
......
handleFocusGainInternal(direction, previouslyFocusedRect);
return true;
}
void handleFocusGainInternal(@FocusRealDirectionint direction, Rect previouslyFocusedRect) {
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
mPrivateFlags |= PFLAG_FOCUSED;
View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
if (mParent != null) {
// 遍历子View
mParent.requestChildFocus(this, this);
}
if (mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
// 看下这个函数
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
}
}
protected void onFocusChanged(boolean gainFocus, @FocusDirectionint direction,
@Nullable Rect previouslyFocusedRect) {
.....
invalidate(true);
.....
}
可以看到最终调用下面情况invalidate来进行重绘。
请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”
视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。
1)View#invalidate:
/** \frameworks\base\core\java\android\view\View.java **/
public void invalidate() {
invalidate(true);
}
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) { // 用于判断是否忽略这个invalidate操作
return;
}
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
// 设置相应invalidate标志位
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN; // 设置此标志用以保证invalidate的执行
}
mPrivateFlags |= PFLAG_DIRTY; // 表示由相应Dirty区域需要绘制
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
/*********** 遍历子树*********/
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
// Damage the entire IsolatedZVolume receiving this view's shadow.
if (isHardwareAccelerated() && getZ() != 0) {
damageShadowReceiver();
}
}
}
2)ViewRootImpl#invalidateChildInParent:
/** \frameworks\base\core\java\android\view\ViewRootImpl.java **/
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
.....
final Rect localDirty = mDirty;
if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
// 遍历View树入口函数
scheduleTraversals();
}
return null;
}
1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。
2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。
3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。
同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。
4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。