Android中View绘制流程(源码)

日常开发中,我们经常碰到需要自定义View(包括自定义View和ViewGroup)的地方,并且在面试中也需要我们熟悉或者精通自定义View。毕竟有时候我们需要实现的效果,Android本身是没有提供的,就需要我们去自定义,掌握原理可以让我们在实际使用中更加得心应手。

一、View是如何被添加到屏幕上的

从Activity走起,我们日常是通过Activity的onCreate()方法中的 setContentView(R.layout.activity_main) 给Activity设置布局文件。我们一步一步的往下走,

  1. 点击setContentView(),到Activity源码
public void setContentView(@LayoutRes int layoutResID) {
      // 调用的是Window的setContentView()方法
      getWindow().setContentView(layoutResID);
      initWindowDecorActionBar();
}
  1. 查看getWindow()发现返回的是一个Window对象,查看Window
public Window getWindow() {
   return mWindow;
}

// 然后到Window对象去查看,看到注释
// he only existing implementation of this abstract class is android.view.PhoneWindow
// 告诉我们它是一个抽象类,有唯一的一个实现类PhoneWindow,所以我们到PhoneWindow中去查看,找到setContentView()方法
  1. 查看PhoneWindow的setContentView(),这里我们主要查看installDecor()和mLayoutInflater.inflate(layoutResID, mContentParent)。
public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // 去创建一个DecorView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            // 根据我们传入的布局资源ID==》layoutResID去解析xml布局资源
            // 通过后面源码的分析,我们知道setContentView(layoutId)
            // layoutResID是被放在基础容器的FrameLayout中的
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
  • 查看PhoneWindow的 installDecor(),省略一下代码,只查看关键代码
private void installDecor() {
      ...
      // 判断DecorView是否为空
      if (mDecor == null) {
            // 需要去创建DecorView
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        ...
        // 判断mContentParent(是ViewGroup)是否为空
        if (mContentParent == null) {
             // 根据DecorView去得到了内容容器FrameLayout
             mContentParent = generateLayout(mDecor);
        }
}        
  • 查看关键的 PhoneWindow的generateDecor(-1) 方法,这是创建DecorView的方法,通过查看源码我们可以看到DecorView是继承FrameLayout(帧布局),是一个容器。
protected DecorView generateDecor(int featureId) {
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        // 重点关注最后一行,在这里直接根据context和featureId创建了一个DecorView
        return new DecorView(context, featureId, this, getAttributes());
    }
  • 查看 PhoneWindow的generateLayout(),根据源码我们知道:根据不同的主题或者一些特性,为窗体PhoneWindow设置了不同的属性。根据features 的不同去得到了一个不同的layoutResource,
protected ViewGroup generateLayout(DecorView decor) {
    ...
	// Inflate the window decor.
    // 定义一个Layout的资源
	int layoutResource;
        int features = getLocalFeatures();
        // 根据不同的features和其他判断条件,给layoutResource设置不同的Layout资源
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
         ...
         // 省略一些代码,下面省略的判断条件基本跟上面这个条件大同小异
        } else {
            layoutResource = R.layout.screen_simple;
        }

        mDecor.startChanging();
        // 解析layoutResource布局代表的基础容器,将基础容器装入DecorView
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        
        // 根据ID_ANDROID_CONTENT去获取主容器,根据下面从Window中拿过来的注释和代码我们可以看到
        // The ID that the main layout in the XML layout file should have.
        // public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        
        ... 省略代码
        
        // 将主容器contentParent返回
        return contentParent;
}
  • 查看 DecorView中的mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);,将layoutResource所代表的基础容器装入DecorView中。
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        ...省略代码

        mDecorCaptionView = createDecorCaptionView(inflater);
        // 解析layoutResource
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            // 将layoutResource得到的View装入DecorView中
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

到此为止的图解,类结构图和视图结构图
类结构图
窗口的类结构图视图结构图
根据layoutResource的其中一个R.layout.screen_simple,我们知道每个主题设置到的布局是一个线性布局LinearLayout ,下一级就是ViewStub 涉及的ActionBar之类的,最后就是我们刚刚讲到的内容容器FrameLayout(Window中的主容器ID:com.android.internal.R.id.content,看下面代码我们就可以知道就是下面代码中FrameLayout的布局ID)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

窗口视图结构图
注意:视图的绘制从DecorView开始依次递归,ViewGroup —> View的调用:measure、layout、draw

总结:首先系统会创建一个顶层的布局容器DecorView,DecorView是一个容器(ViewGroup)继承自FrameLayout,是PhoneWindow持有的实例,是所有应用程序的顶层View,在系统内部进行初始化。当DecorView创建完成之后,系统会根据应用主题特性去加载一个基础容器,不论基础容器是什么,其中都会有一个id为com.android.internal.R.id.content的FrameLayout容器,而我们通过setContentView(layoutId)设置的xml布局就是被添加到这个FrameLayout容器中。


