[置顶] 源码分析:onAttach, onMeasure, onLayout, onDraw 的顺序。

从前文《 源码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?》中知道了activity第一个view或者说根view或者说mDecorView 其实就是一个FrameLayout,以及是在系统handleResume的时候加入到系统windowManager中的,并由framework中的ViewRootImpl 接管,通过ViewRootImpl.setView() 开始整个显示过程的。这次着重梳理一下view的显示过程(onAttach, onMeasure, onLayout, onDraw )在源码中的过程。

从ViewRootImpl.setview 开始。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {  
  2.     synchronized (this) {  
  3.         if (mView == null) {  
  4.             mView = view;  
  5.   
  6.             <span style="color:#ff0000;">requestLayout();</span>  
  7.             if ((mWindowAttributes.inputFeatures  
  8.                     & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {  
  9.                 mInputChannel = new InputChannel();  
  10.             }  
  11.             try {  
  12.                 mOrigWindowType = mWindowAttributes.type;  
  13.                 res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,  
  14.                         getHostVisibility(), mAttachInfo.mContentInsets,  
  15.                         mInputChannel);  
  16.             } catch (RemoteException e) {  
  17.                 mAdded = false;  
  18.                 mView = null;  
  19.                 mAttachInfo.mRootView = null;  
  20.                 mInputChannel = null;  
  21.                 mFallbackEventHandler.setView(null);  
  22.                 unscheduleTraversals();  
  23.                 throw new RuntimeException("Adding window failed", e);  
  24.             } finally {  
  25.                 if (restore) {  
  26.                     attrs.restore();  
  27.                 }  
  28.             }  
在第一次赋值mView的时候,会调用ViewRootImpl.requestLayout();

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void requestLayout() {  
  2.     checkThread();  
  3.     mLayoutRequested = true;  
  4.     scheduleTraversals();  
  5. }  
进而scheduleTraversals();

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void scheduleTraversals() {  
  2.     if (!mTraversalScheduled) {  
  3.         mTraversalScheduled = true;  
  4.   
  5.         //noinspection ConstantConditions  
  6.         if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {  
  7.             final long now = System.nanoTime();  
  8.             Log.d(TAG, "Latency: Scheduled traversal, it has been "  
  9.                     + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)  
  10.                     + "ms since the last traversal finished.");  
  11.         }  
  12.   
  13.         sendEmptyMessage(DO_TRAVERSAL);  
  14.     }  
  15. }  
进而在handleMessage() 中

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void handleMessage(Message msg) {  
  3.     switch (msg.what) {  
  4.     case DO_TRAVERSAL:  
  5.   
  6.         performTraversals();  
  7. span style="white-space:pre">    </span>}  
进而就是performTraversals(),也就是本次分析的重点。

这个函数比较长,不适合把全部函数代码都 贴上来。就分段叙述。

1. 函数刚开始的部分,初始化了一些后面会用到的变量和标志位。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. final View host = mView;  
  2. WindowManager.LayoutParams lp = mWindowAttributes;  
  3. final View.AttachInfo attachInfo = mAttachInfo;  
  4. CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();  
  5. Rect frame = mWinFrame;  
  6. mTraversalScheduled = false;  
  7. mWillDrawSoon = true;  
  8. boolean windowSizeMayChange = false;  
  9. boolean fullRedrawNeeded = mFullRedrawNeeded;  
  10. boolean newSurface = false;  
  11. boolean surfaceChanged = false;  

2.  接下来,是一个重要的判断, 如果是初次执行,则调用host.dispatchAttachedToWindow(attachInfo, 0);

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (mFirst) {  
  2.     // 略去一大堆赋值  
  3.     mLastConfiguration.setTo(host.getResources().getConfiguration());  
  4.     host.dispatchAttachedToWindow(attachInfo, 0);  
  5.     //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);  
  6.   
  7.     host.fitSystemWindows(mAttachInfo.mContentInsets);  
  8.   
  9. else {  
  10.     desiredWindowWidth = frame.width();  
  11.     desiredWindowHeight = frame.height();  
  12.     if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {  
  13.         if (DEBUG_ORIENTATION) Log.v(TAG,  
  14.                 "View " + host + " resized to: " + frame);  
  15.         fullRedrawNeeded = true;  
  16.         mLayoutRequested = true;  
  17.         windowSizeMayChange = true;  
  18.     }  
  19. }  
在dispatchAttachedToWindow()中重点处理了三件事:

 2.1. onAttachedToWindow();

 2.2. listener.onViewAttachedToWindow(this); 

 2.3 onWindowVisibilityChanged(vis);

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void dispatchAttachedToWindow(AttachInfo info, int visibility) {  
  2.     //System.out.println("Attached! " + this);  
  3.     mAttachInfo = info;  
  4.     mWindowAttachCount++;  
  5.     onAttachedToWindow();  
  6.   
  7.     final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =  
  8.             mOnAttachStateChangeListeners;  
  9.     if (listeners != null && listeners.size() > 0) {  
  10.         // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to  
  11.         // perform the dispatching. The iterator is a safe guard against listeners that  
  12.         // could mutate the list by calling the various add/remove methods. This prevents  
  13.         // the array from being modified while we iterate it.  
  14.         for (OnAttachStateChangeListener listener : listeners) {  
  15.             listener.onViewAttachedToWindow(this);  
  16.         }  
  17.     }  
  18.   
  19.     int vis = info.mWindowVisibility;  
  20.     if (vis != GONE) {  
  21.         onWindowVisibilityChanged(vis);  
  22.     }  
  23. }  
在这里onAttachedToWindow() 的注释带来了一个问题: 仅保证会在onDraw 前调用,而不保证在onMeasure 之前或者之后调用 onAttachedToWindow。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * This is called when the view is attached to a window.  At this point it 
  3.  * has a Surface and will start drawing.  Note that this function is 
  4.  * guaranteed to be called before {@link #onDraw(android.graphics.Canvas)}, 
  5.  * however it may be called any time before the first onDraw -- including 
  6.  * before or after {@link #onMeasure(int, int)}. 
  7.  * 
  8.  * @see #onDetachedFromWindow() 
  9.  */  
  10. protected void onAttachedToWindow() {  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected void onAttachedToWindow() {  
  2.     // Order is important here: LayoutDirection MUST be resolved before Padding  
  3.     // and TextDirection  
  4.     resolveLayoutDirectionIfNeeded();  
  5.     resolvePadding();  
  6.     resolveTextDirection();  
  7.     if (isFocused()) {  
  8.         InputMethodManager imm = InputMethodManager.peekInstance();  
  9.         imm.focusIn(this);  
  10.     }  
  11. }  

3 fitSystemWindows(), 如果是系统window 则使用padding的值计算一下insets,并开始android.view.View.requestLayout()

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected boolean fitSystemWindows(Rect insets) {  
  2.     if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {  
  3.         mPaddingLeft = insets.left;  
  4.         mPaddingTop = insets.top;  
  5.         mPaddingRight = insets.right;  
  6.         mPaddingBottom = insets.bottom;  
  7.         requestLayout();  
  8.         return true;  
  9.     }  
  10.     return false;  
  11. }  

在android.view.View.requestLayout()中,就是简单的一层一层向上检查parent是否存在,若存在调用parent的requestLayout();

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Call this when something has changed which has invalidated the 
  3.  * layout of this view. This will schedule a layout pass of the view 
  4.  * tree. 
  5.  */  
  6. public void requestLayout() {  
  7.     if (ViewDebug.TRACE_HIERARCHY) {  
  8.         ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);  
  9.     }  
  10.   
  11.     mPrivateFlags |= FORCE_LAYOUT;  
  12.     mPrivateFlags |= INVALIDATED;  
  13.   
  14.     if (mParent != null) {  
  15.         if (mLayoutParams != null) {  
  16.             mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());  
  17.         }  
  18.         if (!mParent.isLayoutRequested()) {  
  19.             mParent.requestLayout();  
  20.         }  
  21.     }  
  22. }  
但是每层调用完成,并不是立即执行layout操作,而是通过赋值标志位mPrivateFlags |= FORCE_LAYOUT;,来标识一下而已。真正的layout过程在后面。

4. 有一段代码要说一下。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (mLayoutRequested && !mStopped) {  
  2.     // Execute enqueued actions on every layout in case a view that was detached  
  3.     // enqueued an action after being detached  
  4.     getRunQueue().executeActions(attachInfo.mHandler);  
RunQueue 是在handler 没有初始化的时候用来处理事件的消息队列。 把给ViewRootImpl post的事件 类型是runnable , 等到handler 构造好后,再发给handler 处理。不是本文的重点,就简单提一下。


5 接下来是measure 的部分

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.             boolean goodMeasure = false;  
  2.             if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {  
  3.                 // On large screens, we don't want to allow dialogs to just  
  4.                 // stretch to fill the entire width of the screen to display  
  5.                 // one line of text.  First try doing the layout at a smaller  
  6.                 // size to see if it will fit.  
  7.                 final DisplayMetrics packageMetrics = res.getDisplayMetrics();  
  8.                 res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);  
  9.                 int baseSize = 0;  
  10.                 if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {  
  11.                     baseSize = (int)mTmpValue.getDimension(packageMetrics);  
  12.                 }  
  13.                 if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);  
  14.                 if (baseSize != 0 && desiredWindowWidth > baseSize) {  
  15.                     childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);  
  16.                     childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);  
  17.                     host.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  18.                     if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("  
  19.                             + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");  
  20.                     if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {  
  21.                         goodMeasure = true;  
  22.                     } else {  
  23.                         // Didn't fit in that size... try expanding a bit.  
  24.                         baseSize = (baseSize+desiredWindowWidth)/2;  
  25.                         if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="  
  26.                                 + baseSize);  
  27.                         childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);  
  28.                         host.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  29.                         if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("  
  30.                                 + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");  
  31.                         if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {  
  32.                             if (DEBUG_DIALOG) Log.v(TAG, "Good!");  
  33.                             goodMeasure = true;  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.             if (!goodMeasure) {  
  39.                 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);  
  40.                 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);  
  41.                 host.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  42.                 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {  
  43.                     windowSizeMayChange = true;  
  44.                 }  
  45.             }  
这段代码基本就是在某些情况下 用特定的参数来measure , 另外一些情况下,用另外一些值来measure.

都是调用的host.measure(childWidthMeasureSpec, childHeightMeasureSpec); 只是情况不同,使用的参数不同。

计算值的这部分参见前文《 源码分析:LayoutParams的wrap_content, match_parent, 和具体值

然后就是调用measure()方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  
  2.     if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||  
  3.             widthMeasureSpec != mOldWidthMeasureSpec ||  
  4.             heightMeasureSpec != mOldHeightMeasureSpec) {  
  5.   
  6.         // first clears the measured dimension flag  
  7.         mPrivateFlags &= ~MEASURED_DIMENSION_SET;  
  8.   
  9.         if (ViewDebug.TRACE_HIERARCHY) {  
  10.             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);  
  11.         }  
  12.   
  13.         // measure ourselves, this should set the measured dimension flag back  
  14.         onMeasure(widthMeasureSpec, heightMeasureSpec);  
  15.   
  16.         // flag not set, setMeasuredDimension() was not invoked, we raise  
  17.         // an exception to warn the developer  
  18.         if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {  
  19.             throw new IllegalStateException("onMeasure() did not set the"  
  20.                     + " measured dimension by calling"  
  21.                     + " setMeasuredDimension()");  
  22.         }  
  23.   
  24.         mPrivateFlags |= LAYOUT_REQUIRED;  
  25.     }  
  26.   
  27.     mOldWidthMeasureSpec = widthMeasureSpec;  
  28.     mOldHeightMeasureSpec = heightMeasureSpec;  
  29. }  
在一些判断和标志位mPrivateFlags 赋值后,调用onMeasure() 方法。 onMeasure() 的讨论也请移步前文《  源码分析:LayoutParams的wrap_content, match_parent, 和具体值

在方法的最后给标志位置位 
mPrivateFlags |= LAYOUT_REQUIRED;

6 接着一大堆复杂的逻辑和赋值之后,调用了relayoutWindow() 这个还不太明白是怎么回事 // TODO 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);  

7 接着又是一堆逻辑和赋值,并在某种情况下还调用了host.measure()。算是measure 完成了

8. 然后是layout 的部分。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. final boolean didLayout = mLayoutRequested && !mStopped;  
  2. boolean triggerGlobalLayoutListener = didLayout  
  3.         || attachInfo.mRecomputeGlobalAttributes;  
  4. if (didLayout) {  
  5.     mLayoutRequested = false;  
  6.     host.layout(00, host.getMeasuredWidth(), host.getMeasuredHeight());  
在layout() 中,先setFrame,然后判断状态标志位,进而回调onLayout(); 也就是自定义的部分。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void layout(int l, int t, int r, int b) {  
  2.     int oldL = mLeft;  
  3.     int oldT = mTop;  
  4.     int oldB = mBottom;  
  5.     int oldR = mRight;  
  6.     boolean changed = setFrame(l, t, r, b);  
  7.     if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {  
  8.         if (ViewDebug.TRACE_HIERARCHY) {  
  9.             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);  
  10.         }  
  11.   
  12.         onLayout(changed, l, t, r, b);  
  13.         mPrivateFlags &= ~LAYOUT_REQUIRED;  
  14.   
  15.         if (mOnLayoutChangeListeners != null) {  
  16.             ArrayList<OnLayoutChangeListener> listenersCopy =  
  17.                     (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();  
  18.             int numListeners = listenersCopy.size();  
  19.             for (int i = 0; i < numListeners; ++i) {  
  20.                 listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);  
  21.             }  
  22.         }  
  23.     }  
  24.     mPrivateFlags &= ~FORCE_LAYOUT;  
  25. }  

在setFrame()中,判断新的位置和旧的位置是否一致。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected boolean setFrame(int left, int top, int right, int bottom) {  
  2.     boolean changed = false;  
  3.   
  4.     if (DBG) {  
  5.         Log.d("View"this + " View.setFrame(" + left + "," + top + ","  
  6.                 + right + "," + bottom + ")");  
  7.     }  
  8.   
  9.     if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {  
  10.         changed = true;  
  11.   
  12.         // Remember our drawn bit  
  13.         int drawn = mPrivateFlags & DRAWN;  
  14.   
  15.         int oldWidth = mRight - mLeft;  
  16.         int oldHeight = mBottom - mTop;  
  17.         int newWidth = right - left;  
  18.         int newHeight = bottom - top;  
  19.         boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);  
  20.   
  21.         // Invalidate our old position  
  22.         invalidate(sizeChanged);  
  23.   
  24.         mLeft = left;  
  25.         mTop = top;  
  26.         mRight = right;  
  27.         mBottom = bottom;  
  28.   
  29.         mPrivateFlags |= HAS_BOUNDS;  
  30.   
  31.   
  32.         if (sizeChanged) {  
  33.             if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {  
  34.                 // A change in dimension means an auto-centered pivot point changes, too  
  35.                 if (mTransformationInfo != null) {  
  36.                     mTransformationInfo.mMatrixDirty = true;  
  37.                 }  
  38.             }  
  39.             onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);  
  40.         }  
  41.   
  42.         if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {  
  43.             // If we are visible, force the DRAWN bit to on so that  
  44.             // this invalidate will go through (at least to our parent).  
  45.             // This is because someone may have invalidated this view  
  46.             // before this call to setFrame came in, thereby clearing  
  47.             // the DRAWN bit.  
  48.             mPrivateFlags |= DRAWN;  
  49.             invalidate(sizeChanged);  
  50.             // parent display list may need to be recreated based on a change in the bounds  
  51.             // of any child  
  52.             invalidateParentCaches();  
  53.         }  
  54.   
  55.         // Reset drawn bit to original value (invalidate turns it off)  
  56.         mPrivateFlags |= drawn;  
  57.   
  58.         mBackgroundSizeChanged = true;  
  59.     }  
  60.     return changed;  
  61. }  
