Skip to content Skip to sidebar Skip to footer

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

Solution 1:

RecyclerView does not rebind views when their positions change (for obvious performance reasons). For example, if your data set looks like this:

AB C D

and you add item X via

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

to get

A X B C D

RecyclerView will only bind X and run the animation.

There is a getPosition method in ViewHolder but that may not match adapter position if you call it in the middle of an animation.

If you need the adapter position, your safest option is getting the position from the Adapter.

update for your comment

Add a Task field to the ViewHolder.

Change onCreateViewHolder as follows to avoid creating a listener object on each rebind.

// inflates row to create a viewHolder@Overridepublic TaskViewHolder onCreateViewHolder(ViewGroup parent, int type) {
    ViewitemView= LayoutInflater.from(parent.getContext()).
                               inflate(R.layout.list_item, parent, false);

    finalTaskViewHoldervh=newTaskViewHolder(itemView);
    taskViewHolder.closeBtn.setOnClickListener(newView.OnClickListener() {
        @OverridepublicvoidonClick(View v) {
            // delete from SQLite DBTasktaskToDel= vh.getTask();
            finalintpos= taskList.indexOf(taskToDel);
            if (pos == -1) return;
            taskToDel.delete();
            // updating UI
            taskList.remove(pos);
            thisAdapter.notifyItemRemoved(pos);
        }
    });
}

so in your on bind method, you do

// onBindViewHolder binds a model to a viewholder@OverridepublicvoidonBindViewHolder(TaskViewHolder taskViewHolder, int pos) {
    TaskcurrTask= taskList.get(pos);
    taskViewHolder.setTask(currTask);
    taskViewHolder.taskTV.setText(currTask.getDescription());
}

Solution 2:

Like yigit said, RecyclerView works like that:

AB 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

Solution 3:

Why dont you use a public interface for the button click and controle the action in the MainActivity.

In your adapter add:

publicinterfaceOnItemClickListener {
    voidonItemClick(View view, int position, List<Task> mTaskList);
}

and

public OnItemClickListener mItemClickListener;

// Provide a suitable constructor (depends on the kind of dataset)public TaskAdapter (List<Task> myDataset, OnItemClickListener mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
    this.mDataset = mDataset;
}

plus the call in the ViewHolder class

publicclassViewHolderextendsRecyclerView.ViewHolder implementsView.OnClickListener {

    publicViewHolder(View v) {
        super(v);
        ...
        closeBtn = (ImageView)v.findViewById(R.id.xImg);
        closeBtn.setOnClickListener(this);
    }

    @OverridepublicvoidonClick(View v) {
        // If not long clicked, pass last variable as false.
        mItemClickListener.onItemClick(v, getAdapterPosition(), mDataset);
    }
}

In your MainActivity change your adapter to handle the call

// set Adapter
    mAdapter = new TaskAdapter(taskList, new TaskAdapter.OnItemClickListener() {

        @Override
        publicvoidonItemClick(View v, int position) {
            if (v.getId() == R.id.xImg) {
                Task taskToDel = taskList.get(position);
                // updating UI
                taskList.remove(position);
                thisAdapter.notifyItemRemoved(position);
                // remove from db with unique id to use delete query// dont use the position but something like taskToDel.getId() 
                taskToDel.delete();
            } 
        }
    });

Solution 4:

Personally, I don't like this concept of RecyclerViews. Seems like it wasn't thought of completely.

As it was said when removing an item the Recycler view just hides an item. But usually you don't want to leave that item in your collection. When deleting an item from the collection "it shifts its elements towards 0" whereas recyclerView keeps the same size.

If you are calling taskList.remove(position); your position must be evaluated again:

int position = recyclerView.getChildAdapterPosition(taskViewHolder.itemView);

Solution 5:

Thanks to @yigit for his answer, his solution mainly worked, I just tweaked it a little bit so as to avoid using vh.getTask() which I was not sure how to implement.

finalViewHoldervh=newViewHolder(customView);
    finalKittyAdapterfinal_copy_of_this=this;

    // We attach a CheckChange Listener here instead of onBindViewHolder// to avoid creating a listener object on each rebind// Note Rebind is only called if animation must be called on view (for efficiency)// It does not call on the removed if the last item is checked
    vh.done.setChecked(false);
    vh.done.setOnCheckedChangeListener(null);
    vh.done.setOnCheckedChangeListener(newCompoundButton.OnCheckedChangeListener() {
        @OverridepublicvoidonCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            buttonView.setEnabled(false);
            finalintpos2= vh.getAdapterPosition(); // THIS IS HOW TO GET THE UPDATED POSITION// YOU MUST UPDATE THE DATABASE, removed by TitleDatabaseHandlerdb=newDatabaseHandler(mContext);
            db.remove(mDataSet.get(pos2).getTitle(), fp);
            db.close();
            // Update UI
            mDataSet.remove(pos2);
            final_copy_of_this.notifyItemRemoved(pos2);

        }
    });

Notice instead to get the updated position, you can call vh.getAdapterPosition(), which is the line that will give you the updated position from the underlying dataset rather than the fake view.

This is working for me as of now, if someone knows of a drawback to using this please let me know. Hope this helps someone.

Post a Comment for "How Is The Position Of A Recyclerview Adapter Related To The Index Of Its Dataset?"