CheckBox(TextView)与Spanned(链接)的双重响应
客户端注册账号,需要用户确认用户协议及可以查看协议。
选择使用单CheckBox控件配合SpannableString。
但是遇到了一个问题,点击链接后CheckBox也响应了Click导致CheckBox勾选状态改变,这是不期望发生的。
对这个问题,原因查找及解决方式如下。
显示效果
初始代码
1 | CheckBox test = (CheckBox) v.findViewById(R.id.test); |
问题现象
点击链接后不只浏览器被激活并打开www.360.cn,同时复选框勾选状态改变,OnClickListener被激活,如果有其他CheckBox监听也会同时被激活。
问题原因
点击事件,从TextView的onTouchEvent入手1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
if (mEditor != null) mEditor.onTouchEvent(event);
final boolean superResult = super.onTouchEvent(event);
/*
* Don't handle the release after a long press, because it will
* move the selection away from whatever the menu action was
* trying to affect.
*/
if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
mEditor.mDiscardNextActionUp = false;
return superResult;
}
final boolean touchIsFinished = (action == MotionEvent.ACTION_UP) &&
(mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
boolean handled = false;
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
}
其中开始的1
final boolean superResult = super.onTouchEvent(event);
调用了View中得onTouchEvent方法,其中1
2
3
4
5
6if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
激活了控件的Click效果。
从代码分析,这个Click效果可能立即激活,也可能作为一个Message被推入队列稍后执行。
由于现在的主要任务不在这里,就不更深入时序了。
继续回到TextView的onTouchEvent方法中,后面的代码1
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
这里调用了我们代码LinkMovementMethod的onTouchEvent方法
1 | @Override |
其中1
link[0].onClick(widget);
调用了我们代码中ClickableSpan的onClick方法
解决问题
在TextView的onTouchEvent执行前,拦截ACTION_UP方法,并修改为ACTION_CANCEL,避免触发控件performClick1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof TextView) {
TextView text = (TextView) v;
MovementMethod method = text.getMovementMethod();
if (method != null && text.getText() instanceof Spannable
&& event.getAction() == MotionEvent.ACTION_UP) {
if (method.onTouchEvent(text, (Spannable) text.getText(), event)) {
event.setAction(MotionEvent.ACTION_CANCEL);
}
}
}
return false;
}
});