2

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);

    }

}
}
Newaj
  • 3,992
  • 4
  • 32
  • 50
  • 1
    Sounds like it's because your items are being recycled and you're not changing the visibility of the button to `VISIBILE` by default in `onBindViewHolder()`. – Magnus Oct 22 '18 at 17:53
  • @BadCash , how to set default visibility? Will you explain a bit more please? – Newaj Oct 23 '18 at 04:46
  • See my answer. I have tested the solution and it works. If you still can't get it to work, something else is interfering and causing the problem. – Magnus Oct 23 '18 at 08:10

3 Answers3

1

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.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Juanje
  • 1,235
  • 2
  • 11
  • 28
  • it may also be related to the size it covers on his screen. in a different and possibly a larger screen, 1st and 8th will not be the same but rather, for example, 1st and 12th will be the same. If nothing helps, detecting it and using modulus to hide or show the button may help. – bengongon97 Oct 22 '18 at 18:35
  • @bengongon97 is right, I am assuming that 8th view is the first one that need recycling but could be different depending on the screen size. – Juanje Oct 22 '18 at 18:37
  • @Juanje , yeah, problem seems with keeping the state of button. How to do that? Will you explain a bit more please? – Newaj Oct 23 '18 at 04:45
1

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.

ישו אוהב אותך
  • 28,609
  • 11
  • 78
  • 96
  • Problem still exists. 1st item button remains visible after click, right after scrolling up & down it hides. Now also if I click the 9th button , 2nd button is also clicked. For 10th, 3rd button is also clicked & so on. – Newaj Oct 23 '18 at 04:41
  • Added adapter class in question – Newaj Oct 23 '18 at 05:08
  • Moved click to viewholder. Problem still exists. – Newaj Oct 23 '18 at 06:31
  • I have another similar type problem, please take a look, https://stackoverflow.com/questions/53013386/saving-selected-radiobuttons-text-in-model-class-from-recyclerview-item – Newaj Oct 27 '18 at 03:20
1

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.

Magnus
  • 17,157
  • 19
  • 104
  • 189
  • Correct! Thanks. – Newaj Oct 23 '18 at 10:32
  • I have another similar type problem. Will you take a look ? [link] https://stackoverflow.com/questions/53013386/saving-selected-radiobuttons-text-in-model-class-from-recyclerview-item – Newaj Oct 26 '18 at 17:34