从源码分析View绘制流程

  • 先看一张view绘制流程的时序图
  • Activity加载显示基本流程
  • 研究入口:Activity.setContentView(layoutId);

  
  
  1.    public void setContentView(@LayoutRes int layoutResID) {
  2.        getWindow().setContentView(layoutResID);
  3.        initWindowDecorActionBar();
  4.    }
  • 发现调用了getWindow()方法,该方法获取的Window对象实例,但是Window是抽象类,其真正的实现类是PhoneWindow.

  
  
  1.    @Override
  2.    public void setContentView(int layoutResID) {
  3.        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
  4.        // decor, when theme attributes and the like are crystalized. Do not check the feature
  5.        // before this happens.
  6.        if (mContentParent == null) {
  7.            installDecor();
  8.        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  9.            mContentParent.removeAllViews();
  10.        }
  11.        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
  12.            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
  13.                    getContext());
  14.            transitionTo(newScene);
  15.        } else {
  16.            mLayoutInflater.inflate(layoutResID, mContentParent);
  17.        }
  18.        mContentParent.requestApplyInsets();
  19.        final Callback cb = getCallback();
  20.        if (cb != null && !isDestroyed()) {
  21.            cb.onContentChanged();
  22.        }
  23.    }
  • 1.初次进入会进入代码6-7行,查看方法installDecor(), 这个方法非常核心

  
  
  1.    private void installDecor() {
  2.        if (mDecor == null) {
  3.           mDecor = generateDecor();
  4.            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
  5.            mDecor.setIsRootNamespace(true);
  6.            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
  7.                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
  8.            }
  9.        }
  10.        if (mContentParent == null) {
  11.           mContentParent = generateLayout(mDecor);
  12.      
  13.      ....
  • 代码4行,generateDecor()->生成DecorView,界面根布局:DecorView是PhoneWindow的内部类,继承自FrameLayout.

  
  
  1.    protected DecorView generateDecor() {
  2.        return new DecorView(getContext(), -1);
  3.    }
  • 代码14行,在decorview界面上,添加界面

  
  
  1.    protected ViewGroup generateLayout(DecorView decor) {
  2.        ........//actionbar的一些设置
  3.              // System.out.println("Title!");
  4.        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
  5.            layoutResource = R.layout.screen_simple_overlay_action_mode;
  6.        } else {
  7.            // Embedded, so no decoration is needed.
  8.            layoutResource = R.layout.screen_simple;
  9.            // System.out.println("Simple!");
  10.        }
  11.        mDecor.startChanging();
  12.        View in = mLayoutInflater.inflate(layoutResource, null);
  13.       decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
  14.        mContentRoot = (ViewGroup) in;
  15.        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
  16.        ........//其他的一些操作       mDecor.finishChanging();
  17.        return contentParent;

      如果ActionBar没有特殊要求,layoutResource一般会调用系统的xml布局文件R.layout.screen_simple.在这个布局中就有一个id为content的FrameLayout的控件,就是generateLayout(decoreview)方法返回得到的mContentParent.

   
   
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:fitsSystemWindows="true"
  5. android:orientation="vertical">
  6. <ViewStub android:id="@+id/action_mode_bar_stub"
  7. android:inflatedId="@+id/action_mode_bar"
  8. android:layout="@layout/action_mode_bar"
  9. android:layout_width="match_parent"
  10. android:layout_height="wrap_content" />
  11. <FrameLayout
  12. android:id="@android:id/content"
  13. android:layout_width="match_parent"
  14. android:layout_height="match_parent"
  15. android:foregroundInsidePadding="false"
  16. android:foregroundGravity="fill_horizontal|top"
  17. android:foreground="?android:attr/windowContentOverlay" />
  18. </LinearLayout>

  • setContentView(layoutId)方法第17行代码,是将我们传入的xml布局文件添加到mContentParent中。

  • 而view绘制入口则是mDecor.finishChanging()->这个方法里面最终会调用requestLayout()方法

  
  
  1.   public void finishChanging() {
  2.            mChanging = false;
  3.            drawableChanged();
  4.        }
  
  
  1.        private void drawableChanged() {
  2.            if (mChanging) {
  3.                return;
  4.            }
  5.            setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top
  6.                    + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right,
  7.                    mFramePadding.bottom + mBackgroundPadding.bottom);
  8.            requestLayout();
  9.            invalidate();
  • 因为DecorView继承自 FrameLayout->最后会调用View方法的requestLayout()方法

  
  
  1.    @CallSuper
  2.    public void requestLayout() {
  3.        if (mMeasureCache != null) mMeasureCache.clear();
  4.        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
  5.            // Only trigger request-during-layout logic if this is the view requesting it,
  6.            // not the views in its parent hierarchy
  7.            ViewRootImpl viewRoot = getViewRootImpl();
  8.            if (viewRoot != null && viewRoot.isInLayout()) {
  9.                if (!viewRoot.requestLayoutDuringLayout(this)) {
  10.                    return;
  11.                }
  12.            }
  13.            mAttachInfo.mViewRequestingLayout = this;
  14.        }
  15.        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
  16.        mPrivateFlags |= PFLAG_INVALIDATED;
  17.        if (mParent != null && !mParent.isLayoutRequested()) {
  18.            mParent.requestLayout();
  19.        }
  20.        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
  21.            mAttachInfo.mViewRequestingLayout = null;
  22.        }
  23.    }
  • 在requestlayout()方法中,会到ViewRootImpl这个类中去进行绘制处理

  
  
  1.    final class TraversalRunnable implements Runnable {
  2.        @Override
  3.        public void run() {
  4.            doTraversal();
  5.        }
  6.    }
  
  
  1.    void doTraversal() {
  2.        if (mTraversalScheduled) {
  3.            mTraversalScheduled = false;
  4.            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
  5.            if (mProfile) {
  6.                Debug.startMethodTracing("ViewAncestor");
  7.            }
  8.            performTraversals();
  9.            if (mProfile) {
  10.                Debug.stopMethodTracing();
  11.                mProfile = false;
  12.            }
  13.        }
  14.    }
  • 最终在performTraversals()方法中会调用测量meaure,摆放layout,绘制draw方法

  
  
  1. private void performTraversals() {
  2.        // Ask host how big it wants to be
  3.         performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  4.         performLayout(lp, desiredWindowWidth, desiredWindowHeight);
  5.         performDraw();
  • performMeasure()测量方法->最终调用的是view的measure方法->onMeasure()

  
  
  1.    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
  2.        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
  3.        try {
  4.            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  5.        } finally {
  6.            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  7.        }
  8.    }
    
    
  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2. boolean optical = isLayoutModeOptical(this);
  3. if (optical != isLayoutModeOptical(mParent)) {
  4. Insets insets = getOpticalInsets();
  5. int oWidth = insets.left + insets.right;
  6. int oHeight = insets.top + insets.bottom;
  7. widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
  8. heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
  9. }
  10. // Suppress sign extension for the low bytes
  11. long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
  12. if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
  13. if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
  14. widthMeasureSpec != mOldWidthMeasureSpec ||
  15. heightMeasureSpec != mOldHeightMeasureSpec) {
  16. // first clears the measured dimension flag
  17. mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
  18. resolveRtlPropertiesIfNeeded();
  19. int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
  20. mMeasureCache.indexOfKey(key);
  21. if (cacheIndex < 0 || sIgnoreMeasureCache) {
  22. // measure ourselves, this should set the measured dimension flag back
  23. onMeasure(widthMeasureSpec, heightMeasureSpec);
  24. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  25. } else {
  26. long value = mMeasureCache.valueAt(cacheIndex);
  27. // Casting a long to int drops the high 32 bits, no mask needed
  28. setMeasuredDimensionRaw((int) (value >> 32), (int) value);
  29. mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  30. }
  31. // flag not set, setMeasuredDimension() was not invoked, we raise
  32. // an exception to warn the developer
  33. if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
  34. throw new IllegalStateException("View with id " + getId() + ": "
  35. + getClass().getName() + "#onMeasure() did not set the"
  36. + " measured dimension by calling"
  37. + " setMeasuredDimension()");
  38. }
  39. mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
  40. }
  41. mOldWidthMeasureSpec = widthMeasureSpec;
  42. mOldHeightMeasureSpec = heightMeasureSpec;
  43. mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
  44. (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
  45. }
     
     
  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  4. }
      
      
  1. protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
  2. boolean optical = isLayoutModeOptical(this);
  3. if (optical != isLayoutModeOptical(mParent)) {
  4. Insets insets = getOpticalInsets();
  5. int opticalWidth = insets.left + insets.right;
  6. int opticalHeight = insets.top + insets.bottom;
  7. measuredWidth += optical ? opticalWidth : -opticalWidth;
  8. measuredHeight += optical ? opticalHeight : -opticalHeight;
  9. }
  10. setMeasuredDimensionRaw(measuredWidth, measuredHeight);
  11. }
  • performLayout()控件摆放->最后调用的是 view的layout方法

  
  
  1.    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
  2.            int desiredWindowHeight) {
  3.        mLayoutRequested = false;
  4.        mScrollMayChange = true;
  5.        mInLayout = true;
  6.        final View host = mView;
  7.        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
  8.            Log.v(TAG, "Laying out " + host + " to (" +
  9.                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
  10.        }
  11.        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
  12.        try {
  13.            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
  • performDraw()绘制方法->绘制分发

   
   
  1. private void performDraw() {
  2.        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
  3.            return;
  4.        }
  5.        final boolean fullRedrawNeeded = mFullRedrawNeeded;
  6.        mFullRedrawNeeded = false;
  7.        mIsDrawing = true;
  8.        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
  9.        try {
  10.            draw(fullRedrawNeeded);
  11.        } finally {
  12.            mIsDrawing = false;
  13.            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
  14.        }
  15.        // For whatever reason we didn't create a HardwareRenderer, end any
  16.        // hardware animations that are now dangling
  17.        if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
  18.            final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
  19.            for (int i = 0; i < count; i++) {
  20.                mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
  21.            }
  22.            mAttachInfo.mPendingAnimatingRenderNodes.clear();
  23.        }
  24.        if (mReportNextDraw) {
  25.            mReportNextDraw = false;
  26.            if (mAttachInfo.mHardwareRenderer != null) {
  27.                mAttachInfo.mHardwareRenderer.fence();
  28.            }
  29.            if (LOCAL_LOGV) {
  30.                Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
  31.            }
  32.            if (mSurfaceHolder != null && mSurface.isValid()) {
  33.                mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
  34.                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
  35.                if (callbacks != null) {
  36.                    for (SurfaceHolder.Callback c : callbacks) {
  37.                        if (c instanceof SurfaceHolder.Callback2) {
  38.                            ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
  39.                                    mSurfaceHolder);
  40.                        }
  41.                    }
  42.                }
  43.            }
  44.            try {
  45.                mWindowSession.finishDrawing(mWindow);
  46.            } catch (RemoteException e) {
  47.            }
  48.        }
  49.    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值