I am using a RecyclerView to display a list of items, but it refuses to display nicely despite my most sincere efforts. The items repeat after the first 14 in portrait and the first 10 in landscape, with the 15th/11th element scrolling its value with the user scroll. On top of that, scrolling will scramble data when repeatedly scrolling up/down. This link shows this behavior on a list of items numbered 1-28.
I have tried overriding adding this override method to my adapter:
@Override
public long getItemId(int position) {
return position;
}
I have tried using recyclerView.setItemViewCacheSize(10);
; this only pushes the problem index further out. I have also tried using setHasStableIds(true);
.
It seems like this must be a caching issue, but I don't know what would be causing the issue in the first place.
This a greatly reduced version of my adapter. The full class does use two different view types, but I'm about 96% sure that is not the cause of the issue.
public class InventoryListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//global view controls declared
private TextView name;
private List<Items> dataSet;
public class RowViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private MyListener mListener;
RowViewHolder (View v, MyListener listener) {
super(v);
//set view control ids
name = v.findViewById(R.id.name);
v.setOnClickListener(this);
}
//override onClick...
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RowViewHolder rowHolder = (RowViewHolder) holder;
//set control values from database
name.setText(someItem.getName());
}
@Override
public int getItemCount() {
return dataSet.size();
}
@Override
public int getItemViewType(int position) {
//returns 1 or 2 based on data type
}
@Override
public long getItemId(int position) {
return position;
}
//also, methods to update dataSet, delete items, and get item at position
If you would like to see the full project, you can find it here.
I would love to have a RecyclerView that actually scrolls through all of my data correctly!
EDIT: As requested, the full adapter code. Please pardon the mess!
public class InventoryListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private RecyclerViewClickListener myListener;
private List<UserInventory> dataSet = new ArrayList<>();
private List<Unit> itemUnits = new ArrayList<>(); //stores constant values from read-only sqlite database
private TextView itemName, itemQuantity, itemNameLifeBar, itemQuantityLifeBar;
private ProgressBar lifeBar;
private UserInventory deletedItem;
private int deletedPosition;
public class RowViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private RecyclerViewClickListener rowListener;
RowViewHolder (View v, RecyclerViewClickListener listener) {
super(v);
itemName = v.findViewById(R.id.item_name);
itemQuantity = v.findViewById(R.id.item_quantity);
v.setOnClickListener(this);
}
@Override
public void onClick(View view) {
myListener.onClick(view, getAdapterPosition());
}
}
public class LifeBarViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private RecyclerViewClickListener rowListener;
LifeBarViewHolder (View v, RecyclerViewClickListener listener) {
super(v);
itemNameLifeBar = v.findViewById(R.id.item_name_trackable);
itemQuantityLifeBar = v.findViewById(R.id.item_quantity_trackable);
lifeBar = v.findViewById(R.id.lifebar);
myListener = listener;
v.setOnClickListener(this);
}
@Override
public void onClick(View view) {
myListener.onClick(view, getAdapterPosition());
}
}
InventoryListAdapter(RecyclerViewClickListener listener) {
myListener = listener;
setHasStableIds(true);
}
//empty constructor
InventoryListAdapter() {}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
switch (viewType) {
case 1:
View v = LayoutInflater.from(context).inflate(R.layout.inventory_list_item, parent, false);
return new RowViewHolder(v, myListener);
case 2:
View view = LayoutInflater.from(context).inflate(R.layout.inventory_list_trackable, parent, false);
return new LifeBarViewHolder(view, myListener);
}
//shouldn't happen
View view = LayoutInflater.from(context).inflate(R.layout.inventory_list_item, parent, false);
return new RowViewHolder(view, myListener);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
switch(holder.getItemViewType()) {
case 1:
RowViewHolder rowHolder = (RowViewHolder) holder;
UserInventory item = dataSet.get(position);
Unit unit = itemUnits.get(position);
itemName.setText(item.getItemName());
if (item.isQuantify()) { //check if item is actually quantified first
itemQuantity.setText(item.getQuantity() + " " + unit.getAbbreviation());
} else { //otherwise, quantity needs to be empty string
itemQuantity.setText("");
}
break;
case 2:
LifeBarViewHolder lifebarHolder = (LifeBarViewHolder) holder;
UserInventory item2 = dataSet.get(position);
Unit unit2 = itemUnits.get(position);
itemNameLifeBar.setText(item2.getItemName());
itemQuantityLifeBar.setText(item2.getQuantity() + " " + unit2.getAbbreviation());
lifeBar.setMax(item2.getMaxQuantity());
lifeBar.setProgress(item2.getQuantity());
break;
}
}
public void updateData(List<UserInventory> data, final Context context) {
if (data == null) { return; }
UnitDao unitDao = new UnitDao(context); //pulls constant values from read-only sqlite database
dataSet.clear();
itemUnits.clear();
dataSet.addAll(data);
for (int i=0; i<dataSet.size(); i++) {
//Log.e("_IDs", String.valueOf(dataSet.get(i).get_ID()));
itemUnits.add(unitDao.getUnitById(dataSet.get(i).getUnit()));
}
notifyDataSetChanged();
}
public UserInventory deleteItem(int position) {
deletedItem = dataSet.get(position);
deletedPosition = position;
dataSet.remove(position);
itemUnits.remove(position);
//notifyDataSetChanged();
notifyItemRemoved(position);
notifyItemRangeChanged(position, getItemCount());
//showUndoSnackBar();
return deletedItem;
}
public UserInventory getItemAtPosition(int position) {
return dataSet.get(position);
}
@Override
public int getItemCount() {
return dataSet.size();
}
@Override
public int getItemViewType(int position) {
UserInventory item = dataSet.get(position);
if (item.isMultiUnit()) {
//is quantified with target max
return 2;
} else {
return 1;
}
}
@Override
public long getItemId(int position) {
return position;
}
}