Newaj
Newaj

Reputation: 4450

Button is auto clicked if I select one button in RecyclerView item

In my RecyclerView item, there is a button along with other views. I am hiding the button when it is clicked. Problem is, if I click the button on 1st item, the button on 8th item is auto clicked, if I click button on 2nd item, button on 9th item is auto clicked & so on. How to solve this problem?

Adapter class :

public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {

private List<Model> models;
Model model;
// public MyAdapterListener onClickListener;

SparseBooleanArray mStateButtons = new SparseBooleanArray();


public Adapter(List<Model> models){
    this.models = models;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_row, false);
    return  new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
    String question = models.get(position).getQues();
    final String optA = models.get(position).getOptA();
    final String optB = models.get(position).getOptB();
    final String optC = models.get(position).getOptC();
    final String optD = models.get(position).getOptD();
    final String answer = models.get(position).getAns();

    holder.question.setText(question);
    holder.optA.setText(optA);
    holder.optB.setText(optB);
    holder.optC.setText(optC);
    holder.optD.setText(optD);

    holder.options.setTag(position);
    holder.options.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            int radioButtonID = group.getCheckedRadioButtonId();
            int clickedPos = (Integer) group.getTag();

            models.get(clickedPos).setChecked(radioButtonID);


        }
    });
    holder.options.check(models.get(position).getChecked());

    final int currentPosition = holder.getAdapterPosition();
    final Button button = holder.seeAnswer;

    if(mStateButtons.valueAt(currentPosition)) {
        button.setVisibility(View.GONE);
    } else {
        button.setVisibility(View.VISIBLE);
    }

    holder.seeAnswer.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mStateButtons.put(position, true);
        }
    });

}

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

class ViewHolder extends RecyclerView.ViewHolder{

    TextView question;
    RadioButton optA, optB, optC, optD;
    Button seeAnswer;
    RadioGroup options;

    public ViewHolder(View itemView) {
        super(itemView);

        options = (RadioGroup) itemView.findViewById(R.id.rgMcqOptions);
        question = (TextView) itemView.findViewById(R.id.tvMcqQues);
        optA = (RadioButton) itemView.findViewById(R.id.rbOptA);
        optB = (RadioButton) itemView.findViewById(R.id.rbOptB);
        optC = (RadioButton) itemView.findViewById(R.id.rbOptC);
        optD = (RadioButton) itemView.findViewById(R.id.rbOptD);
        seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);

    }

}
}

Upvotes: 2

Views: 1542

Answers (3)

Juanje
Juanje

Reputation: 1315

The problem seems to be that you are not initializing correctly the state of the Button. Cells in a RecyclerView are reused when they appear or hide in the screen. That means that if you hide the 1st position and then this view is recycled to create the 8th, the Button keeps its state, in this case INVISIBLE

Try to assign a value all cases or init the value to VISIBLE.

Upvotes: 1

Magnus
Magnus

Reputation: 18748

You need some way to keep track of which buttons should be hidden and which should not. This is the responsibility of your adapter, so you need to add some form of array to keep track of button states there. A SparseBooleanArray is an efficient and appropriate option:

private SparseBooleanArray hideButtons = new SparseBooleanArray();

In onBindView you need to update the view for the current item being bound, including updating the button visibility:

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
    holder.seeAnswer.setVisibility( hideButtons.get(position, false) ? View.GONE : View.VISIBLE );
    ...
}

And of course you need to actually set the visibility - and store it in the SparseBooleanArray when clicking the button. Putting this event handler in the ViewHolder is a good option:

class ViewHolder extends RecyclerView.ViewHolder{
    Button seeAnswer;
    ...

    ViewHolder(View itemView) {
        super(itemView);

        seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);

        seeAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                seeAnswer.setVisibility(View.GONE);
                hideButtons.put(getAdapterPosition(), true);
            }
        });

        ...
    }

}

This is a tested and verified solution, so if you follow this and it still doesn't work, the cause of your problem is somewhere else.

Upvotes: 1

ישו אוהב אותך
ישו אוהב אותך

Reputation: 29783

This usually happens because you forgot to keep the state of the Button in case it's being recycled. You can use SparseBooleanArray to store the states. Something like this:

public class YourAdapter ... {

    // variable to save the state of buttons.
    // we use state true as hidden, false as visible
    SparseBooleanArray mStateButtons = new SparseBooleanArray();

    ...

    @Override
    public void onBindViewHolder(ContactsAdapter.ViewHolder viewHolder, int position) {
        final int currentPosition = viewHolder.getAdapterPosition();

        // assume this is your button
        Button button = viewHolder.yourButton;

        // set the previous state to button
        if(mStateButtons.valueAt(currentPosition)) {
           // state is true, so the button need to be hide.
           button.setVisibility(View.GONE);
        } else {
           // default value is valse, which is we set as visible.
           button.setVisibility(View.VISIBLE);
        }

        button.setOnClickListener(new View.OnClickListener() { 
            @Override
            public void onClick(View view) { 
              // save the state when clicked
              mStateButtons.put(currentPosition, true);  
            } 
        }); 
    }
}

UPDATE

Try moving the click handling on ViewHolder, something like this:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ...
    Button seeAnswer;

    public ViewHolder(View itemView) {
        super(itemView);

        ...
        seeAnswer = (Button) itemView.findViewById(R.id.btnSeeAnswer);
        itemView.setOnClickListener(this);

    }

    // Handles the row being being clicked
    @Override
    public void onClick(View view) {
       mStateButtons.put(getAdapterPosition(), true);
       view.setVisibility(View.GONE);
    }
}

then remove the button.setOnClickListener(new View.OnClickListener() in onBindViewHolder.

Upvotes: 1

Related Questions