I have a list objects in a Recyclerview
. When long-pressing an item I want to show a dialog with data from the item clicked.
The Recyclerview
Conceptually a ViewModel strikes me as the wrong place to launch a Dialog from. To do it more cleanly I would pass the RecyclerView.ViewHolder into the layout, and have a method on the ViewHolder that triggers a custom listener on your RecyclerView.Adapter. Then whoever subscribes to that listener (Activity/Fragment) can launch the Dialog. May seem a little roundabout, but I don't think a ViewModel of a list item should have knowledge or control of its environment.
Here is an example. This is a general pattern for handling RecyclerView item clicks with data binding and a ViewModel. This is not a complete example, just the code to highlight this specific pattern.
Layout:
<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);
}
}
}
}
I think using a binding adapter for a recyclerview and put the adapter n ViewModel, then make the viewmodel is the model of fragment and passing adapter for the setAdapter method in xml itself.
So you can use the context of item for example itemView.getContext()
to show AlertDialog
The hint from Bayoudh led me in the right direction, but I'm posting this to put the pieces together. Below is a cardview that is clickable. Since my ViewModel
holds no reference to the activity we have to pass the view in question as a parameter.
<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)}" >
The android:onClick="@{(view) -> viewModel.onClick(view)}"
statement takes the current view as a parameter so you can use it in the ViewModel to get context with view.getContext()
as Bayoudh states.
See the Variables section of the official documentation of the Data Binding Library. There you find a variable context
you can use.
A special variable named context is generated for use in binding expressions as needed. The value for context is the Context from the root View's
getContext()
. The context variable will be overridden by an explicit variable declaration with that name.
Basically you could just pass it to another variable like the viewModel
to show the dialog from there.
android:onClick="@{v -> viewModel.showDialog(context)}"