Android View的绘制流程

View的绘制流程

源码分析:

1.绘制入口

ActivityThread.handleResumeActivity
    -->WindowManagerImpl.addView(decorView,layoutParams)
    -->WindowManagerGlobal.addView()

2.绘制类以及方法

ViewRootImpl.setView(decorView,layoutParams,parentView)
    -->ViewRootImpl.requestLayout()
    -->scheduleTraversals()
    -->doTraversal()
    -->performTraversals()

3.绘制的三大步骤

测量:ViewRootImpl.performMeasure
布局: ViewRootImpl.performLayout
绘制:ViewRootImpl.performDraw

绘制入口

frameworks\base\core\java\android\app\ActivityThread.java
public final class ActivityThread {
    ...
     private class H extends Handler {
     ...
      public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    ...
                } break;
         ...
     }
}

深入handleLaunchActivity

  private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ...
        //1.
        Activity a = performLaunchActivity(r, customIntent);
        
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
           //2. handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            }
        }

总结:

  1. 起动一个Activity
  2. handleResumeActivity处理Activity

深入handleResumeActivity

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ...
        2.1 回调activity onResume方法
        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);
        ...
        
        if (r.window == null && !a.mFinished && willBeVisible) {
                   //2.2 获取windowManager以及decor
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
             
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                ...
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                     //2.3添加DecorView中去
                      wm.addView(decor, l);
                    } else {
               ...
                    }
                }
            
        }

解析
  • 回调activity onResume方法
  • 获取windowManager以及decor
  • 添加DecorView 以及WindowManager.LayoutParams到ViewManager
深入2.2 windowmanager是什么?

进入

//frameworks\base\core\java\android\app\ActivityThread.java

 ViewManager wm = a.getWindowManager();

进入getWindowManager()

// frameworks\base\core\java\android\app\Activity.java
    /** Retrieve the window manager for showing custom windows. */
    public WindowManager getWindowManager() {
        return mWindowManager;
    }
    private Window mWindow;
    private WindowManager mWindowManager;
    ...
    mWindowManager = mWindow.getWindowManager();

PhoneWindow是window的实现类
进入phonewindow

//frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java
    /**
     * Return the window manager allowing this Window to display its own
     * windows.
     *
     * @return WindowManager The ViewManager.
     */
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

子类没有实现。说明在父类实现。在window中找。

/frameworks\base\core\java\android\view\Window.java
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

WindowManagerImpl 实现的接口就是ViewManager的子类WindowManager。

public final class WindowManagerImpl implements WindowManager {...}
public interface WindowManager extends ViewManager {}

WindowManagerImpl中就调用了addview()方法。

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

绘制类以及方法

深入2.3 addview

进入WindowManagerGlobal如何addview

private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
            ...
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
       ...

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                //2.3.1
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
               ...
            }
        }
    }

总结:
将root.setView()将view,wparams,panelParentView 进行关联起来

深入2.3.1
// \android8.0\frameworks\base\core\java\android\view\ViewRootImpl.java


    /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            ...
              requestLayout();
              ...
            
        }

进入

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            2.3.1.1.执行方法
            scheduleTraversals();
        }
    }

进入 2.3.1.1.执行方法

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
        final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
                 //2.3.1.1.1
            doTraversal();
        }
    }
    ...
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
                ...
       mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
          ...
        }
    }

调用一个方法并且创建了线程
进入2.3.1.1.1查看doTraversal()方法

    void doTraversal() {
        if (mTraversalScheduled) {
            ...
            performTraversals();
            ...
        }
    }

进入发现绘制的三大流程

private void performTraversals()
{ .
..
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
}

在这里插入图片描述

上半部分流程已经走完了

绘制的三大步骤

private void performTraversals()
{ .
..
//1 
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//2
performLayout(lp, mWidth, mHeight);
...
//3
performDraw();
}

onMeasure的理解

进入1

//\android8.0\frameworks\base\core\java\android\view\ViewRootImpl.java
    View mView;
    ...
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
           //1.1
           mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

进入1.1 View中的measure

   public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ..
        if (forceLayout || needsLayout) {
            // first clears the measured dimension flag
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded();

            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
               //1.1.1 onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
               ...
            }
            
        }
            ...
                 mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;

        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    }

总结:

  • 取缓存
  • 调用onMesure()方法
  • 缓存没读到进行重置

进入1.1.1 onMeasure方法中

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        1.1.1.1
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

进入1.1.1.1 setMeasuredDimension方法

    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;
        }
        //1.1.1.1.1
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }

进入1.1.1.1.1 setMeasuredDimensionRaw()

    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }

对这

        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

两个成员变量进行赋值。

onMeasure :测量宽高

宽高的理解
onMeasure(widthMeasureSpec, heightMeasureSpec);
View = 模式(前两位) + 尺寸(后30位) ->  MeasureSpec 32为int值

其中

模式:SpecMode
尺寸: SpecSize

00 000000000000000000000000000000
MeasureSpec 理解
mode+size -> MeasureSpec
android8.0\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;

        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        public static final int AT_MOST     = 2 << MODE_SHIFT;
        
        
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,          @MeasureSpecMode int mode) {
                //是否是旧模式
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                //主要看这个
                //size & ~MODE_MASK 将前两位值取消
                //返回的意思是size的后30位+ mode的前两位
                    return (size & ~MODE_MASK) | (mode & MODE_MASK);
                }
        }

        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }

        //取前两位
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

        //直接取后30位
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }

    }
