为了监听 inflate 方法,所以我们必须重新设置 Factory。但是 Factory不能重复设置,下面的方式就是为了解决这个问题
第一种方式: 通过反射的方式设置factory
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mFactorySet;
@UnsupportedAppUsage
private Factory2 mFactory2;
public static void setFactory2( @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
inflater.setFactory2(factory);
if (Build.VERSION.SDK_INT < 21) {
final LayoutInflater.Factory f = inflater.getFactory();
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 { // Else, we will force set the original wrapped Factory2
forceSetFactory2(inflater, factory);
}
}
}
可以看出为了解决不能重复设置问题,只需要把 mFactorySet 设置为false即可。但是在Android P及其之后就不支持了,无法反射使用。
再看还有一个变量就是 mFactory2 ,这个虽然也写了 @UnsupportedAppUsage 但是并没有 版本限制,尝试一下,果然可以,不会崩溃。
使用不被允许的API本身是有风险的,不利于系统的稳定。所以应该寻找更合适的方式。
第二种方式:自定义 AppCompatDelegate
源码分析:
@ContentView
public AppCompatActivity(@LayoutRes int contentLayoutId) {
super(contentLayoutId);
initDelegate();
}
private void initDelegate() {
...
addOnContextAvailableListener(new OnContextAvailableListener() {
@Override public void onContextAvailable(@NonNull Context context) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(getSavedStateRegistry() .consumeRestoredStateForKey(DELEGATE_TAG));
}});
}
// 创建AppCompatDelegate
public static AppCompatDelegate create(@NonNull Activity activity, @Nullable AppCompatCallback callback) {
return new AppCompatDelegateImpl(activity, callback);
}
从上面源码中可以看出其实就是在 AppCompatDelegate 中设置的factory,所以我们只需要重写 AppCompatDelegateImpl , installViewFactory 方法设置为空,就可以设置自己的 factory了。但是 它的构造方法不是公开的,而是Standard ,无法直接集成
AppCompatDelegateImpl(Activity activity, AppCompatCallback callback) {
this(activity, null, callback, activity);
}
所以我们就需要在同一包名下去创建新的delegate 才能继承于它。新建包名 androidx.appcompat.app
package androidx.appcompat.app;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.view.Window;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author: HSW
* @data: 2023/7/3
* @desc:
*/
@SuppressLint("RestrictedApi")
public class CustomDelegate extends AppCompatDelegateImpl {
private static Map<Activity, WeakReference<AppCompatDelegate>> sDelegateMap = new WeakHashMap<>();
public static AppCompatDelegate get(Activity activity, AppCompatCallback callback) {
WeakReference<AppCompatDelegate> delegateRef = sDelegateMap.get(activity);
AppCompatDelegate delegate = (delegateRef == null ? null : delegateRef.get());
if (delegate == null) {
delegate = new CustomDelegate(activity, activity.getWindow(), callback);
sDelegateMap.put(activity, new WeakReference<>(delegate));
}
return delegate;
}
private CustomDelegate(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
@Override
public void installViewFactory() {
}
}
使用
Activity 中重写 方法
override fun getDelegate(): AppCompatDelegate {
return CustomDelegate.get(this, this)
}
Application 中注册activity生命周期
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
installLayoutFactory(activity)
}
....
})
然后就可以监听/ 修改 inflate 了
private fun installLayoutFactory(context: Context) {
try {
val layoutInflater = LayoutInflater.from(context)
LayoutInflaterCompat.setFactory2(layoutInflater, CustomLayoutInflaterFactory())
} catch (e: Exception) {
e.printStackTrace()
}
// 通过反射的方式,不建议使用
// val layoutInflater = LayoutInflater.from(context)
// try {
// val field = LayoutInflater::class.java.getDeclaredField("mFactory2")
// field.isAccessible = true
// field.set(layoutInflater, CustomLayoutInflaterFactory())
// } catch (e: NoSuchFieldException) {
// e.printStackTrace()
// } catch (e: IllegalArgumentException) {
// e.printStackTrace()
// } catch (e: IllegalAccessException) {
// e.printStackTrace()
// }
}
如果为了适配android sdk的变化和系统的稳定性,选择第二种是最为合适的