在看View源码的时候,有一个算数的知识我们一定要掌握,就是& | ^ 这三个运算符的叠加使用。
a&~b: 清除标志位b
a|b :增加标志位b
a&b:取出标志位b
a^b:取出a与b的不同部分
setFlag(int flags,int mask)
在View的构造函数里面有两个变量:viewFlagValues
和 viewFlagMasks
。他们通过|运算符把元素添加到里面,这些元素有:
fitsSystamWindow:viewFlagValues|=FITS_SYSTEM_WINDOWS
view_focusable:(viewFlagValues & ~FOCUSABLE_MASK)|getFoucusableAttribute(a)
…..
(还有很多比如可不可见,自动获取焦点,可不可点击都是通过|运算符添加到viewFlagValues里面的。具体可以看View的构造函数。)
上面的属性全部设置完成后,在调用setFlags(viewFlagValues,viewFlagMasks)
将值全部设置到mViewFlags里面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 void setFlags (int flags, int mask) { int old = mViewFlags; mViewFlags = (mViewFlags & ~mask) | (flags & mask); int changed = mViewFlags ^ old; if (changed == 0 ) { return ; } final int newVisibility = flags & VISIBILITY_MASK; if (newVisibility == VISIBLE) { if ((changed & VISIBILITY_MASK) != 0 ) { mPrivateFlags |= PFLAG_DRAWN; invalidate(true ); needGlobalAttributesUpdate(true ); shouldNotifyFocusableAvailable = hasSize(); } } if ((changed & GONE) != 0 ) { needGlobalAttributesUpdate(false ); requestLayout(); if (((mViewFlags & VISIBILITY_MASK) == GONE)) { if (hasFocus()) { clearFocus(); if (mParent instanceof ViewGroup) { ((ViewGroup) mParent).clearFocusedInCluster(); } } clearAccessibilityFocus(); destroyDrawingCache(); if (mParent instanceof View) { ((View) mParent).invalidate(true ); } mPrivateFlags |= PFLAG_DRAWN; } if (mAttachInfo != null ) { mAttachInfo.mViewVisibilityChanged = true ; } } if ((changed & DRAW_MASK) != 0 ) { requestLayout(); invalidate(true ); } }
通过对setFlags()方法分析他的里面有两个地方会主动触发requestLayout:
当页面设置成了GONE
当changed标志里面加上了DRAW_MASK
View里面的参数mAttachInfo是什么时候设置上值得:
在dispatchAttachedToWindow(AttachInfo info,int visibility)。
1 2 3 void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; }
在dispatchDetachedFromWindow里面将mAttacInfo设置为了null。
现在问题来了,这个dispatchAttachedToWindow()
方法是什么时候被调用的。
在ViewRootImpl里面主动触发requestLayout得时候会去一步步走到ViewRootImpl.performTraversals()
方法。在performTraversals
方法里面调用了host.dispatchAttachedToWindow
方法这个host
也就是DevorView
。Decorview
是一个ViewGroup
,所以现在进到ViewGroup得dispatchAttachedToWindow方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 void dispatchAttachedToWindow (AttachInfo info, int visibility) { mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; super .dispatchAttachedToWindow(info, visibility); mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0 ; i < count; i++) { final View child = children[i]; child.dispatchAttachedToWindow(info, combineVisibility(visibility, child.getVisibility())); } final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size(); for (int i = 0 ; i < transientCount; ++i) { View view = mTransientViews.get(i); view.dispatchAttachedToWindow(info, combineVisibility(visibility, view.getVisibility())); } } void dispatchAttachedToWindow (AttachInfo info, int visibility) { mAttachInfo = info; if (mOverlay != null ) { mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility); } mWindowAttachCount++; mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY; if (mFloatingTreeObserver != null ) { info.mTreeObserver.merge(mFloatingTreeObserver); mFloatingTreeObserver = null ; } if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0 ) { mAttachInfo.mScrollContainers.add(this ); mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); ListenerInfo li = mListenerInfo; final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li != null ? li.mOnAttachStateChangeListeners : null ; if (listeners != null && listeners.size() > 0 ) { for (OnAttachStateChangeListener listener : listeners) { listener.onViewAttachedToWindow(this ); } } int vis = info.mWindowVisibility; if (vis != GONE) { onWindowVisibilityChanged(vis); } onVisibilityChanged(this , visibility); if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0 ) { refreshDrawableState(); } needGlobalAttributesUpdate(false ); }
可以看到执行了一个循环,遍历了全部的子view的dispatchAttachedToWindow方法。
所以view的dispatchAttachedToWindow是在onResume阶段,最早由ViewRootImpl触发,然后交给了ViewGroup。遍历调用了所有的子view的dispatchedToWindow。
在ViewRootImpl的构造函数里面会创建对象mAttachInfo:View.AttachInfo
,在AttachInfo
的构造函数里面,有一个Handler的参数,他是在ViewRootImpl
类里面就已经创建好了ViewRootHandler
。这个mAttachInfo也是一层层的传递到ViewRootImpl里面的所有子view里面。那他是通过什么传递的呢,就是通过
dispatchAttachedToWindow
方法传递下去的。
既然现在分析到了这里,那我就看下子view的dispatchAttachedToWindow
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void dispatchAttachedToWindow (AttachInfo info, int visibility) { mAttachInfo = info; if (mRunQueue != null ) { mRunQueue.executeActions(info.mHandler); mRunQueue = null ; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); ListenerInfo li = mListenerInfo; final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners = li != null ? li.mOnAttachStateChangeListeners : null ; if (listeners != null && listeners.size() > 0 ) { for (OnAttachStateChangeListener listener : listeners) { listener.onViewAttachedToWindow(this ); } } }
在view的dispatchAttachedToWindow
方法里面,可以看到有这么一行代码:mRunQueue.executeActions(info.mHandler)
>。通过名字execute意思是执行的意思,他想表达的意思就是执行mRunQueue里面的全部Message事件。
那我们就来看下这个mRunQueue:HandlerActionQueue的数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; public void post (Runnable action) { postDelayed(action, 0 ); } public void postDelayed (Runnable action, long delayMillis) { final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this ) { if (mActions == null ) { mActions = new HandlerAction[4 ]; } mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } } public void removeCallbacks (Runnable action) { synchronized (this ) { final int count = mCount; int j = 0 ; final HandlerAction[] actions = mActions; for (int i = 0 ; i < count; i++) { if (actions[i].matches(action)) { continue ; } if (j != i) { actions[j] = actions[i]; } j++; } mCount = j; for (; j < count; j++) { actions[j] = null ; } } } public void executeActions (Handler handler) { synchronized (this ) { final HandlerAction[] actions = mActions; for (int i = 0 , count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null ; mCount = 0 ; } } public int size () { return mCount; } public Runnable getRunnable (int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].action; } public long getDelay (int index) { if (index >= mCount) { throw new IndexOutOfBoundsException(); } return mActions[index].delay; } private static class HandlerAction { final Runnable action; final long delay; public HandlerAction (Runnable action, long delay) { this .action = action; this .delay = delay; } public boolean matches (Runnable otherAction) { return otherAction == null && action == null || action != null && action.equals(otherAction); } } }
通过这次的代码分析可以知道,我们为什么在onCreate里面调用了view.post方法,而不是在onResume里面了。因为在onCreate里面调用view.post就会往view的mRunQueue数组里面我们的Message消息。然后在页面走到onResume的生命周期的时候,由ViewRootImpl触发了dispatchAttachedToWindow方法层层调用到子view的dispatchAttachedToWindow方法,然后触发了mRunQueue.executeAcions()方法,执行我们在onCreate里面通过view.post添加的所有Message消息 。
继续看回我们的performTraversals()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private void performTraversals () {... getRunQueue().executeActions(mAttachInfo.mHandler); performMeasure() ... } private void performLayout (WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { getRunQueue().post(new Runnable() { @Override public void run () { int numValidRequests = finalRequesters.size(); for (int i = 0 ; i < numValidRequests; ++i) { final View view = finalRequesters.get(i); view.requestLayout(); } } }); } } }
继续看ViewRootImpl.performTraversals()
可以看到perFormMeasure
方法:
1 2 3 4 private void performMeasure (int childWidthMeasureSpec, int childHeightMeasureSpec) { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
继续看下mView.measure()
,分析这个代码可以发现**:**
1. 如果页面需要强制重新布局,那么不管页面的长宽有没有改变都不会调用onMeasure方法.
2. 如果强制更新布局为false,但是needsLayout是true.然后如果长宽没有改变,那么不会执行onMeasure方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public final void measure (int widthMeasureSpec, int heightMeasureSpec) { if (forceLayout || needsLayout) { int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); setMeasuredDimensionRaw((int ) (value >> 32 ), (int ) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long ) mMeasuredWidth) << 32 | (long ) mMeasuredHeight & 0xffffffffL ); }
从这个代码可以
invalidate
核心思想:对于下面这个View树而言,如果A视图没有阿尔法通道,即完全不透明,那么理论上讲,如果只是A视图需要重绘,则不会导致A视图所在的父视图也进行重绘,只有当A视图隐藏时,才会通知父视图B绘制之前A所占领的区域即可。然而,对于包含阿尔法通道的视图系统而言,屏幕的效果是由A和B及底层所有父视图共同产生的,因此,每次当A视图需要重新绘制的时候,其底层的父视图B及所有父视图都需要进行重绘该区域。所以当A需要重新绘制的时候,必须通知父视图也绘制该区域。
invalidate的意思就是使失效:
使什么失效?使当前的状态失效。
为什么要失效?因为只有标记为失效,系统才会在下一次屏幕刷新的时候执行重绘
什么时候失效?在例如点击效果,背景颜色,高度,长度等等当前屏幕元素需要更新的时候。
那么失效做了什么?为什么等下一次刷新呢?而不是直接出发重新绘制呢?这样做啥好处?
invalidate三部曲之始于invalidate
invalidate的调用原理 :
View.invalidateInternal
打上DIRTY标记 为后面的Draw做准备。
mPrivateFlags |= PFLAG_DIRTY;
调用父组件的invalidateChild 并将自身的left,top,right,bottom通知给父组件
ViewGroup.invalidateChild
循环执行父组件的invalidateChildInPatent
,直到执行到ViewRootImpl完成后返回null。也就是尽头了。
ViewGroup.invalidateChildParent
ViewRootImpl.invalidateRectOnScreen
加入重绘区域合集
localDirty.union(dirty.left,dirty.top,dirty.right,dirty.bottom)
本次invalidate的dirty区域同之前的dirty区域合并。
1 2 3 4 5 final boolean intersected = localDirty.intersect(0 , 0 , (int ) (mWidth * appScale + 0.5f ), (int ) (mHeight * appScale + 0.5f )); if (!intersected) { localDirty.setEmpty(); }
判断要重绘的区域是不是在屏幕内,也就是判断dirty和屏幕区域是否有交叉,屏幕外的就不用绘制了。
scheduleTraversals()
开始遍历绘制。
invalidate()使用场景
直接调用invalidate方法,请求重新draw()。但只会绘制调用者本身。
setSelection()方法:请求重新draw,但只会绘制调用者本身
setVisibilify():当View可视状态在INVISIBLE转换到VISIBILE时候,会间接调用invalidate()继而绘制该view
setEnabled():请求重新draw(),但不会重新绘制任何视图包括视图调用者本身。
测量Measture过程
首先在ViewRootImpl里面触发了performTraversals()方法,在performTraversals()方法里面触发了performMeasure()方法。
1 2 3 4 5 6 private void performMeasure (int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null ) { return ; } mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
可以看到调用了mView的measure方法,在mVIew的measure方法里面调用了onMeasure方法。这个mView我们都知道是DecorView,然后DecorView的父类是Framelayout。进入DecorView的onMeasure方法,首先调用了父类的onMeasure方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0 ; int maxWidth = 0 ; int childState = 0 ; for (int i = 0 ; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0 , heightMeasureSpec, 0 ); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 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) { mMatchParentChildren.add(child); } } } } maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); 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)); count = mMatchParentChildren.size(); if (count > 1 ) { for (int i = 0 ; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { final int width = Math.max(0 , getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } final int childHeightMeasureSpec; if (lp.height == LayoutParams.MATCH_PARENT) { final int height = Math.max(0 , getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
看下ViewGroup的measureChildWidthMargins方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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); }
如果父亲是MeasureSpec.EXACTLY:
如果孩子的测量值是大于等于0的,那么这个孩子的结果值就是设置的值,他的模式就是MeasureSpec.EXACTLY。
如果孩子的测量值等于 MATCH_PARENT:那么这个孩子的结果值等于父宽度的最大值-padding-margin值,他的测量模式设置为MeasureSpec.EXACTLY。
如果孩子的测量模式等于WRAP_CONTENT:那么这个孩子的结果值等于父宽度的最大值-padding-margin值,他的测量模式设置为MeasureSpec.AT_MOST。
如果父亲是MeasureSpecc.AT_MOST:
如果孩子的测量值是确定值那么结果孩子的值就是他自己设置上去的值,测量模式设置为MeasureSpec.EXACTLY。
如果孩子的测量值是MATCH_PARENT,那么结果值是父宽度-padding-margin。测量模式 = AT_MOST
如果孩子的测量模式WRAP_CONTENT,那么结果值是父宽度-padding-magin。测量模式设置为AT_MOST
如果父亲设置的是UNSPECIFIED:
如果孩子的测量值是确定值那么结果孩子的值就是他自己设置上去的值,测量模式设置为MeasureSpec.EXACTLY。
如果孩子的测量值是MATCH_PARENT,那么结果值是父宽度-padding-margin。测量模式 = UNSPECIFIED
如果孩子的测量值是WRAP_CONTENT,那么结果值是父宽度-padding-margin。测量模式 = UNSPECIFIED