notifyItemChanged call onCreateViewHolder

1.7k Views Asked by At

I've a strange problem with my recyclerView adapter, I just want to show/hide ImageView depending of the selection but when I call the notifyItemChanged(selection) in my click listener, it call onCreateViewHolder and take a delay to refresh de view, I don't know why and I didn't find another solution to perform what I need.

This is my adapter:

public class ChannelAdapter extends RecyclerView.Adapter<ChannelAdapter.ChannelHolder> {
    private ArrayList<Integer> channelList;
    private Integer selection = 0;

    public ChannelAdapter(ArrayList<Integer> channelList) {
        this.channelList = channelList;
    }

    @NotNull
    @Override
    public ChannelHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, "onCreateViewHolder: ");
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_channel_item_selected, parent, false);
        ChannelHolder holder = new ChannelHolder(view);
        return holder;
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(ChannelHolder holder, int position) {
        Log.d(TAG, "onBindViewHolder: "+position);
        holder.textViewChannelId.setText("#"+channelList.get(position));
        holder.textViewChannel.setText(channelList.get(position).toString());

        if(position==selection){
            holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
            holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
        }
        else{
            holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
            holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
        }
    }

    @Override
    public int getItemCount() {
        return channelList.size();
    }

    public class ChannelHolder extends RecyclerView.ViewHolder {
        TextView textViewChannel, textViewChannelId;
        ImageView imageViewSelectorLeft, imageViewSelectorRight;

        public ChannelHolder(View itemView) {
            super(itemView);
            textViewChannelId = itemView.findViewById(R.id.textViewChannelId);
            textViewChannel = itemView.findViewById(R.id.textViewChannel);
            imageViewSelectorLeft = itemView.findViewById(R.id.imageViewSelectorLeft);
            imageViewSelectorRight = itemView.findViewById(R.id.imageViewSelectorRight);

            itemView.setOnClickListener(v -> {
                notifyItemChanged(selection);
                selection=getAdapterPosition();
                notifyItemChanged(selection);
            });
        }
    }
}

Do I miss something or am I doing it by the wrong way? Thanks in advance for any help

Edit : I tried to use notifyItemChanged with a payload set to 1 and override onBindViewHolder to get the payload but it still call onCreateViewHolder, even when mSupportsChangeAnimations is set to false

2

There are 2 best solutions below

3
Syed Rafaqat Hussain On

You can call your adapter like this

YourAdapter adapter = new YourAdapter(yourList, yourActivity.this);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();

and then you can put this code on itemClick in adapter

if(position==getAdapterPosition()){
        holder.imageViewSelectorLeft.setVisibility(View.VISIBLE);
        holder.imageViewSelectorRight.setVisibility(View.VISIBLE);
    }
    else{
        holder.imageViewSelectorLeft.setVisibility(View.INVISIBLE);
        holder.imageViewSelectorRight.setVisibility(View.INVISIBLE);
    }
4
Ben P. On

By default, your RecyclerView will have a DefaultItemAnimator attached to it. When you call notifyItemChanged() on your adapter, the system will eventually call through to the DefaultItemAnimator to find out whether it needs to create a new ViewHolder or if it can "re-use" the existing one.

@Override
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
        @NonNull List<Object> payloads) {
    return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}

The superclass implementation:

@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
    return !mSupportsChangeAnimations || viewHolder.isInvalid();
}

These suggest that there are two easy ways to make sure that the ViewHolder is reused instead of recreated:

  • Make sure that the payloads list is not empty. This is done by calling adapter.notifyItemChanged(position, payload). It doesn't matter what the payload is, as long as it is non-null.
  • Set mSupportsChangeAnimations to false for your DefaultItemAnimator.
DefaultItemAnimator animator = (DefaultItemAnimator) recyclerView.getItemAnimator();
animator.setSupportsChangeAnimations(false);