0

我有一个复杂的“浮动视图”层次结构,我在不同的活动之间移动。由于这些视图是跨活动共享的,它们都应该有一个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>

我正在使用 a 对这个 XML 进行膨胀MutableContextWrapper并将其添加到将容纳它的 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膨胀对象的上下文是创建视图的 Activity。这意味着上面的代码因为SampleFloatingView没有MutableContextWrapper. 如果我删除此代码,一旦我将视图与原始活动断开连接并将其移动到下一个活动,稍后就会发生内存泄漏。

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

我的问题是是否有更好的方法可以让我从 XML 扩展视图并强制它们的上下文指向MutableContextWrapper?

4

1 回答 1

0

我看到的问题的根本原因是默认 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;
}
于 2018-04-20T15:18:17.930 回答