setContentView 流程

文章详细解释了Android中`setContentView`在Activity和AppCompatActivity中的执行流程,包括PhoneWindow、DecorView的创建过程,以及LayoutInflater如何解析XML并加载到视图中,特别关注了AppCompatActivity中对原有布局的替换操作。

setContentView 流程

继承Activity流程

PhoneWindow.setContentView — 主要目的 创建 DecorView 拿到 Content

–》 installDecor(); // 创建 DecorView 拿到 mContentParent
–》 mDecor = generateDecor(-1);
–》 new DecorView()
–》 mContentParent = generateLayout(mDecor);
a、先获取对应属性的设置
–》 R.layout.screen_simple --》 @android:id/content --》 mContentParent
// R.layout.screen_simple --》 添加到 DecorView(FrameLayout)
–》 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
–》 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

–》 mLayoutInflater.inflate(layoutResID, mContentParent); // R.layout.activity_main 渲染到 mContentParent
在这里插入图片描述

面试: Activity的setContentView 调用PhoneWindow 再调用到decorView里会又一个screen_simple.xml,这个xml有viewStub和fragmentLayout组成,我们的布局就会加载到这个fragment上。AppCompatActivity前面流程基本和Activity一致,唯一的区别就是AppCompatActivity会在fragment上再嵌套一层,然后将原来view一个个删掉,然后添加到新的布局上面,然后就会去解析xml布局,LayoutInflater这里进行加载。

创建phnWindow

ActivityThread.performLaunchActivity
–>activity.attach
–> new PhoneWindow()
–>mInstrumentation.callActivityOnCreate

PhoneWindow —》分为3类,哪些地方会创建
1.Activity
2.Dialog
3.PopupWindow
4.Toast

继承 AppCompatActivity 的流程

AppCompatDelegate.setContentView
–> ensureSubDecor();
–> mSubDecor = createSubDecor();
–> ensureWindow(); // 从Activity 那PhoneWindow
–> mWindow.getDecorView();
–> installDecor(); // mContentParent
。。。 。。。 //看Activity的流程
// R.layout.screen_simple 里面的 content —》 把 content 的 View 复制到 R.id.action_bar_activity_content
–> final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);

            删除掉原来的activity的view,写入到新的contentView里
		      while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

	--> windowContentView.setId(View.NO_ID); // 将原始R.layout.screen_simple的 content id 置为 NO_ID
    --> contentView.setId(android.R.id.content); // subDecerView R.id.action_bar_activity_content --> 置为 content
    --> mWindow.setContentView(subDecor); // 

–> ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
在这里插入图片描述

xml加载流程

// R.layout.activity_main View 创建
–> LayoutInflater.from(mContext).inflate(resId, contentParent);
// 通过反射创建View — 布局的rootView
–> final View temp = createViewFromTag(root, name, inflaterContext, attrs);

		if (-1 == name.indexOf('.')) {  //是否是sdk(非源码自带的布局如LinearLayout)
                view = onCreateView(context, parent, name, attrs);
                	--PhoneLayoutInflater.onCreateView(name, attrs);
                	//作用:补全类名:
private static final String[] sClassPrefixList = {
    "android.widget.",
    "android.webkit.",
    "android.app.",
    "android.view."
};
      	--> View view = createView(name, prefix, attrs);//同下方调用一样的方法
                 
            } else { // name = androidx.constraintlayout.widget.ConstraintLayout
                view = createView(context, name, null, attrs);
                		// 通过反射创建 View 对象
                	--> clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);
                	--> constructor = clazz.getConstructor(mConstructorSignature);//构造方法
                	--> final View view = constructor.newInstance(args);
            }
            
--> rInflateChildren(parser, temp, attrs, true); // 创建子View
	--> rInflate
		--> View view = createViewFromTag(parent, name, context, attrs);
LayoutInflate的参数的作用

// 方式一:将布局添加成功
View view = inflater.inflate(R.layout.inflate_layout, ll, true);

// 方式二:报错,一个View只能有一个父亲(The specified child already has a parent.)
View view = inflater.inflate(R.layout.inflate_layout, ll, true); // 已经addView
ll.addView(view);

// 方式三:布局成功,第三个参数为false
// 目的:想要 inflate_layout 的根节点的属性(宽高)有效,又不想让其处于某一个容器中
View view = inflater.inflate(R.layout.inflate_layout, ll, false);
ll.addView(view);

// 方式四:root = null,这个时候不管第三个参数是什么,显示效果一样
// inflate_layout 的根节点的属性(宽高)设置无效,只是包裹子View,
// 但是子View(Button)有效,因为Button是出于容器下的
View view = inflater.inflate(R.layout.inflate_layout, null, false);
ll.addView(view);

View 没有父容器 —》 尺寸大小 参数的设置无效 View的绘制流程
在这里插入图片描述

https://blog.csdn.net/m0_37707561/article/details/125890487

Q:LayoutInflater 的 inflate 方法不同参数对加载的View有什么区别?
A:主要是 ViewGroup root 和 boolean attachToRoot 这两个参数。当 root 为 null 时,布局文件最外层设置的 layout_xxx 属性就会失效。而当 root 不为 null 时, attachToRoot 为 true 就是调用 root.addView() 去添布局,此时 root 得是ViewGroup,否则会报错;而当 attachToRoot 为 false,则会正常设置 最外层 layout_xxx 属性给当前得布局文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值