CheckBox changes value twice
Asked Answered
S

3

9

I have an Android ListView whose items have a checkbox.

The checkbox is checked by default. Once unchecked it should be removed from the list.

The problem is that onCheckedChanged is being fired twice: when I tap the checkbox to uncheck it (with isChecked false) and after I remove the item (with isChecked true).

This is the relevant code of my ArrayAdapter:

public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.item, parent, false);
        holder = new ViewHolder();
        holder.check = (CheckBox) convertView.findViewById(R.id.check);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    final Object item = this.getItem(position);
    holder.check.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!isChecked) {
                remove(item); // This somehow calls onCheckedChanged again
            }
        }
    });
    return convertView;
}

What am I doing wrong?

Saida answered 12/10, 2010 at 10:32 Comment(2)
What does the remove() method do, since that is not a method on ListAdapter?Corposant
I'm using an ArrayAdapter. According to the Android source code, the remove method removes the item from the array and notifies that the data has been changed.Saida
M
7

I had a similar issue and just resolved it properly without having to avoid using the intended OnCheckedChangeListener.

Problem Code

holder.someCheckBox.setChecked(false);
holder.someCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
       ...
    }
});

Note I set the check status before I set the listener, this was to prevent the listener from firing on creation.

But, the listener started firing the on-check event exactly twice only after the adapter was forced to be re-created or through use of notifyDataSetChanged.

Solution

Clear the listener before setting the check state, even during creation here.

holder.someCheckBox.setOnCheckedChangeListener(null);
holder.someCheckBox.setChecked(false);
holder.someCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
       ...
    }
});

Now any residual listeners will not be fired when setting the initial value of the checkbox.

Makassar answered 29/7, 2016 at 0:53 Comment(0)
C
6

I encountered the same problem, it seems to be an Android bug this twice execution of onCheckChanged method.

My solution: implement onClickListener, instead of onCheckedChangedListener.

Something like this:

private final class CheckUpdateListener implements OnClickListener {

    private Group parent;
    private boolean isChecked;

    private CheckUpdateListener(Group parent) {

        this.parent = parent;

    }

    @Override
    public void onClick(View box) {
        this.isChecked = !parent.isChecked();

        parent.setChecked(isChecked);

        notifyDataSetChanged();

    }

}
Choral answered 7/10, 2011 at 11:42 Comment(1)
Yes, I think that listening to onCheckedChange is overkill and not the right thing to do in most cases. The basic problem however for me was to get the right perspective, ie, that the View is not the same as the data object that is viewed.Miser
C
0

I encounter the same issue.

After some research i found out that there is a similar behaviour in RadioGroup for the setCheckChangeListener.

Tagging messes things up, seems like a bug.

My workaround was setting the tag to null at the end of the listener It worked for me.

private OnCheckedChangeListener checkBoxitemClickListener = new OnCheckedChangeListener() {

    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {


        if(isChecked)
        {               
            //do smth
        }else
        {
            //do smth
        }   

        buttonView.setTag(null);                        
    }
};

If you need to update your ListView after, don't forget to call "notifyDataSetChanged" on your adapter.

Cheers

Contamination answered 21/6, 2011 at 8:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.