安卓 - 源码 - LayoutInflater(一)

下一节 : 安卓 - 源码分析 - LayoutInflater(二)

先看一下LayoutInflater的说明:
/**
 * LayoutInflater用于解析并根据xml布局文件生成对应的View对象
 * 该类不应该被直接调用,而是通过相关方法获取:
 * Activity.getLayoutInflater / Context.getSystemService
 * 通过该方式,才能获取正确绑定上下文的LayoutInflater对象。
 * 
 * 如果你的View需要用额外的Factory去创建自定义的LayoutInflater,
 * 可以cloneInContext复制一个LayoutInflater,之后,
 * 通过setFactory方法将你的Factory添加到LayoutInflater中。
 * 
 * 由于性能原因,View的填充过程重度依赖于xml文件的预处理,
 * 所有目前LayoutInflater不支持在运行时直接解析未编译的xml。
 */

类说明为我们明确了3点:

不要直接创建该类,需要使用指定方法获取LayoutInflater对象。

可以通过Factory对LayoutInflater的填充过程进行自定义;
可以对LayoutInflater复制并重新设置Factory。

LayoutInflater只能解析编译过的xml布局文件。

其中,所有指定的方法,最终都是通过Context.getSystemService实现:

/*
 *内部实现:
 * getWindow().getLayoutInflater()
 * getWindow()得到的是Activity的mWindow对象:
 * mWindow = new PhoneWindow(this, window);
 * 即调用了PhoneWindow.getLayoutInflater(),其实现:
 * mLayoutInflater = LayoutInflater.from(context);
 */
Activity.getLayoutInflater();

/*
 * 内部实现:
 * LayoutInflater factory = LayoutInflater.from(context);
 * return factory.inflate(resource, root);
 */
View.inflate(Context context, @LayoutRes int resource, ViewGroup root);

/*
 * 内部实现:
 * LayoutInflater LayoutInflater = (LayoutInflater) context
 *         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 */
LayoutInflater.from(context);
然后,我们再看一下内部的几个接口和对象:
private boolean mFactorySet;       //是否调用过setFactory
private Factory mFactory;          //继承Factory的对象
private Factory2 mFactory2;        //继承Factory2的对象
private Factory2 mPrivateFactory;  //由系统维护的对象
private Filter mFilter;            //过滤器
/*
 * 过滤器
 * 如果要填充的View不被过滤器允许,将会抛出InflateException  
 */
public interface Filter {
    boolean onLoadClass(Class clazz);
}

/*
 * Factory
 * 3个参数分别为:
 * name    :需要填充的Tag名称,即View名称;
 * context :View所在的上下文环境;
 * attrs   :View在xml定义中定义的属性,
 * 当只处理个别View时,其他的View可以返回null。
 */
public interface Factory {
    public View onCreateView(String name, Context context, AttributeSet attrs);
} 

/*
 * Factory2
 * 重载onCreateView,增加了ViewParent参数。
 */
public interface Factory2 extends Factory {
    public View onCreateView(View parent, String name,
                             Context context, AttributeSet attrs);
}

关于过滤器Filter:
通过setFilter方法,可以为LayoutInflater设置自定义的过滤器

/*
 * 为LayoutInflater添加过滤器,当一个View不被过滤器允许时,
 * 在填充过程的inflate方法里将会抛出InflateException,
 * 新设置的filter将会覆盖之前所有设置的filter。
 */
public void setFilter(Filter filter) {
    mFilter = filter;
    if (filter != null) 
        mFilterMap = new HashMap<String, Boolean>();
}

注意新设置Filter的会覆盖旧的Filter,同时mFilterMap重新初始化。
mFilterMap的作用:记录Filter对不同View的过滤结果,当View进入过滤时,优先查找mFilterMap,避免Filter重复使用。

Filter的调用过程将会在后面的解析过程中分析。

关于Factory:
Factory可以让你对需要填充的View进行自定义。
Factory接口中,方法参数有name、context、attrs,Factory2则多了一个增加parent参数的重载。
应该关注的参数是:name和attrs:

