LayoutInflater的创建流程
基本上有几种创建方式:
-
LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater()
-
LayoutInflater.from(context);
-
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进行进行过滤,控制按需加载。