- 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();
}
}绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论