AsyncLayoutInflater不支持Factory/Factory2导致视图着色等属性丢失如何解决
你遇到的自定义属性失效问题本质是:AppCompat、MaterialComponents组件的扩展属性(比如app:tint、app:boxStrokeColor、app:drawableTint、自定义样式等)都依赖LayoutInflater.Factory2在布局inflate阶段完成解析和应用,官方AsyncLayoutInflater明确禁用了Factory相关支持,inflate时只会调用View的原生构造方法,所有自定义扩展属性都会被忽略,才会出现tint丢失、Spinner背景变默认黑色等问题。
按推荐优先级排序如下:
方案1:改用ViewBinding异步inflate(最推荐,无兼容性问题)
ViewBinding的自动生成代码会直接把XML中定义的所有属性(包括系统属性、自定义扩展属性、tint、背景、样式等)通过Java代码赋值,完全不依赖LayoutInflater的Factory机制,天生适配异步场景,性能和原生AsyncLayoutInflater相当,无需修改原有布局。
改造后代码示例:
// 异步线程执行inflate操作 CompletableFuture.supplyAsync(() -> { return LayoutSecondBind.inflate(LayoutInflater.from(context)); }).thenAcceptAsync(secondBind -> { // 主线程将视图添加到容器 bind.container.add(secondBind.getRoot()); // 所有XML属性已自动应用,无需手动补充设置 }, ContextCompat.getMainExecutor(context));
方案2:自定义支持Factory2的AsyncLayoutInflater
如果你希望保留原有AsyncLayoutInflater的调用方式,可以反射修改其内部LayoutInflater的配置,注入AppCompat的Factory2来解析扩展属性:
public class AppCompatAsyncLayoutInflater extends AsyncLayoutInflater { public AppCompatAsyncLayoutInflater(@NonNull AppCompatActivity activity) { super(activity); try { // 反射获取AsyncLayoutInflater内部持有的LayoutInflater实例 Field inflaterField = AsyncLayoutInflater.class.getDeclaredField("mInflater"); inflaterField.setAccessible(true); LayoutInflater inflater = (LayoutInflater) inflaterField.get(this); // 注入和主线程一致的AppCompat Factory2 inflater.setFactory2(activity.getDelegate().getFactory2()); } catch (Exception e) { e.printStackTrace(); } } }
使用时直接用AppCompatAsyncLayoutInflater替换原有AsyncLayoutInflater即可,大部分扩展属性可正常解析。
注意:该方案依赖反射,且依然不支持包含Fragment的布局inflate,符合官方原始限制
方案3:递归批量补全属性(临时救急)
如果前两个方案无法落地,可以写通用工具方法递归遍历inflate后的视图树,批量补全需要的tint、背景等属性,仅适合改动量较小的场景。
内容的提问来源于stack exchange,提问作者Monsef Khalfaoui