如果不一致,则调用invalidate();
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.     void invalidate(boolean invalidateCache) {  
  2. <span style="white-space:pre">        </span>if (p != null && ai != null) {  
  3.                 final Rect r = ai.mTmpInvalRect;  
  4.                 r.set(00, mRight - mLeft, mBottom - mTop);  
  5.                 // Don't call invalidate -- we don't want to internally scroll  
  6.                 // our own bounds  
  7.                 p.invalidateChild(this, r);  
  8.             }  
  9.         }  
调用父控件的p.invalidateChild() 来计算并标识 dirty 区域, 区域范围就是子view 所处的区域。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void invalidateChild(View child, Rect dirty) {  
  2.     checkThread();  
  3.     if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);  
  4.     if (dirty == null) {  
  5.         // Fast invalidation for GL-enabled applications; GL must redraw everything  
  6.         invalidate();  
  7.         return;  
  8.     }  
  9.     if (mCurScrollY != 0 || mTranslator != null) {  
  10.         mTempRect.set(dirty);  
  11.         dirty = mTempRect;  
  12.         if (mCurScrollY != 0) {  
  13.            dirty.offset(0, -mCurScrollY);  
  14.         }  
  15.         if (mTranslator != null) {  
  16.             mTranslator.translateRectInAppWindowToScreen(dirty);  
  17.         }  
  18.         if (mAttachInfo.mScalingRequired) {  
  19.             dirty.inset(-1, -1);  
  20.         }  
  21.     }  
  22.     if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {  
  23.         mAttachInfo.mSetIgnoreDirtyState = true;  
  24.         mAttachInfo.mIgnoreDirtyState = true;  
  25.     }  
  26.     mDirty.union(dirty);  
  27.     if (!mWillDrawSoon) {  
  28.         scheduleTraversals();  
  29.     }  
  30. }  