二、View的绘制流程
  1. 绘制入口
    ActivityThread的handleResumeActivity,我没有具体去查看,ActivityThread中的一些源码跟以前有了很大的变化。

来看看handleMessage这个类就跟网上讲的很多的不同,其实源码怎么变都是大同小异。只是以前怎么调用一些生命周期方法,现在进行了更多的抽取。

  • 来看一下我们需要使用的handleResumeActivity涉及到的生命周期方法现在的具体实现

ActivityThread实现ClientTransactionHandler接口,然后实现Activity的各个生命周期方法的具体逻辑。

然后在handleMessage中调用,具体调用TransactionExecutor的execute()方法

case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    // TransactionExecutor中去真正的执行Activity的生命周期方法
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;

TransactionExecutor的execute()

public void execute(ClientTransaction transaction) {
        ... 省略代码

        // 下面两个方法最终都会走到执行生命周期方法
        executeCallbacks(transaction);
        
        executeLifecycleState(transaction);
        mPendingActions.clear();
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
    }

TransactionExecutor的调用生命周期的方法,我们发现调用的ClientTransactionHandler的生命周期方法,这些实现是在ActivityThread中。

private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
            ClientTransaction transaction) {
        final int size = path.size();
        for (int i = 0, state; i < size; i++) {
            state = path.get(i);
            if (DEBUG_RESOLVER) {
                Slog.d(TAG, tId(transaction) + "Transitioning activity: "
                        + getShortActivityName(r.token, mTransactionHandler)
                        + " to state: " + getStateName(state));
            }
            switch (state) {
                case ON_CREATE:
                    mTransactionHandler.handleLaunchActivity(r, mPendingActions,
                            null /* customIntent */);
                    break;
                case ON_START:
                    mTransactionHandler.handleStartActivity(r, mPendingActions);
                    break;
                case ON_RESUME:
                    mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                    break;
                case ON_PAUSE:
                    mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
                            "LIFECYCLER_PAUSE_ACTIVITY");
                    break;
                case ON_STOP:
                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
                            "LIFECYCLER_STOP_ACTIVITY");
                    break;
                case ON_DESTROY:
                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                            0 /* configChanges */, false /* getNonConfigInstance */,
                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                    break;
                case ON_RESTART:
                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
                    break;
                default:
                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
            }
        }
    }
  • 接下来具体查看我们关键的View绘制入口方法handleResumeActivity源码
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
		unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        // 回调Activity中onResume()生命周期方法
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        
        ...省略代码
        
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            // wm.addView(decor, l),我们需要去找到这个方法具体的实现的地方
            ViewManager wm = a.getWindowManager();
            // 初始化窗口属性
            WindowManager.LayoutParams l = r.window.getAttributes();
            
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    
                    // 将DecorView装入窗口
                    wm.addView(decor, l);
                } else {
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        // Get rid of anything left hanging around.
        cleanUpPendingRemoveWindows(r, false /* force */);       
}
  • 接下来我们根据getWindowManager在Window中找到具体的实现类WindowManagerImpl,然后在其中去找到wm.add()方法
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
  • WindowManagerImpl的wm.addView()
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
  • 最终找到具体的实现add方法的地方WindowManagerGlobal的addView()
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
		...省略代码
		// 这里声明了一个ViewRootImpl对象
		ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            // 将前面声明的root(ViewRootImpl)对象进行实例化
            root = new ViewRootImpl(view.getContext(), display);
            // 给View设置属性
            view.setLayoutParams(wparams);
            // 最终将对应的View添加到相应的集合中
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // 最后root调用setView方法将我们传入的View,LayoutParams属性和panelParentView进行关联
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
}
  • 走到ViewRootImpl的setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法,然后执行ViewRootImpl的requestLayout()方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ...省略代码
   // Schedule the first layout -before- adding to the window
   // manager, to make sure we do the relayout before receiving
   // any other events from the system.
   requestLayout();
   ...省略代码
}          

+执行ViewRootImpl的 scheduleTraversals()

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 执行这样代码,然后mTraversalRunnable的run方法会被调用
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ... 省略代码
        }
    }
  • 执行mTraversalRunnable的run方法中的ViewRootImpl的doTraversal()
void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            // 将会在这个方法中执行我们View绘制的三大步骤
            performTraversals();

            ...
        }
    }
  • 最后执行ViewRootImpl的 performTraversals() 方法,最终会执行View绘制流程的三个方法,测量、布局、绘制。
