前言
使用RadioButton和Checkbox,Switch这类依赖checked标签来进行工作的view来说,经常会遇到一些checked状态混乱,与click事件混淆,onClickListener与onCheckedChangeListener混淆不清的情况,在这里做如下总结。
checked状态刷新时机
从framework源码来看。
首先,此类view均继承自CompoundButton,也就是说他们的checked逻辑基本都是一样的。
查找CompoundButton里面对mChecked这一变量的修改发现,在toggle方法内调用:
@Override
public void toggle() {
setChecked(!mChecked);
}
继续检查到toggle方法,是在performClick中调用的,可以看到,在调用super的performClick之前,首先调用了toggle:
@Override
public boolean performClick() {
toggle();
final boolean handled = super.performClick();
if (!handled) {
// View only makes a sound effect if the onClickListener was
// called, so we'll need to make one here instead.
playSoundEffect(SoundEffectConstants.CLICK);
}
return handled;
}
接下来看一下setChecked方法:
@Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
...
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
...
也就是说,在view运行到自己的performClick时,会先刷新view的checked状态,并且回调onCheckedChangeListener,这就比较清晰了,因为onClickListener是需要在super.performClick中才会被调用的。
常见问题
在点击事件中,使用checked状态
经过上面分析,在onClick被调用前,checked状态已经刷新了并且onCheckChangedListener也被回调,所以这个时候把onClick时的view.isChecked当作旧的状态去使用,就会出错。记住这个时候已经是这次点击checked change生效后了。
使用onCheckedChangeListener代替onClickListener
可以代替使用,但需要注意,最好不要把响应点击的事件放在其中,因为如果有其他代码处会使用setChecked来改变check状态而不是用户触摸的话,只该被用户点击发生的事件也会发生。
建议:使用onCheckedChangeListener来监听checked状态,onClickListener来处理点击交互,此时注意上一点问题。
如果一定要代替使用,可以通过在onCheckedChangeListener内判断isPressed()来确定是否来自用户交互。
view.setOnCheckedChangeListener((v, checked) -> {
if (v.isPressed()) {
// 点击交互
}
})
代替使用在Accessibility模式下的问题
因为无障碍模式下,用户交互不是直接点击,而是发送调用setChecked,此时如果使用了上述方法,因为没有pressed,那么checkChangeListener就不会得到回调,可能会有错误产生。
最佳实践
在onCheckedChangeListener内不做交互相关监听,只处理其他不可见逻辑;
在onClickListener内只处理需要响应交互的逻辑,将此时的checked状态作为刷新后状态使用。

209

被折叠的 条评论
为什么被折叠?



