一篇文章搞定《Android中View的绘制流程》

本文前言

像事件的分发一样,View的绘制流程我也分成了三部分来讲
分别为:1、怎样到达ViewRootImpl。2、到达ViewRootImpl做了什么。3、View的最终绘制
之后我会利用一个自定义View的实例来反刍这篇文章,像《一篇文章搞定搞定事件分发》一样。

怎样到达ViewRootImpl

过程如下:

1、首先View是在Activity的onCreate阶段通过setContentView解析xml加载进去的。也就是View是在OnCreate阶段加到缓存中的。
2、加在哪个缓存中了呢:这个View对象会被添加到ActivityThread的ActivityClientRecord对象中(后面可以看到取出来添加到DecorView中),并放入ActivityThread的mActivities集合中缓存当前Activity对象的状态信息。
3、ActivityThread.handleResumeActivity()是在Activity的生命周期中的onResume阶段调用的,用于将Activity设置为resumed状态。
4、ActivityThread.handleResumeActivity()会调用WindowManager的addView()方法来把Activity的Window视图添加到窗口上。源码如下:

@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, String reason) {
    
    
        .....
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
    
    
            .....
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            if (!a.mWindowAdded) {
    
    
                ......
                a.mWindowAdded = true;
                wm.addView(decor, l);
            } else {
    
    
                a.onWindowAttributesChanged(l);
    }
}

5、可以看到ActivityThread.handleResumeActivity()方法中,会先通过Activity的getWindow()方法获取到当前Activity的Window,设置相关的Window参数,接着调用WindowManager的addView方法将Window的视图加入到窗口中。
6、这里面ActivityClientRecord只是Activity的一个信息类,记录一下Activity的状态、所属进程等相关信息。
7、WindowManager实现类为WindowManagerImpl,WindowManagerImpl中addView()方法又会调用WindowManagerGlobal的addView()方法。源码如下:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
    
    
    ······
    root = new ViewRootImpl(view.getContext(), display);

    view.setLayoutParams(wparams);

    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    //即将开始流程绘制    
    root.setView(view, wparams, panelParentView);
    ·······
}

8、可以看到他会创建出我们熟知的ViewRootImpl。并将我们的View Set给ViewRootImpl进行管理。
上面的这个过程可以理解为Android ManagerService的功劳,通过管理Activity的生命周期从而加载我们的View。

流程图小结:

在这里插入图片描述

到达ViewRootImpl做了什么

像事件的分发一样,最终View都交给了ViewRootImpl来进行管理。这也是ViewRootImpl代码上万行的原因。经过不断的迭代,View的体积已经很庞大了。这也许是Jetpack Compose出现的原因吧,来替换掉这庞大的View体系,毕竟庞大就难以维护。
下面看看ViewRootImpl对View的绘制做了哪些关键的步骤。

第一步:setView()

首先上面讲到了ViewRootImpl的setView。那setView做了什么呢?
我们知道ViewRootImpl中的mView就是我们的根布局DecorView这个在事件的分发中也有提到。这里再说一遍吧:
其实上面认真读代码的人已经发现了,就在第一步handleResumeActivity方法中。我们先是创建了window之后获取了decor,并通过windowsManager的wm.addView(decor, l)设置给了下一步。最终到达ViewRootImpl。
下面是ViewRootImpl的setView方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    
    
     if (mView == null) {
    
    
        mView = view;
         // 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首先将window给他的view和自己的mView进行了绑定。毕竟自己家的小孩才能管,对吧。

第二步:performTraversals()

下一步就来到了View绘制的关键方法performTraversals()。
上面可以看到执行了requestLayout()方法,requestLayout()方法中会异步执行performTraversals()方法,View的三大流程都是在该方法中执行的。

@Override
public void requestLayout() {
    
    
    if (!mHandlingLayoutInLayoutRequest) {
    
    
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

到这儿我们算是明白View的绘制流程是从哪儿开始的,接下来分析这个过程到底是怎么做的。
直接简化一下performTraversals的代码便于理解(简化把----嘻嘻)

private void performTraversals() {
    
    

    //计算DecorView根View的MeasureSpecint childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

    performLayout(lp, mWidth, mHeight);

    performDraw();
}

真正的奥秘就在performMeasure()、performLayout()、performDraw()大家看着熟悉吗是不是想到了,说烂嘴的onMeasure()、onLayout()、onDraw()方法。
没错昂、看来你很有学习Android的脑瓜。他最终就调用了我们的mView也就是DecorView的三大绘制方法。

第三步:DecorView中的Measure()、Layout()、Draw()

让我们来看一下:
performMeasure()

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

performLayout()

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) {
    
    
        final View host = mView;
        if (host == null) {
    
    
            return;
        }
        try {
    
    
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        } finally {
    
    
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

performDraw()
这个方法中没有直接调用mView的Draw。是先调用draw方法从而调用drawSoftware。最后还是一样会调用Draw。
源码如下:都被简化的了方便理解

private boolean performDraw() {
    
    
    try {
    
    
        boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
        ....
    } finally {
    
    
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

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

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    
    
    try {
    
    
        ....
        mView.draw(canvas);
        ....
    } finally {
    
    
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}  

最后可以看到调用了mView的Measure、Layout、Draw方法。
我们点进去发现,在View的Measure、Layout、Draw会调用我们熟知的onMeasure()、onLayout()、onDraw()方法。这个源码就不粘贴出来了。大家可以点进去看一下。

View的最终绘制

所谓View的最终绘制就是。简单来说就是View是如果通过onMeasure()、onLayout()、onDraw(),最终绘制的界面上的。
下面会对这三个重要方法进行知识解析,随后我会以两篇自定义View的文章去用例子来反刍这个知识点。

首先要知道什么是View树

onMeasure()

onLayout()

onDraw()

猜你喜欢

转载自blog.csdn.net/weixin_45112340/article/details/131306967