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;
        }
});