从ViewModel启动DialogFragment的推荐方法是什么?

13

我有一个在Recyclerview中的对象列表。当长按某个项目时,我想显示一个对话框,其中包含来自所选项目的数据。

Recyclerview每个项都使用数据绑定,我能够在长按时使用Log显示所选项目的数据。

然而,当尝试显示对话框时,需要获取Activity,但不建议在ViewModel对象中使用它。

那么我该如何显示对话框呢?

谢谢,Ove


从概念上讲,ViewModel 对我来说似乎不是启动对话框的正确位置。为了更清晰地实现它,我会将 RecyclerView.ViewHolder 传递到布局中,并在 ViewHolder 上设置一个方法,该方法触发 RecyclerView.Adapter 上的自定义监听器。然后,订阅该监听器的人(Activity/Fragment)可以启动对话框。这可能看起来有点绕,但我认为列表项的 ViewModel 不应该具有其环境的知识或控制权。 - Uli
@Ulli 从概念上讲,我同意。你能把这个加到答案里吗? - Ove Stoerholt
5个回答

8
从概念上讲,ViewModel 不是启动对话框的正确位置。为了更加清晰地实现它,我会将 RecyclerView.ViewHolder 传递到布局中,并在 ViewHolder 上添加一个方法,该方法触发 RecyclerView.Adapter 上的自定义监听器。然后,谁订阅了该监听器(Activity/Fragment)就可以启动对话框。这可能看起来有点绕,但我认为列表项的 ViewModel 不应该知道或控制其环境。
以下是一个示例。这是使用数据绑定和 ViewModel 处理 RecyclerView 项目点击的通用模式。这不是一个完整的示例,只是突出显示这个特定的模式的代码。
布局:
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    >
    <data>
    <variable
        name="viewHolder"
        type="com.example.ViewHolder"
        />
    <variable
        name="viewModel"
        type="com.example.ViewModel"
        />
    </data>

    <com.example.View
        android:layout_width="match_parent"
        android:layout_height="24dp"
        android:onClick="@{() -> viewHolder.onClick(viewModel)}"
        />
</layout>

Adapter:

class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    public interface SelectionListener {
        void onSelectionChanged(int newPosition, ViewModel viewModel);
    }

    private @NonNull WeakReference<SelectionListener> selectionListener =
            new WeakReference<>(null);

    public void setSelectionListener(@Nullable SelectionListener listener) {
        selectionListener = new WeakReference<>(listener);
    }

    public class ViewHolder extends RecyclerView.ViewHolder<ViewBinding> {
        ViewHolder(ViewBinding binding) {
            super(binding.getRoot());

            binding.setViewHolder(this);
            binding.setViewModel(new ViewModel());
        }

        public void onClick(ViewModel viewModel) {
            SelectionListener listener = selectionListener.get();
            if (listener != null) {
                listener.onSelectionChanged(getAdapterPosition(), viewModel);
            }
        }
    }
}

6
请参阅 Data Binding Library 的官方文档中的“Variables”部分。在那里,您可以找到一个名为context的变量,您可以使用它。
引用块中提到:“一个名为context的特殊变量根据需要在绑定表达式中生成。context的值是来自根View的getContext()的Context。如果有具有该名称的显式变量声明,context变量将被覆盖。”
基本上,您只需将其传递给另一个变量,例如viewModel,以从那里显示对话框。
android:onClick="@{v -> viewModel.showDialog(context)}"

在ViewModel模式中使用这样的上下文是否可以? - ashishdhiman2007
3
@ashishdhiman2007 我认为 ViewModel 内部上下文的真正问题在于当你持有一个引用时。 - Eury Pérez Beltré
1
这种做法滥用了 ViewModel 变成了一种 Presenter。ViewModel 负责您的 View 中的数据(可能是一个 Fragment 或带有关联 XML 数据绑定布局的 Activity)。 - Uli

1
我认为可以使用绑定适配器(binding adapter)来为RecyclerView设置适配器,并将适配器放入ViewModel中。然后将ViewModel作为Fragment的模型,通过在XML文件中调用setAdapter方法来传递适配器。请注意保留HTML标签。

0

你可以使用 item 的上下文,例如 itemView.getContext() 来显示 AlertDialog


0
Bayoudh 的提示让我走上了正确的路,但我发帖是为了把所有的东西串起来。下面是一个可点击的卡片视图。由于我的 ViewModel 没有对活动的引用,所以我们必须将相关视图作为参数传递。
<android.support.v7.widget.CardView
        android:id="@+id/cardviewContact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/text_margin_0.5x"
        android:layout_marginRight="@dimen/text_margin_0.5x"
        android:layout_marginTop="@dimen/text_margin_0.5x"
        android:background="?attr/selectableItemBackground"
        android:clickable="true"
        android:minHeight="50dp"
        card_view:cardCornerRadius="4dp"
        android:onClick="@{(view) -> viewModel.onClick(view)}" >

android:onClick="@{(view) -> viewModel.onClick(view)}"语句将当前视图作为参数传递,因此您可以在ViewModel中使用它来获取上下文,如Bayoudh所述,使用view.getContext()


您还可以使用方法引用 android:onClick="@{viewModel::onClick}" - tynn
@tynn 你是什么意思?这不是我在答案中所提到的吗?如果你没有将视图作为参数传递,那你是如何获取活动引用的呢? - Ove Stoerholt
这与你所写的等价,只是更简短而已。我只是想指出这一点。 - tynn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接