Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is the position of a RecyclerView adapter related to the index of its dataset?

I thought they were the same, but they're not. The following code gives an indexOutOfBounds exception when I try to access the "position" index of my dataset, in this case a list of a model I created called Task:

public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder>   {

private List<Task> taskList;
private TaskAdapter thisAdapter = this;

// cache of views to reduce number of findViewById calls
public static class TaskViewHolder extends RecyclerView.ViewHolder {
    protected TextView taskTV;
    protected ImageView closeBtn;

    public TaskViewHolder(View v) {
        super(v);
        taskTV = (TextView)v.findViewById(R.id.taskDesc);
        closeBtn = (ImageView)v.findViewById(R.id.xImg);
    }
}


public TaskAdapter(List<Task> tasks) {
    if(tasks == null)
        throw new IllegalArgumentException("tasks cannot be null");
    taskList = tasks;
}


// onBindViewHolder binds a model to a viewholder
@Override
public void onBindViewHolder(TaskViewHolder taskViewHolder, int pos) {
    final int position = pos;
    Task currTask = taskList.get(pos);
    taskViewHolder.taskTV.setText(currTask.getDescription());

    **taskViewHolder.closeBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("TRACE", "Closing task at position " + position);
            // delete from SQLite DB
            Task taskToDel = taskList.get(position);
            taskToDel.delete();
            // updating UI
            taskList.remove(position);
            thisAdapter.notifyItemRemoved(position);
        }
    });**
}

@Override
public int getItemCount() {
    //Log.d("TRACE", taskList.size() + " tasks in DB");
    return taskList.size();
}


// inflates row to create a viewHolder
@Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int pos) {
    View itemView = LayoutInflater.from(parent.getContext()).
                                   inflate(R.layout.list_item, parent, false);
    Task currTask = taskList.get(pos);

    //itemView.setBackgroundColor(Color.parseColor(currTask.getColor()));
    return new TaskViewHolder(itemView);
}
}

Deleting from my recyclerview gives unexpected results sometimes. Sometimes the element ahead of the one clicked is deleted, other times an indexOutOfBounds exception occurs at "taskList.get(position)".

Reading https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html and https://developer.android.com/training/material/lists-cards.html did not give me any more insight into why this was happening and how to fix it.

It looks like RecyclerView recycles the rows, but I wouldn't expect an indexoutofbounds exception using a smaller subset of numbers to index my list.

like image 854
HukeLau_DABA Avatar asked Nov 02 '14 01:11

HukeLau_DABA


People also ask

What is the relationship between RecyclerView Adapter and RecyclerView ViewHolder?

ViewHolder is not bound to an item or the given RecyclerView. Adapter is not part of this Adapter (if this Adapter merges other adapters).

What RecyclerView Adapter method is used for binding data to the view?

RecyclerView has an Adapter with two very important methods that we implement to bind data: RecyclerView. ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

Which RecyclerView method is called when getting the size of the data set?

getItemCount() : RecyclerView calls this method to get the size of the data set.


1 Answers

Like yigit said, RecyclerView works like that:

A B C D

and you add item X via

mItems.add(1, X);
notifyItemInserted(1, 1);

you get

A X B C D

Using holder.getAdapterPosition() in onClickListener() will give you the right item from dataset to be removed, not the "static" view position. Here's the doc about it onBindViewHolder

like image 102
Ely Dantas Avatar answered Oct 24 '22 01:10

Ely Dantas