如果setframe 的返回值 为true ,则表示 区域的内容发生了变化进而回调onLayout() 方法,和mOnLayoutChangeListeners的onLayoutChange()。


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Called from layout when this view should 
  3.  * assign a size and position to each of its children. 
  4.  * 
  5.  * Derived classes with children should override 
  6.  * this method and call layout on each of 
  7.  * their children. 
  8.  * @param changed This is a new size or position for this view 
  9.  * @param left Left position, relative to parent 
  10.  * @param top Top position, relative to parent 
  11.  * @param right Right position, relative to parent 
  12.  * @param bottom Bottom position, relative to parent 
  13.  */  
  14. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  15. }  
在onLayout() 中, view应该通过调用每一个子view 的layout() 方法,来指定每一个子view 的大小和位置,。


接着有个computesInternalInsets的部分不太懂,sWindowSession.setInsets // TODO

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (computesInternalInsets) {  
  2.     // Clear the original insets.  
  3.     final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;  
  4.     insets.reset();  
  5.   
  6.     // Compute new insets in place.  
  7.     attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);  
  8.   
  9.         try {  
  10.             sWindowSession.setInsets(mWindow, insets.mTouchableInsets,  
  11.                     contentInsets, visibleInsets, touchableRegion);  
  12.         } catch (RemoteException e) {  
  13.         }  
  14.     }  
  15. }  

