浅谈View的绘制流程

  • Window:每个Activity都会创建一个Window用于承载View视图的显示,Window是一个抽象类存在了一个唯一实现类PhoneWindow

  • DecorView:最顶层的View,是一个FrameLayout子类,最终会被加载到Window当中,它内部只有一个垂直方向的LinearLayout分为两部分:
    • TitleBar:屏幕顶部的状态栏
    • ContentView:Activity对应的XML布局,通过setContentView设置到DecorView

Activity和Window关联

  • ActivityThread调用scheduleLaunchActivity()
  • scheduleLaunchActivity发送消息sendMessage(H.LAUNCH_ACTIVITY, r);
  • handleLaunchActivity初始化WindowMannagerService
  • performLaunchActivity创建Activity
  • 执行activity的attach
  • new PhoneWindow(this,window) + 将window和windowMangerService绑定:同时,此时完成了window和activity的绑定
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // Initialize before creating the activity
        WindowManagerGlobal.initialize();
        Activity a = performLaunchActivity(r, customIntent);
        handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
    }

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){
        //省略
        Activity activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
        return activity;
    }

final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason){
        //省略
        View decor = r.window.getDecorView();
        ViewManager wm = a.getWindowManager();
        WindowManager.LayoutParams l = r.window.getAttributes();
        wm.addView(decor, l);
    }

Activity和window和view 的关系

activity是系统可视化交互组件,四大组件都由AMS统一管理生命周期,事实上它的职责只是生命周期的管理,处于单一职责的原则,那势必需要将activity和其上的视图View进行解耦,那么久引入window的概念,它是个抽象类,对于activity来说,它的具体实现类是PhoneWindow,在activity执行attach的时候,会创建一个PhoneWindow对象,PhoneWindow作为装载根视图DecorView的顶级容器,activity通过setContentView实际上是调用了PhoneWindow来创建DecorView,并解析xml布局加载到DecorView的contentView部分。

了解Activity中setContentView源码:

public void setContentView(@LayoutRes int layoutResID) {
        //将xml布局传递到Window当中
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
}

从代码可以看出,ActivitysetContentView实质是将View传递到WindowsetContentView()方法中,WindowsetContenView会在内部调用installDecor()方法创建DecorView,看一下它的部分源码: 

 public void setContentView(int layoutResID) { 
        ...............
        //初始化DecorView以及其内部的content
        installDecor();
        //将contentView加载到DecorVoew当中
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
  private void installDecor() {
        ...............
        //实例化DecorView
        mDecor = generateDecor(-1);
        mDecor.setWindow(this);
        //获取Content
        mContentParent = generateLayout(mDecor);
 }
 protected DecorView generateDecor(int featureId) {
        ...............
        return new DecorView(context, featureId, this/*PhoneWindow*/, getAttributes());
 }

通过generateDecor()new一个DecorView,然后调用generateLayout()获取DecorViewcontent,最终通过inflateActivity视图添加到DecorView中的content中,但此时DecorView还未被添加到Window中。添加操作需要借助ViewRootImpl

ViewRootImpl的作用是用来衔接WindowManagerDecorView,在Activity被创建后会通过WindowManagerDecorView添加到PhoneWindow中并且创建ViewRootImpl实例,随后将DecorViewViewRootImpl进行关联,最终通过执行ViewRootImplperformTraversals()开启整个View树的绘制。

绘制过程

对应于ViewRootImpl类,它是连接WindowMannager和DecorView的纽带,View的三大流程均是通过ViewRootImpl完成的,当activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联在ViewRootImpl里面performTraversals()方法开始,从最顶层的View(ViewGroup)开始逐层对每个View进行绘制操作,下面来看一下该方法部分源代码:

private void performTraversals() {
     ...............
    //measur过程
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    //layout过程
    performLayout(lp, desiredWindowWidth, desiredWindowHeight);
    //draw过程
    performDraw();
}

View与window的逻辑结构

对应于ViewRootImpl类,它是连接WindowMannager和DecorView的纽带,View的三大流程均是通过ViewRootImpl完成的,当activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联在ViewRootImpl里面performTraversals()分发

Android的View绘制流程是一个复杂的过程,主要包括了几个关键的步骤:Measure(测量)、Layout(布局)、Draw(绘制)。

Measure(测量)

在View的绘制流程中,首先进行的是Measure阶段。这个阶段的主要任务是确定View及其子View的大小和宽高。在源代码中,这个过程主要由onMeasure()方法完成。在这个方法中,我们需要根据MeasureSpec(它表示的是父View希望子View占据的尺寸)来确定子View的尺寸。MeasureSpec是一个int类型,包含了两个部分:大小和模式。大小表示父View希望子View占据的尺寸,模式则表示父View是如何希望我们处理这个尺寸的。

Layout(布局)

测量阶段完成后,View会得到一个大小,然后进入Layout阶段。这个阶段的主要任务是将每个View按照它们在XML文件中定义的布局进行排列。在源代码中,这个过程主要由onLayout()方法完成。在此方法中,我们需要根据View的布局参数(如宽、高、左、上、右、下的margin等)来确定View在屏幕上的具体位置。

Draw(绘制)

Layout阶段完成后,每个View都会被放置在屏幕上的一个具体位置。然后进入Draw阶段,这个阶段的主要任务是将View及其子View的内容绘制到屏幕上。在源代码中,这个过程主要由onDraw()方法完成。在此方法中,我们可以在Canvas上绘制任何我们想要的内容。例如,可以在这个方法中绘制一个背景色、一个文字、一个图片等等。

你可能感兴趣的:(关于Android,#,View,View,draw)