Lucas Storti
Lucas Storti

Reputation: 88

Add item to RecyclerView dynamically

I want to be able to add items to my ReclycerView dynamically.

When an item loads -> setText() -> I add another item on list.

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    final Message message = mDataset.get(position);

    if(message.isAnswers()) {
        holder.mAnswer1Button.setText(message.getAnswer1());
        holder.mAnswer2Button.setText(message.getAnswer2());

        holder.mAnswer1Button.setOnClickListener(v -> {
            if(message.getChild1() > 0) {
                add(position + 1, dataListShared.get(message.getChild1()));
                holder.mAnswer1Button.setClickable(false);
                holder.mAnswer2Button.setEnabled(false);
            }
        });
    } else {
        holder.mMessageTextView.setText(message.getMessage());
            if(message.getChild1() > 0) {
                add(position + 1, dataListShared.get(message.getChild1()));
                holder.mMessageTextView.setEnabled(false);
            }
    }
}

This is what I have inside onBindViewHolder. When I am on the first case if(), and I click the button, the item is added to the list. On the second Case else(), I would like for the text to be set on this current item and than already add another one.

How can I achieve this?

Moreover, why add() works inside onClickListener but not outside of it?

The error I get is:

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

Thanks! :)

Upvotes: 2

Views: 5813

Answers (2)

Jai
Jai

Reputation: 3310

The error itself is self explanatory ... It is dangerous to setOnClickListener in onBindViewHolder. This method is step of refresh each recycler item.

You should move method setOnClickListener to ViewHolder which is inner class on your adapter.

class MyViewHolder extends RecyclerView.ViewHolder
    {
        private final OnClickListener mOnClickListener = new MyOnClickListener();
        Button mAnswer1Button, mAnswer2Button;
        public MyViewHolder (View itemView) {
            super(itemView);
            mAnswer1Button = (Button) itemView.findViewById(R.id.item);
            mAnswer2Button = (Button) itemView.findViewById(R.id.item);
            mAnswer1Button.setOnClickListener(mOnClickListener);

        }


       @Override
       public void onClick(final View view) {
            //Now your Logic ....
       }
    }

One more thing you could do is set Create an interface OnItemClickListener and declare onItemClick and then make the activity from where you are setting up the adapter for the particular recycler view implment OnItemClickListener this and over there you can dynamically add another item and setAdapter again or notifyDataSetChanged()

Your InterFace

 public interface OnItemClickListener{
      public void onItemClick(int position);
 }

Your MainActivity

   class MainActivity implements OnItemClickListener{
       RecyclerView mRecyclerView ;

       @Override
    public void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.layout);
    mRecyclerView = (RecyclerView ) findViewById(R.id.recyclerview);
    mAdapter= new MyAdapter(ArrayList, getContext(), MainActivity.this);

    }

       @Overrride
       public void onItemClick(int position)
       {
               //Now your Logic ....

       } 
   } 

Now you can call this method onItemClick from the Adpater clas by setting onClickListener mAnswer1Button in the ViewHolder class and calling this method with in the onClick

Upvotes: 1

Micha
Micha

Reputation: 394

As commented, I think it should work like this:

private int position;

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    this.position = position;
}


@Override
public void onViewAttachedToWindow(ViewHolder holder) {
    final Message message = mDataset.get(position);

    if(message.isAnswers()) {
        holder.mAnswer1Button.setText(message.getAnswer1());
        holder.mAnswer2Button.setText(message.getAnswer2());

        holder.mAnswer1Button.setOnClickListener(v -> {
            if(message.getChild1() > 0) {
                add(position + 1, dataListShared.get(message.getChild1()));
                holder.mAnswer1Button.setClickable(false);
                holder.mAnswer2Button.setEnabled(false);
            }
        });
    } else {
        holder.mMessageTextView.setText(message.getMessage());
            if(message.getChild1() > 0) {
                add(position + 1, dataListShared.get(message.getChild1()));
                holder.mMessageTextView.setEnabled(false);
            }
    }
}

In onBindViewHolder(), you apparently cannot change the dataset - I guess as the framework is still busy displaying the previous dataset. But when saving the position in an instance variable, and updating the dataset in onViewAttachedToWindow(), the RecylerView should be ready for more data.

To be honest though, I wouldn't add all this logic to the ViewHolder, but pass an interface to it so the logic can be kept in a more central place, like a presenter.

Upvotes: 0

Related Questions