10, 略去一部分处理过程,有焦点的回调和处理, 输入法的处理等。

11. 进入draw 的部分。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if (!cancelDraw && !newSurface) {  
  2.     if (mPendingTransitions != null && mPendingTransitions.size() > 0) {  
  3.         for (int i = 0; i < mPendingTransitions.size(); ++i) {  
  4.             mPendingTransitions.get(i).startChangingAnimations();  
  5.         }  
  6.         mPendingTransitions.clear();  
  7.     }  
  8.     mFullRedrawNeeded = false;  
  9.   
  10.     final long drawStartTime;  
  11.     if (ViewDebug.DEBUG_LATENCY) {  
  12.         drawStartTime = System.nanoTime();  
  13.     }  
  14.   
  15.     draw(fullRedrawNeeded);  
  16.   
  17.     if (ViewDebug.DEBUG_LATENCY) {  
  18.         mLastDrawDurationNanos = System.nanoTime() - drawStartTime;  
  19.     }  
  20.   
  21.     if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0  
  22.             || mReportNextDraw) {  
  23.         if (LOCAL_LOGV) {  
  24.             Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());  
  25.         }  
  26.         mReportNextDraw = false;  
  27.         if (mSurfaceHolder != null && mSurface.isValid()) {  
  28.             mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);  
  29.             SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();  
  30.             if (callbacks != null) {  
  31.                 for (SurfaceHolder.Callback c : callbacks) {  
  32.                     if (c instanceof SurfaceHolder.Callback2) {  
  33.                         ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(  
  34.                                 mSurfaceHolder);  
  35.                     }  
  36.                 }  
  37.             }  
  38.         }  
  39.         try {  
  40.             sWindowSession.finishDrawing(mWindow);  
  41.         } catch (RemoteException e) {  
  42.         }  
  43.     }  

