LayoutInflater

 

LayoutInflater的创建流程

基本上有几种创建方式:

  1. LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater()
  2. LayoutInflater.from(context);
  3. LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

其实都是一样的,如下所示:

 

android.app.Activity#attach
    mWindow = new PhoneWindow(this);
        mLayoutInflater = LayoutInflater.from(context);
            LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                android.view.ContextThemeWrapper#getSystemService(String name)
                    if (LAYOUT_INFLATER_SERVICE.equals(name)) {
                        if (mInflater == null) {
                            mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
                        }
                        return mInflater;
                    }

 

android.app.Activity#getLayoutInflater()

    由上可知获取的是上边创建的LayoutInflater

 

每个window都有一个LayoutInflater对象。

 

LayoutInflater的几个接口

下边的接口可以控制

 

/**
 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
 * to be inflated.
 * 
 */
public interface Filter {
    /**
     * Hook to allow clients of the LayoutInflater to restrict the set of Views 
     * that are allowed to be inflated.
     * 
     * @param clazz The class object for the View that is about to be inflated
     * 
     * @return True if this class is allowed to be inflated, or false otherwise
     */
    @SuppressWarnings("unchecked")
    boolean onLoadClass(Class clazz);
}

public interface Factory {
    /**
     * Hook you can supply that is called when inflating from a LayoutInflater.
     * You can use this to customize the tag names available in your XML
     * layout files.
     * 
     * <p>
     * Note that it is good practice to prefix these custom names with your
     * package (i.e., com.coolcompany.apps) to avoid conflicts with system
     * names.
     * 
     * @param name Tag name to be inflated.
     * @param context The context the view is being created in.
     * @param attrs Inflation attributes as specified in XML file.
     * 
     * @return View Newly created view. Return null for the default
     *         behavior.
     */
    public View onCreateView(String name, Context context, AttributeSet attrs);
}

public interface Factory2 extends Factory {
    /**
     * Version of {@link #onCreateView(String, Context, AttributeSet)}
     * that also supplies the parent that the view created view will be
     * placed in.
     *
     * @param parent The parent that the created view will be placed
     * in; <em>note that this may be null</em>.
     * @param name Tag name to be inflated.
     * @param context The context the view is being created in.
     * @param attrs Inflation attributes as specified in XML file.
     *
     * @return View Newly created view. Return null for the default
     *         behavior.
     */
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
}

 

 

Activity.setContentView

->PhoneWindow.setContentView

之后会进行布局的加载,即 使用LayoutInflater来读取布局xml,

mLayoutInflater.inflate(layoutResID, mContentParent);

 

所以如果我们想要监听或对view的创建进行干预,

就可以在Activity.setContentView之前对LayoutInflater设置Factory。

Factory2和Factory只会调用一个,如果设置了Factory2则不会调用Factory。

一般是想要对整个布局的某些view设置相同的字体等属性。

 

注意:一个LayoutInflater对象只能被设置一次Factory/Factory2,多次设置会IllegalStateException

对于AppCompatActivity,因为AppCompatActivity在onCreate中会设置Factory,

所以需要在调用super.onCreate(savedInstanceState);之前进行设置。

同时由于AppCompatActivity设置的factory目的是把系统view转换成appcompatxxxView,

所以,如果我们添加自己的factory后,需要在factory.onCreateView中手动调用

View view = mAppCompatDelegate.createView(parent, name, context, attrs);

 

LayoutInflater#inflate流程

Activity.setContentView

->PhoneWindow.setContentView

mLayoutInflater.inflate(layoutResID, mContentParent);

 

View LayoutInflater#inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

View LayoutInflater#createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr)
    View view;
    if (mFactory2 != null) {
        view = mFactory2.onCreateView(parent, name, context, attrs);
    } else if (mFactory != null) {
        view = mFactory.onCreateView(name, context, attrs);
    } else {
        view = null;
    }

    if (view == null && mPrivateFactory != null) {
        view = mPrivateFactory.onCreateView(parent, name, context, attrs);
    }

    if (view == null) {
        final Object lastContext = mConstructorArgs[0];
        mConstructorArgs[0] = context;
        try {
            if (-1 == name.indexOf('.')) {
                view = onCreateView(parent, name, attrs);
            } else {
                view = createView(name, null, attrs);
            }
        } finally {
            mConstructorArgs[0] = lastContext;
        }
    }

return view;

 

 

如果没有设置Factory2/Factory的话就会调用onCreateView/createView,

在createView中会使用Filter来对view进行进行过滤,控制按需加载。

posted @ 2019-10-20 00:52  嘤嘤嘤123  阅读(165)  评论(0编辑  收藏  举报