Android布局填充器--深入LayoutInflater一探究竟
文章目录
Android源码–深入LayoutInflater一探究竟(基于android-12.0.0_r34分析)
前文:
单例设计模式在Android开发实际应用场景解析–activity的管理
https://blog.csdn.net/weixin_46039528/article/details/132287718?spm=1001.2014.3001.5501
Android源码设计模式–单例模式分析,系统服务开机注册单例模式源码解析
https://blog.csdn.net/weixin_46039528/article/details/132309733?spm=1001.2014.3001.5501
LayoutInflater 是一个抽象类,找到它具体的实现类
@SystemService(Context.LAYOUT_INFLATER_SERVICE)public abstract class LayoutInflater {......}
系统开机注册
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(ctx.getOuterContext());}});
跟进PhoneLayoutInflater,这里看到PhoneLayoutInflater是LayoutInflater的一个实现类。
public class PhoneLayoutInflater extends LayoutInflater { //TextView 的完整路径是 android.widget.TextView,一些view的前缀字符串 private static final String[] sClassPrefixList = { "android.widget.", "android.webkit.", "android.app." };...... @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { //在View名字的前面添加前缀来构造 View 的完整路径,android.widget.TextView for (String prefix : sClassPrefixList) { try { View view = createView(name, prefix, attrs); if (view != null) { return view; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } }......}
核心就是onCreateView通过传进来的名字,name,prefix前缀构造出对应的View对象,比如TextView。
接下来我们康康AppCompatActivity的setContentView,它的核心就是通过LayoutInflater来加载我们的布局。AppCompatActivity 的 setContentView 方法和 Activity的 setContentView 方法是有区别的。因为我们都是继承AppCompatActivity开发较多,此处只对AppCompatActivity分析。
@Overridepublic void setContentView(@LayoutRes int layoutResID) { initViewTreeOwners(); //getDelegate返回的是一个对象。 getDelegate().setContentView(layoutResID);}
跟进getDelegate,看到返回的是AppCompatDelegate,这是一个抽象类。
@NonNullpublic AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = AppCompatDelegate.create(this, this); } return mDelegate;}
我们来看 AppCompatDelegate 的 create(Activity activity, AppCompatCallback callback) 方法,
@NonNull public static AppCompatDelegate create(@NonNull Dialog dialog, @Nullable AppCompatCallback callback) { //AppCompatDelegateImpl是它的一个实现类,就是它调用了setContentView return new AppCompatDelegateImpl(dialog, callback); }
跟进到AppCompatDelegateImpl中的,有三个重载方法
...... @Override public void setContentView(int resId) { //初始化DecorView ensureSubDecor(); //设置setContentView 要的父布局 ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content); //加载之前移除所有的布局 contentParent.removeAllViews(); //完成布局的加载 LayoutInflater.from(mContext).inflate(resId, contentParent); mAppCompatWindowCallback.getWrapped().onContentChanged(); }......
我们看到会加载一个系统定义好的布局contentParent,通过 LayoutInflater 的 inflate 函数将指定布局的视图添加到mContentParent 中。
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); } public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { ...... // 获取xm1资源解析器 XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }// 参数1为xmL 解析器,参数2为要解析布局的父视图,参数3为是否将要解析的视图添加到父视图中 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; //存储父视图 View result = root; ...... //解析merge 标签 if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException(" can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml //不是merge 标签那么直接解析布局中的视图 //这里就是通过 xml的 tag 来解析 layout 根视图 //name 就是要解析的视图的类名,如 RelativeLayout //createViewFromTag会将该元素的parent 及名字传递过来 final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) {System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied //生成布局参数 params = root.generateLayoutParams(attrs); //如果attachToRoot 为 false,给 temp 设置布局参数 if (!attachToRoot) {// Set the layout params for temp if we are not// attaching. (If we are, we use addView, below)temp.setLayoutParams(params); } } ...... // Inflate all children under temp against its context. //解析 temp 下的所有子view rInflateChildren(parser, temp, attrs, true); ...... // We are supposed to attach all the views we found (int temp) // to root. Do that now. //如果Root 不为空,attachToRoot 为true,将 temp 添加到父视图中 if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. //如果root 为空或者attachToRoot 为 false,结果就是 temp if (root == null || !attachToRoot) { result = temp; } }...... return result; } }
大致流程:解xml中的根标签,最外层的xml布局元素,如果根标签是 merge,那么调用rInflate进行解析,rnflate会将 merge 标签下的所有子View直接添加到根标签中,如果标签是普通元素,调用createViewFromTag 对该元素进行解析,调用rInflate解析 temp 根元素下的所有子 View,并且将这些子 View 都添加到temp下。
解析单个元素的createViewFromTag
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); }...... try { //跟进这个方法 View view = tryCreateView(parent, name, context, attrs); if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { //安卓系统自带View控件的创建解析 if (-1 == name.indexOf('.')) { view = onCreateView(context, parent, name, attrs); } else { //自定义控件创建解析 view = createView(context, name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } } ...... }
跟进tryCreateView
@UnsupportedAppUsage(trackingBug = 122360734) @Nullable public final View tryCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) { ...... View view; //通过onCreateView创建View if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, context, attrs); } else if (mFactory != null) { view = mFactory.onCreateView(name, context, attrs); } else { view = null; } ...... return view; }
最终我们的view都是通过createView方法创建的。
//通过反射机制构造 view对象@Nullablepublic 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); if (constructor != null && !verifyClassLoader(constructor)) { constructor = null; sConstructorMap.remove(name); } Class<? extends View> clazz = null; try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it //如果 prefix不为空,那么构造完整的 View 路径加载该类 clazz = Class.forName(prefix != null ? (prefix + name) : name, false, mContext.getClassLoader()).asSubclass(View.class); ...... //从class对象中获取构造函数 constructor = clazz.getConstructor(mConstructorSignature); constructor.setAccessible(true); //将构造函数存入缓存中 sConstructorMap.put(name, constructor); } else { ...... }...... try { //通过反射构造View final View view = constructor.newInstance(args); ......}
这只是单个View的xml解析,要把子View全部解析完,我们看rInflate。
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { //深度优先遍历算法 final int depth = parser.getDepth(); int type; boolean pendingRequestFocus = false; while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {...... } else { //元素名进行解析 final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); //递归调用进行解析 rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); } }...... }
来源地址:https://blog.csdn.net/weixin_46039528/article/details/132353252
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341