目录
功能场景
最近做了一个远程控制车辆的功能
1)每个功能定义了一个组合型的自定义View A,里面有一个CheckBox B,点击A或者B能控制开关B的状态。但是“闪灯鸣笛”这一项,如果点击了B,并不是一点击B就去反转CheckBox状态,而是不处理事件。
2)整个的点击交由热区A的点击事件处理,弹出Dialog,让用户选择“闪灯”、“闪灯鸣笛”、“鸣笛”三个选项,由于这三个选项可能都不会选中,所以无法使用RadioButton,仍然选择使用CheckBox处理。
“闪灯”、“闪灯鸣笛”、“鸣笛”三个选项选择状态的变化,对于组合型的自定义View A要对应地联动变化,所以每个选项都监听setOnCheckedChangeListener。“闪灯”、“闪灯鸣笛”、“鸣笛”三个选项选择状态的变化的原因可能有2种:第1种,自身被点击了;第2种,其它的选项点击了,导致自己状态的变化。对于第2种变化,并不需要处理setOnCheckedChangeListener的逻辑,只要选项的状态变化了就行。
问题:正常情况下,CheckBox一点击就会反转状态,回调onCheckedChanged方法。
需求:我在这个场景下的业务需求有2个
1)屏蔽CheckBox的点击事件,可以设置不允许手动点击改变状态的CheckBox,只能通过调用setChecked或者toggle()方法改变选中状态。
2)可以监听只有在手动点击下状态改变,通过setChecked或者toggle()触发的状态变化则不监听。
分析:
1) CheckBox为什么点击会反转状态?
2)CheckBox是如何反转状态的?
所以带着上面的两个问题去查看CheckBox的源码:
CheckBox继承CompoundButton,CompoundButton复写了performClick方法,里面调用了
toggle()方法,toggle()方法调用了setChecked(!mChecked)。
@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;
}
/**
* <p>Changes the checked state of this button.</p>
*
* @param checked true to check the button, false to uncheck it
*/
@Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mCheckedFromResource = false;
mChecked = checked;
refreshDrawableState();
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
mBroadcasting = false;
}
// setStateDescription will not send out event if the description is unchanged.
setDefaultStateDescritption();
}
处理办法:
1.自定义CheckBox,复写其onTouchEvent方法,控制是否处理事件。
@Override
public boolean onTouchEvent(MotionEvent event) {
isTouch = true;
if(canToggle){
return super.onTouchEvent(event);
}else{
//这里要设置成false,因为我想点击CheckBox时,CheckBox不要拦截事件。
return false;
}
}
2.自定义OnTouchCheckedChangeListener,限制只有Touch事件触发的onChange才去回调。
public void setOnTouchCheckedChangeListener(OnTouchCheckedChangeListener onTouchCheckedChangeListener) {
this.onTouchCheckedChangeListener = onTouchCheckedChangeListener;
this.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isTouch && onTouchCheckedChangeListener != null){
onTouchCheckedChangeListener.onCheckedChanged(buttonView, isChecked);
}
isTouch = false;
}
});
}
/**
* 只监听Touch状态下的状态变化
*/
public abstract static class OnTouchCheckedChangeListener implements OnCheckedChangeListener{}
最终自定义CheckBox的源码
package com.szlanyou.baseappmodule.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* @author 陈章
* create at 2020/12/4 14:32
* desc:
* 1)可以设置不允许手动点击改变状态的CheckBox,只能通过调用{@link #setChecked(boolean)}或者{@link #toggle()}方法改变选中状态。
* 2)可以监听只有在手动点击下状态改变,通过{@link #setChecked(boolean)}或者{@link #toggle()}触发的状态变化则不监听。
*/
public class ToggleControlCheckBox extends androidx.appcompat.widget.AppCompatCheckBox {
private boolean canToggle = true;
private boolean isTouch = false;
private OnTouchCheckedChangeListener onTouchCheckedChangeListener;
public ToggleControlCheckBox(@NonNull Context context) {
super(context);
}
public ToggleControlCheckBox(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ToggleControlCheckBox(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
isTouch = true;
if(canToggle){
return super.onTouchEvent(event);
}else{
//这里要设置成false,因为我想点击CheckBox时,CheckBox不要拦截事件。
return false;
}
}
public void setCanToggle(boolean canToggle) {
this.canToggle = canToggle;
}
public OnTouchCheckedChangeListener getOnTouchCheckedChangeListener() {
return onTouchCheckedChangeListener;
}
public void setOnTouchCheckedChangeListener(OnTouchCheckedChangeListener onTouchCheckedChangeListener) {
this.onTouchCheckedChangeListener = onTouchCheckedChangeListener;
this.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isTouch && onTouchCheckedChangeListener != null){
onTouchCheckedChangeListener.onCheckedChanged(buttonView, isChecked);
}
isTouch = false;
}
});
}
/**
* 只监听Touch状态下的状态变化
*/
public abstract static class OnTouchCheckedChangeListener implements OnCheckedChangeListener{}
}
0 条评论