【问题标题】:How to use a LayoutInflater to inflate a layout with views pointing to a MutableContextWrapper?如何使用 LayoutInflater 为布局膨胀,其视图指向 MutableContextWrapper?
【发布时间】:2018-04-19 19:19:38
【问题描述】:

我有一个复杂的“浮动视图”层次结构,我在不同的活动之间移动。由于这些视图在活动之间共享,它们都应该有一个MutableContextWrapper,这将允许它们更改基本上下文。理论上我可以同时拥有多个浮动视图。

这是一个浮动视图的 XML 示例:

<com.test.views.SampleFloatingView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sample_floating_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <com.test.views.MinimizedImage
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:scaleType="center"/>

</com.test.views.SampleFloatingView>

我正在使用 MutableContextWrapper 扩展此 XML 并将其添加到将容纳它的 FrameLayout(因此我可以将它拖过屏幕),但我没有得到我需要的结果:

@NonNull
private FrameLayout createSampleFloatingView() {
    MutableContextWrapper mutableContextWrapper = new MutableContextWrapper(getActivity());
    final FrameLayout view = new FrameLayout(mutableContextWrapper);
    LayoutInflater layoutInflater = LayoutInflater.from(mutableContextWrapper);
    View floatingView = layoutInflater.inflate(R.layout.sample_floating_view, null, true).findViewById(R.id.sample_floating_view);
    setViewMutableContext(floatingView);
    view.addView(floatingView);
    return view;
}

private void setViewMutableContext(View view) {
    Context context = view.getContext();
    if (context instanceof MutableContextWrapper) {
        ((MutableContextWrapper) context).setBaseContext(getActivity());
    } else {
        throw new RuntimeException("View " + view + " doesn't have a MutableContextWrapper");
    }
    if (view instanceof ViewGroup) {
        int childCount = ((ViewGroup) view).getChildCount();
        for (int i = 0; i < childCount; i++) {
            setViewMutableContext(((ViewGroup) view).getChildAt(i));
        }
    }
}

问题在于,虽然FrameLayout 有一个MutableContextWrapper,但膨胀对象的上下文是创建视图的活动。这意味着上面的代码崩溃为SampleFloatingView 没有MutableContextWrapper。如果我删除此代码,一旦我将视图与原始活动断开连接并将其移至下一个活动,稍后就会发生内存泄漏。

此问题的一个明显解决方案是手动创建整个层次结构并在构造函数中传递MutableContextWrapper(就像我为FrameLayout 所做的那样),但出于明显的原因我宁愿避免它。

我的问题是,是否有更好的方法可以让我从 XML 中扩充视图并强制其上下文指向 MutableContextWrapper

【问题讨论】:

    标签: java android android-layout android-inflate mutable-context-wrapper


    【解决方案1】:

    我看到的问题的根本原因是默认 LayoutInflater 的工作方式(您从 LayoutInflater.from(context) 获得的那个)。它使用最后一个活动来夸大视图,这就是我得到不想要的结果的原因。我不能说我对这个解决方案太激动了,但它似乎奏效了。你要做的是创建一个自定义的 LayoutInflater 并设置它的Factory。根据我阅读的一些文档,如果您使用支持/兼容视图,我使用的解决方案可能存在兼容性问题,但我假设您也可以解决这些问题,以防遇到它。

    这是自定义 FactoryLayoutInflater 类的代码:

    @SuppressWarnings("rawtypes")
    public static class MyLayoutInflaterFactory implements LayoutInflater.Factory2 {
    
        private static final Class<?>[] constructorSignature = new Class[] {Context.class, AttributeSet.class };
    
        final MutableContextWrapper mutableContextWrapper;
    
        public MyLayoutInflaterFactory(MutableContextWrapper mutableContextWrapper) {
            this.mutableContextWrapper = mutableContextWrapper;
        }
    
        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            Constructor<? extends View> constructor = null;
            Class<? extends View> clazz = null;
            try {
                clazz = mutableContextWrapper.getClassLoader().loadClass(name).asSubclass(View.class);
                constructor = clazz.getConstructor(constructorSignature);
                constructor.setAccessible(true);
                return constructor.newInstance(mutableContextWrapper, attrs);
            } catch (Throwable t) {
                return null;
            }
        }
    
        @Override
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            // Perhaps this can be done better
            return onCreateView(name, context, attrs);
        }
    }
    
    public static class MyLayoutInflater extends LayoutInflater {
    
        protected MyLayoutInflater(Context context) {
            super(context);
        }
    
        @Override
        public LayoutInflater cloneInContext(Context newContext) {
            return new MyLayoutInflater(newContext);
        }
    }
    

    以及我使用它来膨胀浮动视图的方式:

    @NonNull
    private FrameLayout createSampleFloatingView() {
        MutableContextWrapper mutableContextWrapper = new MutableContextWrapper(getActivity());
        FrameLayout view = new FrameLayout(mutableContextWrapper);
        LayoutInflater layoutInflater = new MyLayoutInflater(mutableContextWrapper);
        LayoutInflaterCompat.setFactory2(layoutInflater, new MyLayoutInflaterFactory(mutableContextWrapper));
        layoutInflater.inflate(R.layout.sample_floating_view, view, true);
        return view;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多