// Ask host how big it wants to be
// 找到我们绘制流程第一步测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

// 然后找到绘制流程的第二步布局
performLayout(lp, mWidth, mHeight);

// 找到我们绘制流程的最后一步,draw
performDraw();

View绘制的详细流程
  1. 测量performMeasure
  • View的measure()方法measure(int widthMeasureSpec, int heightMeasureSpec)
  • 在measure方法中调用了**onMeasure(int widthMeasureSpec, int heightMeasureSpec)**方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
  • 调用setMeasuredDimension(int measuredWidth, int measuredHeight)
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;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }
  • 最终调用了setMeasuredDimensionRaw(int measuredWidth, int measuredHeight),最终得到控件的宽高
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        // 最终的结果就是给我们测量的宽高进行保存,实际就是测量我们控件的宽高
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
  • 通过回溯,我们发现我们的measure涉及到了MeasureSpec对象

View的测量是通过 尺寸 + 模式 来保存的,将这两个值保存在MeasureSpec。
MeasureSpec是一个32位的int类型的值,前两位保存模式,后30位保存尺寸。
SpecMode(前2位) + SpecSize(后30位)

MeasureSpec中的三种模式

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

// 不确定,父类没有约束,view可以使任意大小,一般是系统使用
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
// 对应的二进制
00000000 00000000 00000000 00000000

// 精确的,父类测量除了View的大小,所以我们view的大小就是specSize
// 对应我们的match_content和固定大小
public static final int EXACTLY  = 1 << MODE_SHIFT;
// 对应的二进制
01000000 00000000 00000000 00000000

// 包裹,父容器指定一个可用大小,view最大不能超过这个容器的大小
// 对应我们的wrap_content
public static final int AT_MOST  = 2 << MODE_SHIFT;
10000000 00000000 00000000 00000000
// 对应的二进制

我们常用的MeasureSpec的方法:

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转换成MeasureSpec
            // 这里意思是我们要去size的后30位和mode的前两位
            // &:都为1才是1,否则为0,|:只要有一个为1就是1,否则为0
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
      }
}

MODE_MASK:11000000 00000000 00000000 00000000
~MODE_MASK:00111111 11111111 11111111 11111111
public static int getMode(int measureSpec) {
       //noinspection ResourceType
       return (measureSpec & MODE_MASK);
}

public static int getSize(int measureSpec) {
     return (measureSpec & ~MODE_MASK);
}
  • 最后我们回到ViewRootImpl中的performTraversals()中去,去查看DecorView的测量,找到performMeasure(childWidthMeasureSpec, childHeightMeasureSpec),通过代码我们可以看到DecorView获取宽高的MeasureSpec调用了getRootMeasureSpec()方法。

DecorView的MeasureSpec是由窗口大小和DecorView自身的LayoutParams决定。

// mWidth窗口容器的宽,lp.width我们的顶级ViewDecorView的宽
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

通过getRootMeasureSpec获取DecorView的MeasureSpec

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

通过前面的查找,我们知道performMeasure方法会走到View的onMeasure,因为DecorView是继承的FrameLayout,所以我们去查看FrameLayout的onMeasure方法。

FrameLayout的OnMeasure

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	...省略代码
	// 遍历所有子View
	for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                // 调用ViewGroup的measureChildWithMargins测量所有孩子的尺寸
                // 将父容器的测量规格传递下去
                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);
                    }
                }
            }
        }
}

ViewGroup的measureChildWithMargins

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

获取子View的测量规格

// spec:父容器的测量规格
// padding:父容器已经使用的空间大小
// childDimension:子View的尺寸
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的MeasureSpec
parentMeasureSpec \ childLayoutParamsEXACTLYAT_MOSTUNSPECIFIED
dp/pxEXACTLY childSizeEXACTLY childSizeEXACTLY childSize
match_parentEXACTLY parentSizeAT_MOST parentSizeUNSPECIFIED 0
wrap_contentAT_MOST parentSizeAT_MOST parentSizeUNSPECIFIED 0

总结:
1、ViewGroup测量:measure() --> onMeasure()(需要测量子控件的宽高) --> setMeasuredDimension --> setMeasuredDimensionRaw(保存自己的宽高)
2、View的测量:measure() --> onMeasure() --> setMeasuredDimension() --> setMeasuredDimensionRaw(保存自己的宽高)

注意:我们平时在自定义View的时候,一定要重写OnMeasure()方法,我们从上面View的OnMeasure()方法中可以看到,其中默认调用了getDefaultSize()方法,然后从getDefaultSize的源码中可以看到,不管是AT_MOST还是EXACTLY最终返回的测量结果都是specSize,所以会造成我们在布局中写match_parent和wrap_content没有任何区别


  1. 布局performLayout
  • 1、调用view.layout确定自身的位置,即确定上、下、左、右的值
  • 2、如果是ViewGroup,需要在onLayout中确定子view的位置
  • 在ViewRootImpl中找到 performLayout() 方法。
