- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
6 LayoutInflaterCompat
LayoutInflater 我们用的很多,一般都是用来把布局填充成 View,它里面有两个方法:
// api 1 引入 setFactory(Factory factory) // api 11 引入 setFactory2(Factory2 factory)
这里面需要传入接口的实现类,如下:
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); }
通过上述接口可以看出来,新的 setFactory2(Factory2 factory)
比老的 setFactory(Factory factory)
在构建 View 的时候多传入了一个 Parent View。如果你想用 setFactory2(Factory factory)
需要实现带 Parent View 和不带 Parent View 的两个方法,比较复杂,所以 v4 包中的 LayoutInflaterCompat 就为我们提供了兼容性处理,先看用法:
LayoutInflater layoutInflater = getLayoutInflater(); LayoutInflaterCompat.setFactory(layoutInflater, new LayoutInflaterFactory() { @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { // name 是布局文件中 View 的名称,在这里可以坐很多操作,比如: // 给 TextView 设置字体。 // 把 TextView 变成 Button,如果需要的话。 // 修改后的 View,如果返回空则会调用 LayoutInflater 本身实例化 View 的方法,详情见 createViewFromTag 中 try 下面的逻辑。 return null; } });
举个例子: 现在我们用 AS 开发,一般默认是继承 AppCompatActivity
,在初始化的时候:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { // 获取兼容包的委托类 final AppCompatDelegate delegate = getDelegate(); // 安装 Factory delegate.installViewFactory(); delegate.onCreate(savedInstanceState); if (delegate.applyDayNight() && mThemeId != 0) { // If DayNight has been applied, we need to re-apply the theme for // the changes to take effect. On API 23+, we should bypass // setTheme(), which will no-op if the theme ID is identical to the // current theme ID. if (Build.VERSION.SDK_INT >= 23) { onApplyThemeResource(getTheme(), mThemeId, false); } else { setTheme(mThemeId); } } super.onCreate(savedInstanceState); }
在 AppCompatDelegate
的实现类 AppCompatDelegateImplV7
中,· installViewFactory()
:
@Override public void installViewFactory() { // 使用 LayoutInflaterCompat 进行兼容性适配 LayoutInflater layoutInflater = LayoutInflater.from(mContext); if (layoutInflater.getFactory() == null) { // 如果之前没有设置工厂,则自己实现接口然后传入,用来实现 AppCompat 特性,比如支持向低版本 tint 着色等新特性。 // 实际上它也是在工厂的实现类中用 AppCompatXXX 去替换 XXX,比如用 AppConpatTextView 替换 TextView 等诸如此类。 LayoutInflaterCompat.setFactory(layoutInflater, this); } else { // 如果已经设置了 Factory 并且不是当前类,则什么也不做,只打印日志。这样就会失去上述中 tint 等特性。 if (!(LayoutInflaterCompat.getFactory(layoutInflater) instanceof AppCompatDelegateImplV7)) { Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed" + " so we can not install AppCompat's"); } } }
上述方法实际上调用 LayoutInflaterCompat 的下面的方法:
public static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { IMPL.setFactory(inflater, factory); }
IMPL 是 LayoutInflaterCompat 中内部接口 LayoutInflaterCompatImpl 的实现类。这个接口有三个实现类:
- LayoutInflaterCompatImplBase。
- LayoutInflaterCompatImplV11。
- LayoutInflaterCompatImplV21。
static final LayoutInflaterCompatImpl IMPL; static { final int version = Build.VERSION.SDK_INT; if (version >= 21) { // 5.0 及其以上版本 IMPL = new LayoutInflaterCompatImplV21(); } else if (version >= 11) { // 3.0 及其以上版本 IMPL = new LayoutInflaterCompatImplV11(); } else { // 低于 3.0 版本 IMPL = new LayoutInflaterCompatImplBase(); } }
因此,调用 LayoutInflaterCompat 的 setFactory 方法,实际是调用对应版本的 IMPL 的 setFactory 方法。
1. 低于 3.0 版本
LayoutInflaterCompatImplBase 中调用的是 LayoutInflaterCompatBase 的 setFactory 方法,
static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { inflater.setFactory(factory != null ? new FactoryWrapper(factory) : null); } static LayoutInflaterFactory getFactory(LayoutInflater inflater) { LayoutInflater.Factory factory = inflater.getFactory(); if (factory instanceof FactoryWrapper) { return ((FactoryWrapper) factory).mDelegateFactory; } return null; }
其中, FactoryWrapper
是 LayoutInflaterCompatBase
的静态内部类:
class LayoutInflaterCompatBase { static class FactoryWrapper implements LayoutInflater.Factory { final LayoutInflaterFactory mDelegateFactory; FactoryWrapper(LayoutInflaterFactory delegateFactory) { mDelegateFactory = delegateFactory; } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { return mDelegateFactory.onCreateView(null, name, context, attrs); } public String toString() { return getClass().getName() + "{" + mDelegateFactory + "}"; } } static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { inflater.setFactory(factory != null ? new FactoryWrapper(factory) : null); } static LayoutInflaterFactory getFactory(LayoutInflater inflater) { LayoutInflater.Factory factory = inflater.getFactory(); if (factory instanceof FactoryWrapper) { return ((FactoryWrapper) factory).mDelegateFactory; } return null; } }
核心的方法就是 FactoryWrapper
的 onCreateView
,可以看到它调用的是带有 Parent View 参数的 onCreateView
方法,不过 Parent View 传的是 null。
2. 大于 3.0 小于 5.0
LayoutInflaterCompatImplV11 中调用的就是 LayoutInflaterCompatHC 的 setFactory 方法,LayoutInflaterCompatHC 中:
static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { // 如果传入的 Factory 不为空则包装一下,否则传空 final LayoutInflater.Factory2 factory2 = factory != null ? new FactoryWrapperHC(factory) : null; // 设置 Factory2. inflater.setFactory2(factory2); // 获取当前 Inflater 的 Factory。 final LayoutInflater.Factory f = inflater.getFactory(); // 如果属于 Factory2 则通过反射把 mFactory 赋值给 mFactory2。 if (f instanceof LayoutInflater.Factory2) { // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21). // We will now try and force set the merged factory to mFactory2 forceSetFactory2(inflater, (LayoutInflater.Factory2) f); } else { // 否则设置 mFactory2 为新创建的 Factory2。 // Else, we will force set the original wrapped Factory2 forceSetFactory2(inflater, factory2); } } // 利用反射修改 Factory2 /** * For APIs >= 11 && < 21, there was a framework bug that prevented a LayoutInflater's * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater * that already had a Factory2 registered. We work around that bug here. If we can't we * log an error. * 对于版本>- 11 并且 < 21,如果调用 cloneInContext 从 LayoutInflater 克隆一个 LayoutInflater,在 FrameWork 层有一个 bug 阻止了 LayoutInflater 的 Factory2 的合并,因为已经有一个 Factory2 被注册了,所在在此通过反射的方式去修改 Factory2。 */ static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) { if (!sCheckedField) { try { sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2"); sLayoutInflaterFactory2Field.setAccessible(true); } catch (NoSuchFieldException e) { Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class " + LayoutInflater.class.getName() + "; inflation may have unexpected results.", e); } sCheckedField = true; } if (sLayoutInflaterFactory2Field != null) { try { sLayoutInflaterFactory2Field.set(inflater, factory); } catch (IllegalAccessException e) { Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater " + inflater + "; inflation may have unexpected results.", e); } } }
bug 的具体产生原因见: LayoutInflater 在 Api 21 以下的 setFactory2 的 bug 是怎么产生的
3. 大于 5.0
static void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { inflater.setFactory2(factory != null ? new LayoutInflaterCompatHC.FactoryWrapperHC(factory) : null); }
这个其实没啥用,可以跟第二条合并的,但是最新的 v4 包没有改,但是在 api >= 20 的 Android 源码里面,其实已经这么做了:
static final LayoutInflaterCompatImpl IMPL; static { final int version = Build.VERSION.SDK_INT; if (version >= 11) { IMPL = new LayoutInflaterCompatImplV11(); } else { IMPL = new LayoutInflaterCompatImplBase(); } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论