Reputation: 794
I have a problem with recyclerView. I'm using this layout to expand cardView in recyclerView: https://github.com/AAkira/ExpandableLayout .
If I click on some item to expand and than scroll down every 7th element is also expanded. What can I do to stop that effect? I know that this is caused beacause recyclerView remembers state of view holder and mIsViewExpanded in method onBindiewHolder is set to true every 7th item. Thanks in advance for any solutions!
Here is code of my viewholder:
class EventsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView edit, cancel, expandIcon;
private ExpandableLayout expandableArea;
private boolean mIsViewExpanded = false;
public EventsViewHolder(View itemView) {
super(itemView);
expandIcon = (ImageView) itemView.findViewById(R.id.card_item_expand_icon);
expandIcon.setOnClickListener(this);
//divider = (View) itemView.findViewById(R.id.event_card_divider);
}
private void collapseArea(){
expandableArea.collapse();
edit.setVisibility(View.GONE);
cancel.setVisibility(View.GONE);
mIsViewExpanded = false;
expandIcon.setImageResource(R.drawable.vector_drawable_ic_expand_more_black___px);
}
private void expandArea(){
expandableArea.expand();
edit.setVisibility(View.VISIBLE);
cancel.setVisibility(View.VISIBLE);
mIsViewExpanded = true;
expandIcon.setImageResource(R.drawable.vector_drawable_ic_expand_less_black___px);
}
@Override
public void onClick(View view) {
if(mIsViewExpanded){
collapseArea();
isItemExpanded[getAdapterPosition()] = false;
}
else {
expandArea();
isItemExpanded[getAdapterPosition()] = true;
}
}
}
When I click on icon inside CardView expandableArea is expanding or collapsing depending on mIsViewExpanded value.
EDIT. Adapter code:
public class EventsRecyclerViewAdapter extends RecyclerView.Adapter<EventsRecyclerViewAdapter.EventsViewHolder> implements PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
public interface EventsRecyclerViewAdapterInterface {
public void emptyAdapter();
public void itemDeleted(int id);
}
private List<Event> listData = new ArrayList<>();
private LayoutInflater inflater;
private MainActivity context;
private View view;
private int positionToCancel;
private boolean[] isItemExpanded;
private EventsRecyclerViewAdapterInterface deleteListener;
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
removeItem(positionToCancel);
return true;
}
@Override
public void onDismiss(PopupMenu menu) {
menu.dismiss();
}
public EventsRecyclerViewAdapter(Activity context, EventsRecyclerViewAdapterInterface deleteListener) {
this.inflater = LayoutInflater.from(context);
this.context = (MainActivity) context;
this.deleteListener = deleteListener;
}
@Override
public EventsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
view = inflater.inflate(R.layout.item_event_card, parent, false);
return new EventsViewHolder(view);
}
@Override
public void onBindViewHolder(EventsViewHolder holder, int position) {
Event item = listData.get(position);
if(!isItemExpanded[position]){
if(holder.mIsViewExpanded){
holder.collapseArea();
}
}
else {
holder.expandArea();
}
holder.title.setText(item.getName());
holder.place.setText(item.getLocation());
DateTimeFormatter formatter = DateTimeFormat.forPattern(StaticValues.DATE_TIME_PATTERN_FOR_VIEW);
holder.time.setText(formatter.print(item.getDate()));
holder.edit.setOnClickListener(view1 -> takeDetailsEvent(item));
holder.cancel.setOnClickListener(view1 -> {
positionToCancel = position;
MyPopupMenu popup = new MyPopupMenu(view1.getContext(), view1, context);
popup.inflate(R.menu.event_delete_menu);
popup.setOnMenuItemClickListener(this);
popup.setOnDismissListener(this);
popup.show();
});
holder.container.setOnClickListener(view1 -> {
Intent intent = new Intent(view1.getContext(), EventDetailsActivity.class);
intent.putExtra("eventId", item.getApiId());
view1.getContext().startActivity(intent);
});
}
private void removeItem(int position) {
deleteListener.itemDeleted(listData.get(position).getApiId());
listData.remove(position);
notifyDataSetChanged();
if (getItemCount() == 0) {
deleteListener.emptyAdapter();
}
}
private void takeDetailsEvent(Event event) {
Intent intent = new Intent(view.getContext(), CreateEventActivity.class);
intent.putExtra("id", event.getApiId());
view.getContext().startActivity(intent);
}
public void setListData(List<Event> eventList) {
listData = eventList;
isItemExpanded = new boolean[listData.size()];
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return listData.size();
}
}
This is how I deal with it right now. In adapter class I have boolean array which lenght is the same as listData. On position corresponding to list item I keep value: true when expanded, false when collapsed. In the onBindViewHolder I check if position is expanded and if not I collapse it. It's not the ideal solution because during scroll items are collapsing and it doesn't look good.
Upvotes: 4
Views: 3661
Reputation: 425
Override these 2 methods in your adapter.
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
Reference = How to prevent items from getting duplicated when scrolling recycler view
Upvotes: 2
Reputation: 3331
RecyclerView
doesn't create new views instead it reuses the existing views when scrolling (ScrappedViews
). So what is happening is when you expand a CardView
and scroll, since the CardView
s are reused you get another expanded view.
So simply collapse/expand the view in the onBindView()
of your Adapter
, for that you'll need to associate a state with each item in adapter's list.
To resolve the expand/collapse animation during scrolling simply setDuration(0)
which allows you to expand/collapse without any delay or animation for smoother scrolling experience.
Upvotes: 2
Reputation: 66
It is expected behavior of recyclerView. RecyclerView recycles items that are not in view. if you do not want your items to be recycled use holder.setIsRecyclable(false); in your onBindViewHolder() method of your adapter.
holder.setIsRecyclable(false);
Upvotes: 0