MeasureSpec中的成员定义
  • public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    • 00000000 00000000 00000000 00000000
    • 表示 :父容器不对View做任何限制,系统内部使用
  • public static final int EXACTLY = 1 << MODE_SHIFT;
    • 01000000 00000000 00000000 00000000
    • 精确模式
    • 表示:父容器检测出View的大小,View的最终大小就是 SpecSize
    • 对应的布局
      属性是:LayoutPamras.match_parent 或者是 固定大小
  • public static final int AT_MOST = 2 << MODE_SHIFT;
    • 10000000 00000000
      00000000 00000000
    • 最大模式
    • 表示父容器会指定一个可用大小,View的大小不能超过这个值
    • 对应的布局属性是:LayoutPamras.wrap_content
MeasureSpec中的方法
  • makeMeasureSpec(int size, int mode)
    • d
  • makeSafeMeasureSpec
  • getMode 后30位
  • getSize 前2位
DecorView的测量 MeasureSpec 的规则

performTraversals()方法中:

android8.0\frameworks\base\core\java\android\view\ViewRootImpl.java
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
            int mWidth;
            int mHeight;
            ...
            final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
            
            private void performTraversals() {
            WindowManager.LayoutParams lp = mWindowAttributes;
            ...
            int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
            int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
            ...
            // Ask host how big it wants to be
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
 

DecorView的performMeasure()是由

childWidthMeasureSpec
childHeightMeasureSpec

两个属性决定的。
查看getRootMeasureSpec()方法

    private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }

DecorView的测量总结:

MeasureSpec是由窗口的大小和自身的LayoutParams所决定的遵守如下规则:

  • 如果DecorView的LayoutParams是:LayoutParams.MATCH_PARENT。精确模式,它的尺寸是窗口大
    小。
  • 如果DecorView的LayoutParams是LayoutParams.WARP_CONTENT。最大模式,最大为窗口大小
  • 如果DecorView的LayoutParams给的是固定大小,精确模式,大小为LayoutParams的大小。
View的测量

深入
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

进入到了View的measure

   public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        if (forceLayout || needsLayout) {
            ...

            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
             //再次调用onMeasure方法 onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                ...
            }
            
        }
    }

DecorView继承FrameLayout。查看其中的onMeasure方法

android8.0\frameworks\base\core\java\android\widget\FrameLayout.java

    @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) {
               //1. 
               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);
                    }
                }
            }
        }
        ...
          setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

进入 //1.

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

进入getChildMeasureSpec()

 //spec 是指父控件的MeasureSpec
 //padding 是指当前父容器已经使用的空间
//childDimension是指子控件的布局参数对应的尺寸

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }


View的测量总结:
View的测量- 确定View的MeaSureSpec

View的MeasureSpec的获取是由父容器MeasureSpec和自身的LayoutParams决定的。

在这里插入图片描述

总结
  • 对于ViewGroup的测量

    • measure->onMeasure(测量子控件宽高)->setMeasureDimension->setMeasureDimensionRaw(保存自己的宽高)
  • 对于view的测量

    • measure->onMeasure(不需要)->setMeasureDimension->setMeasureDimensionRaw(保存自己的宽高)
  • 区别:
    子控件不需要测量子控件的宽高。通过onMeasure中的一系列方法获得自身的宽高

  • 注:
    自定义view的时
    候。我们需要去重写onMeasure()方法去重新确定view的宽高,否则布局文件中写
    wrap_content和match_content的效果是一样的。

performLayout理解

   WindowManager.LayoutParams lp = mWindowAttributes;
//顶层布局属性   宽高
   performLayout(lp, mWidth, mHeight);

进入

private void performLayout(WindowManager.LayoutParams lp, int desiredWind
owWidth,
int desiredWindowHeight) {
    ...
    final View host = mView;
    ...
    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight
    ());
    ...
    mInLayout = false;
}

进入host.layout

public void layout(int l, int t, int r, int b) {
    ...
    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;
    boolean changed = isLayoutModeOptical(mParent) ?
    setOpticalFrame(l, t, r, b) :
    //通过这个来确定
    setFrame(l, t, r, b);
    ...
    onLayout(changed, l, t, r, b);
    ....
}

摆放空间的上下左右的值

调用onLayout方法

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }

如果是ViewGroup类型,需要调用onLayout确定子View的位置。

FrameLayout的onLayout()方法中:
它会先调用一个layoutChildren()这个方法,这个方法就是去遍历我们的子view,对子view的控
件位置摆放,然后又会去执行child.layout()方法 形成了递归的操作。

performDraw理解

   private void performDraw() {
   ...
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        ...
   }

进入draw

    private void draw(boolean fullRedrawNeeded) {
    ...
  if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
        ...
    }

进入drawSoftware


private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int
xoff, int yoff,
boolean scalingRequired, Rect dirty) {
    ...
    mView.draw(canvas)
    ...
}

进入mView.draw()

 @CallSuper
    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)
         */

列出了6个步骤

如果需要对子控件进行绘制,我们查看一下ViewGroup 中 dispatchDraw() 方法

同样是对子控件进行遍历,然后调用child.draw()方法

总结

  • ViewGroup的绘制流程

    • 绘制背景 drawBackground(canvas)
    • 绘制自己 onDraw(canvas)
    • 子view dispatchDraw()
    • 绘制前景,滚动条等装饰 onDrawForeground(canvas)
  • View的绘制流程

    • 绘制背景 drawBackground(canvas)
    • 子view dispatchDraw()
    • 绘制前景,滚动条等装饰 onDrawForeground(canvas)
在自定义开发

需要去实现onMeasure()—> onLayout() — onDraw()

  • 如果是自定义ViewGroup方法
    • onMeasure()—> onLayout () — onDraw()
  • 如果是自定义view的方法
    • onLayout方法不用 去实现,而onDraw()方法可选。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值