Thursday, January 21, 2016

Calling setCheck on a CheckBox in an Adapter calls onCheckedChange...why?

[Update:

Calling setCheck on a CheckBox (in getView) of an Adapter shouldn't trigger a call to the onCheckedChange listener, because (in my opinion) the listener is listening for the physical touch on the screen, not a programmatic call to configure the display.  Man, did that take some time to figure out, because I kept thinking the feature/bug was in my code, not Android code.

I hope it's a bug/feature in an old library, though I thought I had the latest libs.  A lot of my development notes turn into stacks that start at the answer and descend into the path to the solution, so don't be surprised if a nugget turns up on top of this update that addresses this with the final answer.

This issue just suddenly showed up in code that was working and I didn't update anything, so I spent time thinking my new code had broken something.  Because wow, did it break my program so spectacularly and when I was putting a whole new feature in, and in a way that left me with no awareness that this was the source, that I was tearing through working code like it was broken until I realized it was over in that other part of the program, in an innocuous part of code, that I thought was working.  Because the call to setCheck had been working for five years.  Or I'd just been lucky all that time.

In order to fix the bug (feature?), I set the onCheckedChangeListener to null before calling setCheck, then I set the listener back to the instance that needs to respond to the call.

active.setOnCheckedChangeListener(null);
active.setChecked( isChecked );

active.setOnCheckedChangeListener(this);

50% more code, lost time, and all for no good reason.  Whatever it takes to ship...so long as it works.

I'm keeping the original article below, because it highlights how insidious OS features/bugs are.  They can look like a sequencing problem, when in fact, their behavior performs in unexpected ways.

P. S. I'm pretty sure in the end, it's my ignorance that caused the bug.  I probably should have read the docs for setChecked, groveled a bug database, or checked online for solutions.  But I'm already swimming in Android docs and setting a checkbox has never, in any OS I've worked with (to the best of my recollection), triggered an event as if the user had changed something.

end update]

Just a mental note to remind me not do what I just did, because it took a minute to figure out what was wrong:
I set the listener after setting the state of the control in the getView method in the Adapter.  By setting the listener before setting the state, Android was causing subsequent getTag calls to return null.  I'll figure out why later, because I really have to finish this product I'm working on.
This bug was especially confusing because I don't remember changing any of the code and it just suddenly broke in a place I knew had been working and it suddenly stopped working and I wasn't working on this piece of code at all.

// No.  This code is broken.
CheckBox active = (CheckBox)entry.findViewById( R.id.setting_active );
active.setTag( object );
active.setOnCheckedChangeListener(this);
active.setChecked( isChecked );


// Yes.  This code works.
CheckBox active = (CheckBox)entry.findViewById( R.id.setting_active );
active.setChecked( isChecked );
active.setTag( object );
active.setOnCheckedChangeListener(this);

No comments:

Post a Comment