name:
需要填充的View的类名,即对应xml中的Tag名。例如TextView。
你可以根据这个类名自定义View的创建,可以创建对应的View,也可以创建另外的View。
如:AppCompatActivity处理TextView,会返回AppCompatTextView而不是TextView。

attrs:
View属性,可以对这个属性进行修改,例如添加新属性,修改原有属性等。
通过修改attrs,可以轻松打造换肤功能。

使用setFactory方法,对LayoutInflater添加自定义Factory:

/*
 * 可以为LayoutInflater添加一个实现Factory接口的类去改变View创建的过程,
 * 这个Factory对象不能为null,且只能设置一次,设置后将不能进行变更,
 * 在解析xml布局文件过程中,解析到View时,Factory会被调用,
 * 如果Factory返回一个View,这个View则会被添加到控件层次中,
 * 如果返回空,将会调用的默认的onCreateView方法创建View。
 */
public void setFactory(Factory factory) {
    // 已设置与非空判定,判定不通过会抛异常
    if (mFactorySet) 
        throw new IllegalStateException("A factory has already "
            + "been set on this LayoutInflater");
    if (factory == null) 
        throw new NullPointerException("Given factory can not be null");

    // mFactorySet默认为false,这里将mFactorySet设为true
    // 再次调用setFactory将抛出上面的异常
    mFactorySet = true;
    
    // 如果原有的LayoutInflater带有mFactory
    // 需要保留原有mFactory,下面再去介绍
    if (mFactory == null) 
        mFactory = factory;
    else
        mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}

也可以使用setFactory2,设置一个Factory2对象。
setFactory2和setFactory方法一样,但同时会将Factory2设置为mFactory和mFactory2:

public void setFactory2(Factory2 factory) {
    ...
    if (mFactory == null) {
        mFactory = mFactory2 = factory;
    } else {
        mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
    }
}

上面涉及到了一个FactoryMerger类

private static class FactoryMerger implements Factory2 {
    private final Factory mF1, mF2;
    private final Factory2 mF12, mF22;
        
    FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
        mF1 = f1; mF2 = f2; mF12 = f12; mF22 = f22;
    }
        
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        View v = mF1.onCreateView(name, context, attrs);
        if (v != null) return v;
        return mF2.onCreateView(name, context, attrs);
    }

    public View onCreateView(View parent, String name, 
                             Context context, AttributeSet attrs) {
        View v = mF12 != null ? 
                    mF12.onCreateView(parent, name, context, attrs) :
                    mF1.onCreateView(name, context, attrs);
        if (v != null) return v;
        return mF22 != null ? 
                   mF22.onCreateView(parent, name, context, attrs) : 
                   mF2.onCreateView(name, context, attrs);
    }
}

这个类实现了Factory2接口,即是Factory的实现类,
它的作用,其实就是保留上一个Factory作为默认View创建方法。
例如:
现在有一个LayoutInflater:

LayoutInflater inflater;
inflater.setFactory((name, context, attrs) -> {
    if(TextUtils.equals(name, TextView.class.getSimpleName()))
        return new EditText(context,attrs);
    return null;
})

这时候,想得到一个对EditView做处理,同时保留原来Factory行为的新LayoutInflater:

// 复制原有的LayoutInflater
LayoutInflater newInflater = inflater.cloneInContext(context); 
newInflater.setFactory((name, context, attrs) -> {
    if(TextUtils.equals(name, EditText.class.getSimpleName()))
        return new TextView(context,attrs);
    return null;
})

由于newInflater已经存在一个Factory,所以setFactory会为我们创建一个FactoryMerger对象:

public void setFactory(Factory factory) {
    if (mFactory == null) 
        mFactory = factory;
    else
        mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}

当填充EditText时,新的Factory会返回一个TextView对象;
而填充TextView时,则会返回null,这时会调用原有的Factory进行解析,即会返回一个EditText对象。

余下的分析放在
下一节 : 安卓 - 源码分析 - LayoutInflater(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章