在Android中,Factory2 是 LayoutInflater 的一个接口。它的核心作用是拦截并接管布局文件(XML)中每一个视图(View)的创建过程。
- 核心作用 :你可以把它理解为一个"视图创建拦截器 "。当系统解析XML布局、准备将标签名(如
<TextView>)实例化为真正的View对象时,会优先询问你设置的Factory2:"你想怎么创建这个View?"。 - 工作原理 :
LayoutInflater内部有一个createViewFromTag方法。在创建View时,它会首先检查是否设置了Factory2。如果设置了,就会调用Factory2.onCreateView()方法。如果这个方法返回了一个有效的View对象,则直接使用;如果返回null,则系统会按照默认流程去创建View。
🔧 主要用途与应用场景
利用这个"拦截"能力,Factory2 可以实现许多强大的功能:
| 用途场景 | 功能说明 | 示例 / 实现关键 |
|---|---|---|
| 全局替换系统组件 | 将XML中的标准控件自动替换为兼容性或自定义版本。 | AppCompatActivity 用它自动将 <Button> 替换为 AppCompatButton。 |
| 统一修改视图属性 | 为所有特定类型的View(如所有TextView)动态添加统一样式或行为。 | 拦截 TextView 创建,设置全局默认字体、文字颜色或背景。 |
| 实现无侵入的监控 | 在不修改业务代码的情况下,为所有View添加性能监控或事件埋点。 | 创建View时,为其包装一层代理,统一监听点击、测量等事件。 |
| 动态主题/换肤 | 根据运行时主题,动态替换视图对应的资源。 | 根据主题状态,在创建View时选择不同的资源ID或构造方式。 |
💡 如何使用与关键代码
使用 Factory2 的基本步骤如下,但需要注意与 AppCompatActivity 的兼容性问题。
1. 基本使用模式 你需要实现 LayoutInflater.Factory2 接口,并在 Activity.onCreate() 的 super.onCreate() 之前进行设置。
java
public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// 必须在 super.onCreate 之前设置
LayoutInflater.from(this).setFactory2(new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// 1. 在这里进行拦截和自定义
if ("TextView".equals(name)) {
return new MyCustomTextView(context, attrs); // 返回你的自定义View
}
// 2. 对于不想处理的View,返回null,让系统或其他Factory处理
return null;
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
// 此方法为继承自Factory的接口,通常通过上一个方法实现即可
return onCreateView(null, name, context, attrs);
}
});
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2. 与 AppCompatActivity 的兼容性问题(关键!) 如果你继承 AppCompatActivity,会发现上述方法可能无效或报错。这是因为 AppCompatActivity 自己也在 onCreate 中设置了一个 Factory2 来实现向后兼容(如将 Button 替换为 AppCompatButton),而 LayoutInflater 的 Factory 只能被设置一次。
解决方案:代理模式 正确的方式是创建一个代理,将你不处理的View创建请求,转发给 AppCompatDelegate 去处理。
java
public class MyAppCompatActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
LayoutInflater.from(this).setFactory2(new LayoutInflater.Factory2() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// 1. 自己的逻辑:拦截TextView
if ("TextView".equals(name)) {
return new MyCustomTextView(context, attrs);
}
// 2. 重要:将其他View的创建代理给AppCompat
AppCompatDelegate delegate = getDelegate();
View view = delegate.createView(parent, name, context, attrs);
if (view != null) {
return view;
}
// 3. 如果AppCompat也没处理,返回null走默认流程
return null;
}
// ... 省略另一个 onCreateView 方法
});
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
⚠️ 重要注意事项
- 只能设置一次 :
LayoutInflater的setFactory或setFactory2只能成功调用一次 ,重复设置会抛出IllegalStateException。这就是为什么在AppCompatActivity中需要特殊处理。 - 时序非常重要 :自定义的
Factory2必须在super.onCreate(savedInstanceState)之前设置 ,否则可能会因为AppCompatActivity已抢先设置而失败。 - 优先使用 Factory2 :
Factory2继承自Factory,并多了一个parent参数,能提供更多上下文信息。在 API 11+ 的应用中应优先使用setFactory2。