Android RecyclerView : notifyDataSetChanged() IllegalStateException

前端 未结 22 1777
天涯浪人
天涯浪人 2020-11-28 02:57

I\'m trying to update the items of a recycleview using notifyDataSetChanged().

This is my onBindViewHolder() method in the recycleview adapter.

@Over         


        
相关标签:
22条回答
  • 2020-11-28 03:50

    When you have the Message Error:

    Cannot call this method while RecyclerView is computing a layout or scrolling
    

    Simple, Just do what cause the Exception in:

    RecyclerView.post(new Runnable() {
        @Override
        public void run() {
            /** 
            ** Put Your Code here, exemple:
            **/
            notifyItemChanged(position);
        }
    });
    
    0 讨论(0)
  • 2020-11-28 03:53

    At first I thought Moonsoo's answer (the accepted answer) wouldn't work for me because I cannot initialize my setOnCheckedChangeListener() in the ViewHolder constructor because I need to bind it each time so it gets an updated position variable. But it took me a long time to realize what he was saying.

    Here is an example of the "circular method call" he is talking about:

    public void onBindViewHolder(final ViewHolder holder, final int position) {
        SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);
        mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                           if (isChecked) {
                               data.delete(position);
                               notifyItemRemoved(position);
                               //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                               notifyItemRangeChanged(position, data.size());
                           }
                       }
                });
        //Set the switch to how it previously was.
        mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.
    }
    

    The only problem with this, is that when we need to initialize the switch to be on or off (from past saved state, for example), it is calling the listener which might call nofityItemRangeChanged which calls onBindViewHolder again. You cannot call onBindViewHolder when you are already in onBindViewHolder], because you cannot notifyItemRangeChanged if you are already in the middle of notifying that the item range has changed. But I only needed to update the UI to show it on or off, not wanting to actually trigger anything.

    Here is the solution I learned from JoniDS's answer that will prevent the infinite loop. As long as we set the listener to "null" before we set Checked, then it will update the UI without triggering the listener, avoiding the infinite loop. Then we can set the listener after.

    JoniDS's code:

    holder.checkbox.setOnCheckedChangeListener(null);
    holder.checkbox.setChecked(condition);
    holder.checkbox.setOnCheckedChangeListener(checkedListener);
    

    Full solution to my example:

    public void onBindViewHolder(final ViewHolder holder, final int position) {
        SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);
    
        //Set it to null to erase an existing listener from a recycled view.
        mySwitch.setOnCheckedChangeListener(null);
    
        //Set the switch to how it previously was without triggering the listener.
        mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.
    
        //Set the listener now.
        mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    data.delete(position);
                    notifyItemRemoved(position);
                    //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                    notifyItemRangeChanged(position, data.size());
                }
            }
        });
    }
    
    0 讨论(0)
  • 2020-11-28 03:53

    Before notifyDataSetChanged() just check that with this method: recyclerView.IsComputingLayout()

    0 讨论(0)
  • 2020-11-28 03:55

    Simple use Post:

    new Handler().post(new Runnable() {
            @Override
            public void run() {
                    mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1);
                }
            }
        });
    
    0 讨论(0)
提交回复
热议问题