如何监听inflate方法

为了监听 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的变化和系统的稳定性,选择第二种是最为合适的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值