Android开发笔记

2.2 LayoutInflater 加载布局文件源码

2018-09-10  本文已影响20人  littlezan

LayoutInflater 加载布局文件源码

LayoutInflater是一个用于将xml布局文件加载为View或者ViewGroup对象的工具,我们可以称之为布局加载器。

获取LayoutInflater实例

有3种方式获取到取LayoutInflater实例

第一种

(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

第二种 是针对第一种的封装

LayoutInflater.from(context); 

第三种 在Activity中,封装了获取LayoutInflater的接口

Activity.getLayoutInflater()

LayoutInflater 的使用创建View

创建View,有4个重载方式

一般常用的是前2种

方式 结果
inflate( resourceId, rootView) 将解析布局文件生成的View,添加到rootView中
addView(parseView, layoutParams)
inflate( resourceId, null) 创建不带LayoutParams的View
布局文件里顶层空间的LayoutParams将会失效
inflate( resourceId, null, false) 同上
inflate( resourceId, rootView, false) 创建带LayoutParams的View
布局文件里顶层空间的LayoutParams有效果
但是不会加入到rootView中
inflate( resourceId, rootView, true) 同第一个

源码解析

LayoutInflate
|
|-inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
        |
        |-final View temp = createViewFromTag(root, name, inflaterContext, attrs); 
        |创建根节点View
        |
        |-if(root != nul) root.generateLayoutParams(attrs);
        |根据根节点属性创建,根节点View的LayoutParams
        |
        |-if(!attachToRoot) temp.setLayoutParams(params); 
        |如果添加到rootView,设置布局文件根View的LayoutParams属性
        |
        |- rInflateChildren(parser, temp, attrs, true);
        |加载布局文件中子View
        |
        |-if (root != null && attachToRoot) root.addView(temp, params);
        |将布局文件中根节点View添加到rootView中
        
这里解释了,inflate重载方法中root和attachToRoot这两个参数的含义



LayoutInflate
|
|-View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr)
|根据布局文件节点名称创建View
    |
    |
    |-if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, context, attrs);
    |1.优先mFactory2创建view
    |
    |-if (mFactory != null)  view = mFactory.onCreateView(name, context, attrs);
    |2.mFactory创建view
    |
    |-if (view == null && mPrivateFactory != null)  view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    |3.mPrivateFactory创建view
    |
    |-view = createView(name, null, attrs);
    |4.直接通过反射方式创建View
        |
        |-clazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class);
        |-constructor = clazz.getConstructor(mConstructorSignature);
        |-final View view = constructor.newInstance(args);
        |通过反射创建View

如果继承自AppCompateActivity,mFactory2!=null,创建AppCompateTextView,等对象
                    



LayoutInflate
|
|-rInflateChildren(parser, temp, attrs, true);
|加载子View
    |
    |-final View view = createViewFromTag(parent, name, context, attrs);
    |创建子View
    |
    |-final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
    |生成子View LayoutParmas属性
    |
    |-rInflateChildren(parser, view, attrs, true);
    |创建该节点下子View
    |
    |-viewGroup.addView(view, params);
    |将子View添加到父View中
    |
    |-if (finishInflate) parent.onFinishInflate();
    |结束递归rInflateChildren
    

rInflateChildren函数是一个根据布局文件的节点递归遍历生生成子View,并添加到View树中,
当某个节点下面的所有子节点View解析生成完成后,才会调起onFinishInflate回调


利用LayoutInflate替换布局文件中默认View

  1. 实现LayoutInflater.Factory2接口
public class TestLayoutFactory implements LayoutInflater.Factory2 {
    private static final String TAG = "TestLayoutFactory";
    private final AppCompatDelegate delegate;

    public TestLayoutFactory(AppCompatDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        if (TextUtils.equals("TextView", name)) {
            return new CustomTextView(context, attrs);
        }
        return delegate.createView(parent, name, context, attrs);
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return null;
    }
}
  1. 在AppCompatActivity的onCreate()方法中设置LayoutInfalte.Factory
    override fun onCreate(savedInstanceState: Bundle?) {
        LayoutInflaterCompat.setFactory2(layoutInflater, TestLayoutFactory(getDelegate()))
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_layout_factory)
    }

一定要在super.onCreate(savedInstanceState) 代码之前设置,否则会报错:

Caused by: java.lang.IllegalStateException: A factory has already been set on this LayoutInflater

因为在调用父类的onCreate()方法中,已经创建了LayoutInfalte.Factory,再次创建的时候,就会报错了

上一篇下一篇

猜你喜欢

热点阅读