1

Hello this is my first question on stackoverflow. I have seen this question, but none of their solutions really helped me.

How to update RecyclerView Adapter Data?

  1. My problem is, that I want to remove the view holder and not recycle it. When pressing the delete button, the viewholder is not deleted but recycled, which means that the counter is not reinstantiated with "1", but with the old value. For example if I delete the first item with a counter of 7, and add a new item into the empty recycler view, then the counter is initialized with 7 instead of 1.

  2. When deleting a viewholder with items.remove(position); notifyItemRemoved(position);I must use notifyItemRangeChanged(position, items.size());, because position is not updated. For Example: When deleting the first element, the first element of the recyclerview with position 0 is deleted. When deleting the first item of the recyclerview once again, the second item is deleted at position 1. So I have to use notifyItemRangeChanged(position, items.size());. But is there any cleaner way of notifying my adapter about the correct positions of the items in the recycler view?

public class ItemsRecViewAdapter extends RecyclerView.Adapter<ItemsRecViewAdapter.ViewHolder> {

private Context context;
private ArrayList<Item> items;

public ItemsRecViewAdapter(Context context) {
    this.context = context;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_item,parent,false);
    ViewHolder holder = new ViewHolder(view);
    return holder;
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    holder.groceryItem.setText(items.get(position).getName());
    holder.parent.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(context, items.get(position).getName()+" selected.", Toast.LENGTH_SHORT).show();
        }
    });
    holder.plusButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int counter = Integer.parseInt(holder.counter.getText().toString());
            counter++;
            holder.counter.setText(String.valueOf(counter));
            notifyDataSetChanged();
        }
    });
    holder.minusButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int counter = Integer.parseInt(holder.counter.getText().toString());
            if (counter == 1){
                Toast.makeText(context, items.get(position).getName()+" deleted.", Toast.LENGTH_SHORT).show();
                items.remove(position);
                notifyItemRemoved(position);
                notifyItemRangeChanged(position, items.size());
            }
            else{
                counter--;
                holder.counter.setText(String.valueOf(counter));
                notifyDataSetChanged();
            }
        }
    });
    holder.deleteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(context, items.get(position).getName()+" deleted.", Toast.LENGTH_SHORT).show();
            items.remove(position);
            notifyItemRemoved(position);
            notifyItemRangeChanged(position, items.size());
        }
    });
}

@Override
public int getItemCount() {
    return items.size();
}

public void setItems(ArrayList<Item> items) {
    this.items = items;
    notifyDataSetChanged();
}

public class ViewHolder extends RecyclerView.ViewHolder{
    private TextView groceryItem, counter;
    private LinearLayout parent;
    private ImageButton plusButton, minusButton, deleteButton;

    public ViewHolder(@NonNull View itemView) {
        super(itemView);
        groceryItem = itemView.findViewById(R.id.groceryItem);
        counter = itemView.findViewById(R.id.counter);
        plusButton = itemView.findViewById(R.id.plusButton);
        minusButton = itemView.findViewById(R.id.minusButton);
        deleteButton = itemView.findViewById(R.id.deleteButton);
        parent = itemView.findViewById(R.id.parent);
    }
}

}

And this is my MainActivity class:

public class MainActivity extends AppCompatActivity {

private EditText mEditText;
private Button mAddButton;
private RecyclerView mRecView;


private ImageButton mPlusButton;
private TextView mCounter;
private ArrayList<Item> items;
ItemsRecViewAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mEditText = findViewById(R.id.item_editText);
    mAddButton = findViewById(R.id.add_button);
    adapter = new ItemsRecViewAdapter(MainActivity.this);
    mRecView = findViewById(R.id.itemsRecyclerView);
    mRecView.setAdapter(adapter);
    mRecView.setLayoutManager(new LinearLayoutManager(MainActivity.this,RecyclerView.VERTICAL,false));

    items = new ArrayList<>();
    adapter.setItems(items);


    mAddButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!mEditText.getText().toString().equals("")) {
                Item item = new Item(mEditText.getText().toString());
                items.add(item);
                adapter.setItems(items);
                if (mEditText.length() > 0) {
                    TextKeyListener.clear(mEditText.getText());
                }
            }
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_menu,menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()){
        case R.id.deleteAllBtn:
            items.clear();
            adapter.notifyDataSetChanged();
            Toast.makeText(this, "All items deleted...",Toast.LENGTH_SHORT).show();
            break;
    }
    return true;
}

}

I am enjoying this very much, and I would be very thankful for any tips.

1 Answers1

0

ViewHolders are not suitable for storing any data because they are volatile and re-used between different items. Trying to "remove" viewholder is twisted way of trying to fix bad implementation.

You should either have counter as field in your Item that you can modify or hold an array of counters you can match with your items. Then you need to use that data during onBindViewHolder. There's no need to call notifyDatasetChanged after altering one field either because you're not altering the structure of your list.

You're also making mistake of storing hard reference to position argument in your listeners - you should be using holder.getBindingAdapterPosition() instead which returns current item position so you won't need to notify item range changed anymore - and you won't need to instantiate them every time you bind viewholder either, they can be created only once in that case.

This is how you can clean up the adapter by adding counter field to your Item:

public class ItemsRecViewAdapter extends RecyclerView.Adapter<ItemsRecViewAdapter.ViewHolder> {

    private Context context;
    private ArrayList<Item> items;

    public ItemsRecViewAdapter(Context context) {
        this.context = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_item,parent,false);
        ViewHolder holder = new ViewHolder(view);
        holder.parent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, items.get(holder.getBindingAdapterPosition()).getName()+" selected.", Toast.LENGTH_SHORT).show();
            }
        });
        holder.plusButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Item item = items.get(holder.getBindingAdapterPosition());
                item.setCounter(item.getCounter()+1);
                holder.counter.setText(String.valueOf(item.getCounter()));
            }
        });
        holder.minusButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Item item = items.get(holder.getBindingAdapterPosition());
                if (item.getCounter() == 1){
                    Toast.makeText(context, item.getName()+" deleted.", Toast.LENGTH_SHORT).show();
                    items.remove(holder.getBindingAdapterPosition());
                    notifyItemRemoved(holder.getBindingAdapterPosition());
                }
                else{
                    item.setCounter(item.getCounter()-1);
                    holder.counter.setText(String.valueOf(item.getCounter()));
                }
            }
        });
        holder.deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Item item = items.get(holder.getBindingAdapterPosition());
                Toast.makeText(context, item.getName()+" deleted.", Toast.LENGTH_SHORT).show();
                items.remove(holder.getBindingAdapterPosition());
                notifyItemRemoved(holder.getBindingAdapterPosition());
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Item item = items.get(position);
        holder.groceryItem.setText(item.getName());
        holder.counter.setText(String.valueOf(item.getCounter()));
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void setItems(ArrayList<Item> items) {
        this.items = items;
        notifyDataSetChanged();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        private TextView groceryItem, counter;
        private LinearLayout parent;
        private ImageButton plusButton, minusButton, deleteButton;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            groceryItem = itemView.findViewById(R.id.groceryItem);
            counter = itemView.findViewById(R.id.counter);
            plusButton = itemView.findViewById(R.id.plusButton);
            minusButton = itemView.findViewById(R.id.minusButton);
            deleteButton = itemView.findViewById(R.id.deleteButton);
            parent = itemView.findViewById(R.id.parent);
        }
    }
}
Pawel
  • 10,706
  • 1
  • 20
  • 27
  • Although StackOverflow says I should avoid comments like thanks, I want to sincerely thank you anyway for your answer. I got it now. – FacelessTimma Mar 14 '21 at 19:07