UI核心 - UI绘制流程及原理

0 阅读2分钟

在这里插入图片描述

1. View添加到屏幕窗口的源码分析

入口:在Activity的onCreate中调用setContentView传入布局资源ID。 接着调用 :getWindow().setContentView(layoutResID); 其中 getWindow,返回就 Window 的唯一实现类 PhoneWindow。

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

PhoneWindow:

    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ...
        }
        ...
    }
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor(-1); // return new DecorView 
           ...
        } else {
            ...
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...
        }
    }

在这里插入图片描述 在 PhoneWindow 的 setContentView 中,会调用 installDecor 里面的方法执行 generateDecor 创建 DecorView。 DecorView继承自FrameLayout,是应用窗口的顶层View容器。 然后将 mDecor 传进 generateLayout, 该方法根据系统主题属性(如是否有ActionBar、浮动窗口等)确定不同的layoutResource(例如R.layout.screen_simple)。 通过 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); 将得到的布局资源添加到 DecorView 中。 然后通过 findViewById 获取 id 为 com.android.internal.R.id.content 的基础布局,一般为 FrameLayout,后面我们的界面就是绘制在这个布局里面的。 最终返回这个 contentParent 在这里插入图片描述

小结

PhoneWindow持有DecorView。DecorView中包含根据主题生成的基础布局(如screen_simple),该基础布局通常包含标题栏(Title/ActionBar)和内容区域(ID为content的FrameLayout)。我们的XML布局最终被添加到这个content容器中。

2. View绘制流程的触发机制

绘制的入口是ActivityThread的handleResumeActivity方法。 该方法在执行performResumeActivity(回调onResume)后,初始化WindowManager.LayoutParams,并调用WindowManager的addView方法。

WindowManager 来源:

  • Activity中的WindowManager接口实现类为WindowManagerImpl。
  • WindowManagerImpl的addView方法委托给WindowManagerGlobal的addView方法。
  • WindowManagerGlobal.addView中创建ViewRootImpl对象,并将DecorView、布局参数等与之关联。

关键调用:root.setView(view, wparams, panelParentView),其中root即为ViewRootImpl实例。

3. 绘制核心流程:PerformTraversals

在ViewRootImpl.setView中调用requestLayout()。该方法首先通过checkThread()确保当前操作在主线程执行,随后调用scheduledTraversals()。 scheduledTraversals()通过Choreographer(编舞者) post一个TraversalRunnable。当VSync信号到来时,TraversalRunnable的run方法被执行,进而调用doTraversal()。 doTraversal()的核心是调用performTraversals()。在此方法中,依次执行UI绘制的三个关键步骤:

  • 测量 (Measure):调用performMeasure(),确定View的大小。
  • 布局 (Layout):调用performLayout(),确定View的位置。
  • 绘制 (Draw):调用performDraw(),将View内容渲染到屏幕上。 在这里插入图片描述