'recyclerAdapter.notifyItemInserted updates item on wrong position
I created 3 recyclerViews which are in the same NestedScrollView. They all use the same ArrayList to display items.
Lastly I tried to follow one tutorial to add onSwipe to one of the recyclerViews which worked almost perfectly. Firstly I remove the object on selected position with recyclerAdapter.notifyItemRemoved(position) which behaves correctly and then I press the undo button which triggers recyclerAdapter.notifyItemInserted(position) but it doesnt update the recyclerView on correct position.
In depth behaviour:
When I swipe the first item, meaning position is 0. The viewHolder.getAdapterPosition() returns 0 which then calls recyclerAdapter.notifyItemRemoved(position) and after pressing the undo buttons which adds the item back I see via debug that the position inside the adapter is also 0 as it should.
But when I press any other item than on position 0, the viewHolder.getAdapterPosition() returns correct position for example 2 I again remove the item which works correctly. But after calling recyclerAdapter.notifyItemInserted(position) I can see that the position in method onBindViewHolder seems to always be the size of arrayList that is in the adapter instead of the position.
There are no error messages shown when this happens.
MainActivity.java
public class MainActivity extends AppCompatActivity {
//Objects
private RecyclerView timeRecyclerView, barRecyclerView, activityRecyclerView;
private RecyclerViewActivityAdapter recyclerViewActivityAdapter;
private RecyclerViewTimeAdapter recyclerViewTimeAdapter;
private RecyclerViewBarAdapter recyclerViewBarAdapter;
private Database database;
//Vars
private final String activityName = MainActivity.this.getClass().getSimpleName();
public static final String logName = "[LOG] ";
private final List<Day> scheduleList = new ArrayList<>();
private int displayWidth;
private int displayHeight;
private int rowSize;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getDisplaySize();
loadDatabase();
setTimeRecyclerView();
setBarRecyclerView();
setActivityRecyclerView();
setOnSwipe();
}
private void loadDatabase() {
Log.i(MainActivity.logName + activityName, "loadDatabase()");
//loads data from database (only called onStart)
database = new Database(this);
scheduleList.clear();
scheduleList.addAll(database.getAll());
}
private void setActivityRecyclerView() {
Log.i(MainActivity.logName + activityName, "setBarRecyclerView()");
activityRecyclerView = findViewById(R.id.activityRecyclerView);
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerViewActivityAdapter = new RecyclerViewActivityAdapter(scheduleList, rowSize);
recyclerViewActivityAdapter.setHasStableIds(true);
activityRecyclerView.setItemAnimator(new DefaultItemAnimator());
activityRecyclerView.setLayoutManager(linearLayoutManager);
activityRecyclerView.setAdapter(recyclerViewActivityAdapter);
}
private void setBarRecyclerView() {
Log.i(MainActivity.logName + activityName, "setBarRecyclerView()");
barRecyclerView = findViewById(R.id.barRecyclerView);
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerViewBarAdapter = new RecyclerViewBarAdapter(scheduleList, rowSize);
recyclerViewBarAdapter.setHasStableIds(true);
barRecyclerView.setItemAnimator(new DefaultItemAnimator());
barRecyclerView.setLayoutManager(linearLayoutManager);
barRecyclerView.setAdapter(recyclerViewBarAdapter);
}
private void setTimeRecyclerView() {
Log.i(MainActivity.logName + activityName, "setTimeRecyclerView()");
timeRecyclerView = findViewById(R.id.timeRecyclerView);
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerViewTimeAdapter = new RecyclerViewTimeAdapter(scheduleList, rowSize);
recyclerViewTimeAdapter.setHasStableIds(true);
timeRecyclerView.setItemAnimator(new DefaultItemAnimator());
timeRecyclerView.setLayoutManager(linearLayoutManager);
timeRecyclerView.setAdapter(recyclerViewTimeAdapter);
}
private void getDisplaySize() {
Log.i(MainActivity.logName + activityName, "getDisplaySize()");
if (android.os.Build.VERSION.SDK_INT <= 29){
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width= displayMetrics.widthPixels;
int height= displayMetrics.heightPixels;
displayWidth = Math.round(width / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
displayHeight = Math.round(height / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
rowSize = displayHeight / 5;
} else { //api ver 30 and higher
WindowManager windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
WindowInsets windowInsets = metrics.getWindowInsets();
Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
| WindowInsets.Type.displayCutout());
int insetsWidth = insets.right + insets.left;
int insetsHeight = insets.top + insets.bottom;
final Rect bounds = metrics.getBounds();
displayWidth = bounds.width() - insetsWidth;
displayHeight = bounds.height() - insetsHeight;
rowSize = displayHeight / 5;
}
}
private void setOnSwipe() {
Log.i(MainActivity.logName + activityName, "setOnSwipe()");
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Day deletedActivity = scheduleList.get(position);
scheduleList.remove(position);
recyclerViewActivityAdapter.notifyItemRemoved(position);
recyclerViewBarAdapter.notifyItemRemoved(position);
recyclerViewTimeAdapter.notifyItemRemoved(position);
MakeSnackbar(activityRecyclerView, "Successfully removed - " + deletedActivity.getName(), deletedActivity, position);
}
}).attachToRecyclerView(activityRecyclerView);
}
private void MakeSnackbar(RecyclerView activityRecyclerView, String message, Day deletedActivity, int position) {
Log.i(MainActivity.logName + activityName, "MakeSnackbar(RecyclerView, "+ message +", "+ deletedActivity.getName() +" , "+ position +")");
Snackbar snackbar = Snackbar.make(activityRecyclerView, message, Snackbar.LENGTH_LONG);
snackbar.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v) {
scheduleList.add(position, deletedActivity);
recyclerViewActivityAdapter.notifyItemInserted(position);
recyclerViewBarAdapter.notifyItemInserted(position);
recyclerViewTimeAdapter.notifyItemInserted(position);
snackbar.dismiss();
}
}).show();
snackbar.addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
if (event != DISMISS_EVENT_ACTION && event != DISMISS_EVENT_MANUAL){
database.removeActivity(deletedActivity.getId());
}
}
@Override
public void onShown(Snackbar snackbar) {
}
});
}
}
RecyclerAdapters (RecyclerViewActivityAdapter) - the only difference between the adapter is that they display different data in the onBindViewHolder (besides layout and object reference)
public class RecyclerViewActivityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final String activityName = RecyclerViewActivityAdapter.this.getClass().getSimpleName();
private List<Day> data;
private int objectSize;
private Context context;
public RecyclerViewActivityAdapter(List<Day> data, int size){
this.objectSize = size;
this.data = data;
}
public static class ActivityViewHolder extends RecyclerView.ViewHolder{
public LinearLayout activityLayout;
public TextView activityName;
public ActivityViewHolder(@NonNull View itemView){
super(itemView);
activityLayout = itemView.findViewById(R.id.activityLayout);
activityName = itemView.findViewById(R.id.activityName);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Log.i(MainActivity.logName + activityName, "onCreateViewHolder(ViewGroup,"+viewType+")");
context = parent.getContext();
Log.i(MainActivity.logName + activityName, "onCreateViewHolder type - ActivityViewHolder");
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.segment_recycler_view_activity,parent,false);
return new RecyclerViewActivityAdapter.ActivityViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Log.i(MainActivity.logName + activityName, "onBindViewHolder(ViewHolder,"+position+")");
//setLayout
}
@Override
public int getItemCount() {
return data.size();
}
Solution 1:[1]
public void removeItem(int position) {
mItems.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());
}
have a helper method like in the sample code. You should definitely use notifyItemRangeChanged.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Yusuf Ya?ar |

