ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

android高级UI布局层次结构及布局加载流程源码解析,终局之战

2021-12-25 14:02:34  阅读:155  来源: 互联网

标签:name 布局 注释 源码 UI attrs view root View


复制代码

2、mWindow.setContentView(layoutResID)

通过上面我们知道最终调用的是PhoneWindow的setContentView,方法如下所示,主要干了两件事情,注释1用于生成DecorView和mContentParent;注释2处用于将layoutResID(也就是我们实现的布局id)设置到mContentParent中去;

public void setContentView(int layoutResID) {

if (mContentParent == null) {

installDecor(); //1

}

、、、

mLayoutInflater.inflate(layoutResID, mContentParent);//2

、、、

}

复制代码

3、installDecor()

installDecor方法代码如下所示,注释1处通过generateDecor方法生成mDecor变量,generateDecor方法内部其实就是new了一个DecorView;注释2处通过generateLayout方法获得mContentParent;mDecor是PhoneWindow的成员变量,是DecorView类型的,继承自FrameLayout;而这个mContentParent代表mDecor本身或者mDecor的子布局,什么意思呢?其实在mDecor的上部有一个占位View,根据不同的主题加载不同的DecorView,如果没有加载顶部view那mContentParent就是mDecor了;

private void installDecor() {

、、、

mDecor = generateDecor(-1); //1

、、、

mDecor.setWindow(this);

、、、

mContentParent = generateLayout(mDecor); //2

}

复制代码

4、布局层次

经过上面的分析,我们发现Activity持有一个PhoneWindow的成员变量;PhoneWindow持有一个DecorView的成员变量,DecorView内部持有一个mContentParent,所以布局层次结构就是 Activity – PhoneWindow – DecorView – mContentParent;

而我们写的xml布局就是放在了mContentParent中;那么xml布局是如何加载到mContentParent容器的呢?其实也就是步骤2的注释2的mLayoutInflater.inflate(layoutResID, mContentParent)进行加载的,我们在下一部分分析加载的流程;

二、布局加载流程分析


1、mLayoutInflater.inflate(layoutResID, mContentParent)的调用链如下所示;

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

return inflate(resource, root, root != null); //1

}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

final Resources res = getContext().getResources();

XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot); //2

} finally {

parser.close();

}

}

复制代码

2、最终会调用到如下inflate方法,注释1处将root赋值给result;注释2处会通过createViewFromTag创建根View;注释4处用于inflate所有的children;这里解释一下最后一个attachToRoot参数,如果设置为true,会调用注释5将根view添加到root中然后将root返回;如果设置为false,会调用到注释3处,将root的参数设置到根view中去,但并不会执行addView操作,然后调用到注释6处将根view赋值给result,这样返回的就是根view而不是root;

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {

View result = root; //1

final String name = parser.getName();

// Temp is the root view that was found in the xml

final View temp = createViewFromTag(root, name, inflaterContext, attrs); //2

ViewGroup.LayoutParams params = null;

if (root != null) {

// Create layout params that match root, if supplied

params = root.generateLayoutParams(attrs);

if (!attachToRoot) {

// Set the layout params for temp if we are not

// attaching. (If we are, we use addView, below)

temp.setLayoutParams(params); //3

}

}

// Inflate all children under temp against its context.

rInflateChildren(parser, temp, attrs, true); //4

// We are supposed to attach all the views we found (int temp)

// to root. Do that now.

if (root != null && attachToRoot) {

root.addView(temp, params); //5

}

// Decide whether to return the root that was passed in or the

// top view found in xml.

if (root == null || !attachToRoot) {

result = temp; //6

}

return result;

}

复制代码

3、通过createViewFromTag创建根view,此方法调用链如下所示;注释1处是第一次创建view;注释2和注释3最终都会调用到注释3处的方法;注释1是一种情况,注释3是另一种情况,我们分别讨论一下;

private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {

return createViewFromTag(parent, name, context, attrs, false);

}

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,

boolean ignoreThemeAttr) {

View view = tryCreateView(parent, name, context, attrs); //1

if (view == null) {

final Object lastContext = mConstructorArgs[0];

mConstructorArgs[0] = context;

try {

if (-1 == name.indexOf(’.’)) {

view = onCreateView(context, parent, name, attrs); //2

} else {

view = createView(context, name, null, attrs); //3

}

}

}

return view;

}

复制代码

3-1 tryCreateView

第一种情况tryCreateView方法如下所示,如果mFactory2不为空,会通过注释1处mFactory2的onCreateView方法创建view;如果mFactory不为空,会通过注释2处mFactory的onCreateView创建view;那么这两个factory是什么呢?如注释3和注释4所示,Factory是一个单方法的接口,Factory2继承自Factory,重载了一个onCreateView方法;这两个变量初始都为空,因此我们开发者可以实现这两个接口,然后赋值给这两个变量,这样就会优先通过我们设置的方法加载view了;

public final

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

View tryCreateView(@Nullable View parent, @NonNull String name,

@NonNull Context context,

@NonNull AttributeSet attrs) {

View view;

if (mFactory2 != null) {

view = mFactory2.onCreateView(parent, name, context, attrs); //1

} else if (mFactory != null) {

view = mFactory.onCreateView(name, context, attrs); //2

} else {

view = null;

}

return view;

}

private Factory mFactory; //3

private Factory2 mFactory2; //4

public interface Factory {

View onCreateView(@NonNull String name, @NonNull Context context,

@NonNull AttributeSet attrs);

}

public interface Factory2 extends Factory {

View onCreateView(@Nullable View parent, @NonNull String name,

@NonNull Context context, @NonNull AttributeSet attrs);

}

复制代码

3-2 createView

因为系统并没有给mFactory和mFactory2赋值,因此会走到createView方法;方法代码如下所示,注释1处首先从sConstructorMap获取构造方法,第一次肯定为空;然后走到注释2处,通过反射获得构造方法,mConstructorSignature这个参数代表的是两个参数的构造方法,因此返回的就是带两个参数的构造方法;接着在注释3处将构造方法放入sConstructorMap中;最后在注释4处通过newInstance方法获取view实例并返回;

public final View createView(@NonNull Context viewContext, @NonNull String name,

@Nullable String prefix, @Nullable AttributeSet attrs)

throws ClassNotFoundException, InflateException {

Constructor<? extends View> constructor = sConstructorMap.get(name); //1

if (constructor == null) {

constructor = clazz.getConstructor(mConstructorSignature); //2

constructor.setAccessible(true);

sConstructorMap.put(name, constructor); //3

}

Object lastContext = mConstructorArgs[0];

mConstructorArgs[0] = viewContext;

Object[] args = mConstructorArgs;

args[1] = attrs;

final View view = constructor.newInstance(args); //4

return view;

标签:name,布局,注释,源码,UI,attrs,view,root,View
来源: https://blog.csdn.net/flutter001/article/details/122142657

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有