在draw() 方法中,计算了dirty 的区域, 如果使用了硬件加速的话,进行相应的处理,否则使用canvas  来绘制view。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">    private void draw(boolean fullRedrawNeeded) {</span>  
  2. <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">                        mView.draw(canvas);</span>  
在android.view.View.draw(Canvas)中,绘制所有的子类

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void draw(Canvas canvas) {  
  2.     if (ViewDebug.TRACE_HIERARCHY) {  
  3.         ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);  
  4.     }  
  5.   
  6.     final int privateFlags = mPrivateFlags;  
  7.     final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&  
  8.             (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
  9.     mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;  
  10.   
  11.     /* 
  12.      * Draw traversal performs several drawing steps which must be executed 
  13.      * in the appropriate order: 
  14.      * 
  15.      *      1. Draw the background 
  16.      *      2. If necessary, save the canvas' layers to prepare for fading 
  17.      *      3. Draw view's content 
  18.      *      4. Draw children 
  19.      *      5. If necessary, draw the fading edges and restore layers 
  20.      *      6. Draw decorations (scrollbars for instance) 
  21.      */  
  22.   
  23.     // Step 1, draw the background, if needed  
  24.     int saveCount;  
  25.   
  26.     if (!dirtyOpaque) {  
  27.         final Drawable background = mBGDrawable;  
  28.         if (background != null) {  
  29.             final int scrollX = mScrollX;  
  30.             final int scrollY = mScrollY;  
  31.   
  32.             if (mBackgroundSizeChanged) {  
  33.                 background.setBounds(00,  mRight - mLeft, mBottom - mTop);  
  34.                 mBackgroundSizeChanged = false;  
  35.             }  
  36.   
  37.             if ((scrollX | scrollY) == 0) {  
  38.                 background.draw(canvas);  
  39.             } else {  
  40.                 canvas.translate(scrollX, scrollY);  
  41.                 background.draw(canvas);  
  42.                 canvas.translate(-scrollX, -scrollY);  
  43.             }  
  44.         }  
  45.     }  
  46.   
  47.     // skip step 2 & 5 if possible (common case)  
  48.     final int viewFlags = mViewFlags;  
  49.     boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;  
  50.     boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;  
  51.     if (!verticalEdges && !horizontalEdges) {  
  52.         // Step 3, draw the content  
  53.         if (!dirtyOpaque) onDraw(canvas);  
  54.   
  55.         // Step 4, draw the children  
  56.         dispatchDraw(canvas);  
  57.   
  58.         // Step 6, draw decorations (scrollbars)  
  59.         onDrawScrollBars(canvas);  
  60.   
  61.         // we're done...  
  62.         return;  
  63.     }  

注释写的很明白, 在step 1 中绘制背景, 在step 3 中调用自己的ondraw(); 在step 4 中 调用dispatchDraw() 绘制子view 。
最后调用sWindowSession.finishDrawing() 估计是通知底层完成吧,不太懂。//TODO

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. try {  
  2.     sWindowSession.finishDrawing(mWindow);  
  3. catch (RemoteException e) {  
  4. }  

draw的部分到此完成


至此,一次完整的绘制流程就走完了,剩下的就是一遍遍的重复这个过程啦。回调过程参见前文《深入分析UI 上层事件处理核心机制 Choreographer


from:http://blog.csdn.net/farmer_cc/article/details/31454803
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值