4

I want to delete items from a recyclerview when pressing a view which is in the items.

The situation: Having a list made using a RecyclerView with a dataset and a ViewHolder which has a onClick on a view for deleting the item from the list, I need to know which item has been clicked on the ViewHolder and communicate it to the Adapter, because the Adapter has the dataset. Inside the ViewHolder I can know which item has been pressed with this method: getAdapterPosition(), so it seems to be easy to know which item to delete.

The problem: ViewHolder is a subclass inside the RecyclerView.Adapter, and the Adapter has the dataset, so I'm trying to understand which is the best way for the viewholder to communicate the Adapter that must delete the Item which has been clicked.

State of the art: I can see some questions here in Stack Overflow, some of them old, some of them more new, for example this: Android RecyclerView addition & removal of items but the solution doesn't clarify how to communicate the Adapter that must delete the item. Probably the user was doing it using static fields or something, but it is not a good way of achieving this. And I can't see other ways explained in other questions.

This is a sample from the official Recycler guide where you can see that the ViewHolder is nested inside the Adapter and hasn't access to it: https://developer.android.com/guide/topics/ui/layout/recyclerview.html

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        TextView v = (TextView) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.my_text_view, parent, false);
        ...
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);

    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
NullPointerException
  • 36,107
  • 79
  • 222
  • 382
  • y not set the click in `onBindViewHolder(..)` instead there we get the position as argument – Santanu Sur Feb 28 '18 at 19:56
  • @SantanuSur explain please, I don't understand what do you mean – NullPointerException Feb 28 '18 at 19:56
  • 1
    You can just put a `onClickListener` inside your `onBindViewHolder()` rather than inside your ViewHolder class. – karandeep singh Feb 28 '18 at 19:58
  • 1
    We can set the `clickListener` in `onBindViewHolder(ViewHolder holder, int position) { holder.setonClick(.....onClick(View view) { this.notifyItemRemoved(position); }}` wont it work ?? – Santanu Sur Feb 28 '18 at 20:00
  • The ViewHolder shouldn't be communicating anything. Its a lookup table for views, an optimization over repeatedly calling findViewById. It shouldn't have any logic in it at all. – Gabe Sechan Feb 28 '18 at 20:54

3 Answers3

2

I like the RecyclerView example by BigNerdRanch because they have a custom ViewHolder with a method (bindCrime() in the example) which is called from onBindViewHolder(). This method takes an item from the data list and sets all the Views depending on the item's content.

You could use a similar method, let's call it bindData(), and pass in not only the item from the data list but also [an anonymous instance of] a custom callback, let's call it ViewHolderCallback, which is in fact just some interface:

interface ViewHolderCallback{
    void itemWasClicked(int position);
}

Then onBindViewHolder() could look like this:

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element
    holder.bindData(mDataset[position], new ViewHolderCallback(){

        @Override
        public void itemWasClicked(int position){
             // remove the item 
        }
    });
}

...and bindData() like this:

void bindData(String text, ViewHolderCallback callback){
    mTextView.setText(text);
    itemView.setOnClickListener(new View.OnClickListener(){
         
        @Override
        public void onClick(View view){
             callback.itemWasClicked(getAdapterPosition()); 
        }
    });
}
Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
1

Finally i solved it by a simpler way, i just added the listener for deleting the item in the onBindViewHolder method of the adapter:

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    holder.trashImage.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            onDeleteClick(view.getContext(), position);
            notifyItemRemoved(position);
            notifyItemRangeChanged(position, mDataset.size());
        }
    });
}

worked like a charm

NullPointerException
  • 36,107
  • 79
  • 222
  • 382
0

I have been able to solve this using RecyclerView.ViewHolder#getBindingAdapter() doing something like this:

 public static class ViewHolder extends RecyclerView.ViewHolder 
    implements View.OnClickListener {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }

       @Override
       public void onClick(View row) {
           MyAdapter adapter = (MyAdapter) getBindingAdapter();
           adapter.doSomething();
       }
    }
fizzyh2o
  • 1,237
  • 9
  • 18