Android View 绘制流程 过程分析

本文详细分析了Android View的绘制流程,从如何获取View的宽高开始,逐步探讨了从attach()方法、setContentView()方法到ActivityThread的handleResumeActivity()方法,再到ViewRootImpl类和Choreographer的页面刷新机制。最后总结了View绘制开始的整个过程,包括performTraversals()和performMeasure()、performLayout()、performDraw()等关键步骤。
摘要由CSDN通过智能技术生成

前言

今天整理了一下Android View的绘制流程,记录一下


一、如何获取View的宽高

我们经常会遇到要获取控件宽高的情况,可以通过以下两种方式来获取。

1. 通过View的post()

	MFragmentTabView tabView = findViewById(R.id.fragment_tab_view);
	tabView.post(new Runnable() {
   
	    @Override
	    public void run() {
   
	        tabView.getWidth();
	        tabView.getHeight();
	    }
	});

2. 通过ViewTreeObserver

       MFragmentTabView tabView = findViewById(R.id.fragment_tab_view);
       tabView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   
           @Override
           public void onGlobalLayout() {
   
               tabView.getWidth();
               tabView.getHeight();
           }
       });

二、View真正开始绘制是从哪里开始的呢

我们知道,在Activity的onCreate(),onResume() 中,都无法通过View的getHeight()、getWidth() 获取真正的宽高,说明此时还没有进行完View的绘制流程,那么View的绘制到底是从哪里开始的呢,我们从Activity的启动流程里已经知道,Activity生命周期被执行的第一个方法是attach().

1. 检查attach()方法

	final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
   
		......
		//实例化Window对象,并设置各种回调方法
        mWindow = new PhoneWindow(this, window, activityConfigCallback); 
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ......
    }

这里并没有关于View绘制的内容,只是创建了Window接口的唯一实现类PhoneWindow的对象实例,还有一些入参的赋值操作。然后我们接下来按照声明周期,去看onCreate()方法,发现onCreate()方法里也没有View绘制的方法,但是我们会在onCreate()回调中调用setContentView()方法,传入我们的布局文件。因为从Android21以后出现了AppCompatActivity来支持Meterial Design等特性,我们现在的Activity都是继承自AppCompatActivity,然后我们会发现setContentView方法最后是在 AppCompatDelegateImpl中实现

2. 检查setContentView()方法

@Override
public void setContentView(View v) {
   
    //生成DecorView的子View
    ensureSubDecor();
    //找到R.id.content控件,并清空子View
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    contentParent.removeAllViews();
    //将我们activity中设置的xml文件转成的View放到R.id.content控件中
    contentParent.addView(v);
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

进入到ensureSubDecor()方法中,发现是通过createSubDecor()创建的SubDector,

mSubDecor = createSubDecor();

在createSubDecor() 方法中,

private ViewGroup createSubDecor() {
   
    // 此处省略了根据主题Theam设置Window feature代码
    .......
    
    // 确保创建了Window对象,并将Window和Activity关联起来
    ensureWindow();
    // 创建DecorView及其子View contentParent,并关联到Window
    mWindow.getDecorView();

    // 此处省略了根据不同条件,给subDecor赋值不同的布局文件,生成SubDecor对象
    .......
   
    // 获取SubDecor中R.id.action_bar_activity_content控件
    final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
            R.id.action_bar_activity_content);
    // 获取DecorView中R.id.content控件
    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
    if (windowContentView != null) {
   
        // 将DecorView中content控件的子View移除并添加到SubDecor的content控件中
        while (windowContentView.getChildCount() > 0) {
   
            final View child = windowContentView.getChildAt(0);
            windowContentView.removeViewAt(0);
            contentView.addView(child);
        }
        windowContentView.setId(View.NO_ID);
        //将R.id.content赋值给SubDecor的content控件
        contentView.setId(android.R.id.content);
        if (windowContentView instanceof FrameLayout) {
   
            ((FrameLayout) windowContentView).setForeground(null);
        }
    }

    //将SubDecor设置到Window中->将subDecor设置为DecorView的子View
    mWindow.setContentView(subDecor);
	.......
    return subDecor;
}

关于DecorView的创建,从PhoneWindow的getDecorView中,进入到installDecor()方法中,

private void installDecor() {
   
    mForceDecorInstall = false;
    if (mDecor == null) {
   
        //生成DecorView并关联Window
        mDecor = generateDecor(-1);
       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ByeMoon丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值