How Is The Position Of A Recyclerview Adapter Related To The Index Of Its Dataset?
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?"