从前文《源代码解析:dialog, popupwindow, 和activity 的第一个view是怎么来的?》中知道了activity第一个view或者说根view或者说mDecorView 事实上就是一个FrameLayout,以及是在系统handleResume的时候增加到系统windowManager中的,并由framework中的ViewRootImpl
接管,通过ViewRootImpl.setView() 開始整个显示过程的。

这次着重梳理一下view的显示过程(onAttach, onMeasure, onLayout, onDraw )在源代码中的过程。

从ViewRootImpl.setview 開始。

  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();

  1. public void requestLayout() {
  2. checkThread();
  3. mLayoutRequested = true;
  4. scheduleTraversals();
  5. }

进而scheduleTraversals();

  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() 中

  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. 函数刚開始的部分,初始化了一些后面会用到的变量和标志位。

  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);

  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);

  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。

  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() {
  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()

  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();

  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. 有一段代码要说一下。

  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 的部分

  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()方法

  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

  1. relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);

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

8. 然后是layout 的部分。

  1. final boolean didLayout = mLayoutRequested && !mStopped;
  2. boolean triggerGlobalLayoutListener = didLayout
  3. || attachInfo.mRecomputeGlobalAttributes;
  4. if (didLayout) {
  5. mLayoutRequested = false;
  6. host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

在layout() 中,先setFrame,然后推断状态标志位,进而回调onLayout(); 也就是自己定义的部分。

  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()中,推断新的位置和旧的位置是否一致。

  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. if (sizeChanged) {
  32. if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
  33. // A change in dimension means an auto-centered pivot point changes, too
  34. if (mTransformationInfo != null) {
  35. mTransformationInfo.mMatrixDirty = true;
  36. }
  37. }
  38. onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
  39. }
  40.  
  41. if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
  42. // If we are visible, force the DRAWN bit to on so that
  43. // this invalidate will go through (at least to our parent).
  44. // This is because someone may have invalidated this view
  45. // before this call to setFrame came in, thereby clearing
  46. // the DRAWN bit.
  47. mPrivateFlags |= DRAWN;
  48. invalidate(sizeChanged);
  49. // parent display list may need to be recreated based on a change in the bounds
  50. // of any child
  51. invalidateParentCaches();
  52. }
  53.  
  54. // Reset drawn bit to original value (invalidate turns it off)
  55. mPrivateFlags |= drawn;
  56.  
  57. mBackgroundSizeChanged = true;
  58. }
  59. return changed;
  60. }

假设不一致,则调用invalidate();

  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(0, 0, 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 所处的区域。

  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()。

  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 的大小和位置,。

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

  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 的部分。

  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。

  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)中。绘制全部的子类

  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(0, 0, 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

  1. try {
  2. sWindowSession.finishDrawing(mWindow);
  3. } catch (RemoteException e) {
  4. }

draw的部分到此完毕

http://u.cncn.com/space-323-do-blog-id-377295.html

http://u.cncn.com/space-323-do-blog-id-377142.html

http://u.cncn.com/space-mtag-tagid-782.html

http://u.cncn.com/space-mtag-tagid-783.html

至此,一次完整的绘制流程就走完了,剩下的就是一遍遍的反复这个过程啦。

回调过程參见前文《深入分析UI 上层事件处理核心机制 Choreographer

尾巴

尾巴。接下来过一下事件的流程吧。

源代码分析:onAttach, onMeasure, onLayout, onDraw 的顺序。的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  3. android 自定义view android onmeasure onlayot ondraw

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha android onmeasure onlayot ondraw 顺序 ====== 1 ...

  4. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  5. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  6. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  7. Raid1源代码分析--开篇总述

    前段时间由于一些事情耽搁了,最近将raid1方面的各流程整理了一遍.网上和书上,能找到关于MD下的raid1的文档资料比较少.决定开始写一个系列的关于raid1的博客,之前写过的一篇读流程也会在之后加 ...

  8. Android 消息处理源代码分析(1)

    Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...

  9. Parrot源代码分析之海贼王

    我们的目的是找到speedup-example在使用Parrot加速的原因,假设仅仅说它源于Context Switch的降低,有点简单了,它究竟为什么降低了?除了Context Switch外是否还 ...

随机推荐

  1. 让指定JS出现智能提示

    大家都知道,在Asp.net MVC中,引入了CSS,JS捆绑优化这个好东东.但是,你会发现,运用了这个捆绑优化后,或者引用了模板后,在模板中引用了JS的文件后,在页面中编辑JS文件后,却不能利索地出 ...

  2. js-获取用户移动端网络类型:wifi、4g、3g、2g...

    今天工作时间很宽裕, 忽然想起,自己做过的所有页面中,有些页面经常会面临用户在网络状态很差的时候打开页面,页面是挂了的状态,感觉很LOW~. 所以我决定在今后的页面中我需要先判断用户的网络状态, 若是 ...

  3. HDU 5732 Subway(2016多校1J,树的重心 + 哈希)

    题目链接  2016多校1 Problem J 题意  给定两棵相同的树,但是编号方案不同.求第一棵树上的每个点对应的第二棵树上的点.输出一种方案即可. 首先确定树的直径的中点.两棵树相等意味着两棵树 ...

  4. XML签名

    英文:https://www.javacodegeeks.com/2013/10/xml-security-with-digital-signature-in-java.html 中文:http:// ...

  5. Reverse Words in a String II -- LeetCode

    Given an input string, reverse the string word by word. A word is defined as a sequence of non-space ...

  6. POJ 3368 Frequent values(线段树区间合并)

    [题目链接] http://poj.org/problem?id=3368 [题目大意] 有一个有序序列,要求区间查询出现次数最多的数 [题解] 维护每个区间左端点和右端点,以及左右的长度,还有区间的 ...

  7. 可持久化线段树(主席树)(图文并茂详解)【poj2104】【区间第k大】

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=63740442 向大(hei)佬(e)实力学(di ...

  8. Oracle、SQLServer、ArcSDE怎么查看版本、补丁

    http://blog.csdn.net/linghe301/article/details/6712544

  9. linux下打包,压缩,解压缩

    Linux下最常用的打包程序就是tar了,使用tar程序打出来的包我们常称为tar包,tar包文件的命令通常都是以.tar结尾的.生成tar包后,就可以用其它的程序来进 行压缩了,所以首先就来讲讲ta ...

  10. Go -- 升级go版本

    先卸载go的旧版本, 参考卸载go; 然后用brew安装, 如果之前用brew安装的go, 可直接brew update go, 否则, 安装go新版: 执行一下 export PATH=$PATH: ...