// Window的LayoutParams,window的宽、高
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
        ...省略代码
 		// mView就是我们的DecorView
		final View host = mView;
		if (host == null) {
            return;
        }
        ...省略代码
        // 调用View的layout方法,确定view自身的位置
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        ...省略代码
}
  • View的 layout() 方法
public void layout(int l, int t, int r, int b) {
        ...省略代码
		// layout其实就是为了求解左、上、右、下的值
		int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;
        // 通过setFrame去获取mLeft、mTop、mBottom、mRight的值
        // 确定View自身的位置
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            // 调用了onLayout方法,是一个空方法,如果是ViewGroup,就需要实现这个方法,然后确定子View的位置
            onLayout(changed, l, t, r, b);

            ...省略代码
        }
        ...省略代码
}
  • View的 setFrame() 方法
protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = false;

        if (DBG) {
            Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
                    + right + "," + bottom + ")");
        }

        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = true;

            // Remember our drawn bit
            int drawn = mPrivateFlags & PFLAG_DRAWN;

            int oldWidth = mRight - mLeft;
            int oldHeight = mBottom - mTop;
            int newWidth = right - left;
            int newHeight = bottom - top;
            boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

            // Invalidate our old position
            invalidate(sizeChanged);
			
			// 获取左、上、右、下的值
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

            mPrivateFlags |= PFLAG_HAS_BOUNDS;


            if (sizeChanged) {
                sizeChange(newWidth, newHeight, oldWidth, oldHeight);
            }

            if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
                // If we are visible, force the DRAWN bit to on so that
                // this invalidate will go through (at least to our parent).
                // This is because someone may have invalidated this view
                // before this call to setFrame came in, thereby clearing
                // the DRAWN bit.
                mPrivateFlags |= PFLAG_DRAWN;
                invalidate(sizeChanged);
                // parent display list may need to be recreated based on a change in the bounds
                // of any child
                invalidateParentCaches();
            }

            // Reset drawn bit to original value (invalidate turns it off)
            mPrivateFlags |= drawn;

            mBackgroundSizeChanged = true;
            mDefaultFocusHighlightSizeChanged = true;
            if (mForegroundInfo != null) {
                mForegroundInfo.mBoundsChanged = true;
            }

            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
        return changed;
    }

总结:布局的流程
1、ViewGroup --> layout(确定自己的位置,四个点left,top,right,bottom的位置) --> onLayout(确定子view的位置)
2、View --> layout(确定自己的位置,四个点left,top,right,bottom的位置)


  1. 绘制performDraw
  • 绘制背景drawBackground(canvas)
  • 绘制自己onDraw(canvas)
  • 绘制子View,dispatchDraw(canvas)
  • 绘制前景,滚动条等onDrawForeground(canvas)

在ViewRootImpl中找到 performDraw() 方法,然后在其中找到 draw() 方法。

 ...省略代码
try {
			// 找到draw方法
            boolean canUseAsync = draw(fullRedrawNeeded);
            if (usingAsyncReport && !canUseAsync) {
                mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
                usingAsyncReport = false;
            }
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
 ...省略代码        

ViewRootImpl的 draw(),然后在方法中找到drawSoftware()

if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
                        scalingRequired, dirty, surfaceInsets)) {
                    return false;
                }

最终在drawSoftware()方法中找到mView.draw(canvas),然后走到View的draw()方法中去

View的draw()

public void draw(Canvas canvas) {
     // 关键代码,绘制流程
     /*
         * 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)
         */
		// Step 1, draw the background, if needed
        int saveCount;

        drawBackground(canvas);

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // Step 7, draw the default focus highlight
            drawDefaultFocusHighlight(canvas);

            if (debugDraw()) {
                debugDrawFocus(canvas);
            }

            // we're done...
            return;
        }
}

我们查看ViewGroup对View的绘制,在ViewGroup中搜索dispatchDraw(canvas),ViewGroup为我们实现了这个方法。

// 截取一部分代码,还有其他很多源码,有兴趣可以自己观摩
for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
                    // 遍历所有的子View,然后调用drawChild进行子View的绘制
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                // 遍历所有的子View,然后调用drawChild进行子View的绘制
                more |= drawChild(canvas, child, drawingTime);
            }
        }

ViewGroup的drawChild()

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        // 最终又调用了View的draw方法
        return child.draw(canvas, this, drawingTime);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃骨头不吐股骨头皮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值