ListView的Item里的TextView设置ClickableSpan和LinkMovementMethod导致ListView无法响应点击事件

ListView的Item里的TextView设置ClickableSpan和LinkMovementMethod导致ListView无法响应点击事件

说说我的案例:类似朋友圈,一个列表,是用户发表动态,动态可以有评论,评论可以有二级评论。还有赞。点击评论或二级评论,可以回复评论者。长按动态或评论,弹出dialog。用户名颜色和文字内容不一样,点击用户名可以进入用户个人页面。这些和朋友圈都一样。
因为用户名颜色不同,而且有点击事件,评论内容作为listview 的item也有点击事件,所以,要对textview添加ClickableSpan

第一步,Clickable 继承ClickableSpan

实现OnClick()方法,实现updateDrawState()设置字体样式(可选)。

    class Clickable extends ClickableSpan implements View.OnClickListener {

        private final View.OnClickListener mListener;

        public Clickable(View.OnClickListener l){
            mListener = l;
        }

        @Override
        public void onClick(View v){
            mListener.onClick(v);
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(inflater.getContext().getResources().getColor(R.color.v2_txt_626));
            ds.setUnderlineText(false);
        }
    }
第二步,build字符样式,设置ClickableSapn

可以设置多个。然后将SpannableStringBuilder(或者SpannableString)设置给TextView。就像设置String一样。

SpannableStringBuilder style = new SpannableStringBuilder(content);
style.setSpan(new Clickable(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //your onClick code
                }
            }), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(style);

第三步,少了最后一步,设置的ClickableSpan样式能生效,但是不能点击

textView.setMovementMethod(LinkMovementMethod.getInstance());
衍生问题–1、setMovementMethod导致ListView点击事件失效

上面的方法已经成功在TextView里设置了一个点击区域。但是,这时ListView 不能点击了。
关于这一个问题,网上资料很少,找到两三个,但都指向了一个地址:
http://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable
这里投票最多的方法,经测试有效。我也采用了。
TextView 的点击区域有效,ListView 的onItemClick也有效。OK。

代码copy过来了

public class TextViewFixTouchConsume extends TextView {

    boolean dontConsumeNonUrlClicks = true;
    boolean linkHit;

    public TextViewFixTouchConsume(Context context) {
        super(context);
    }

    public TextViewFixTouchConsume(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TextViewFixTouchConsume(
            Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        linkHit = false;
        boolean res = super.onTouchEvent(event);

        if (dontConsumeNonUrlClicks)
            return linkHit;
        return res;

    }

    public void setTextViewHTML(String html)
    {
        CharSequence sequence = Html.fromHtml(html);
        SpannableStringBuilder strBuilder =
                new SpannableStringBuilder(sequence);
        setText(strBuilder);
    }

    public static class LocalLinkMovementMethod extends LinkMovementMethod {
        static LocalLinkMovementMethod sInstance;


        public static LocalLinkMovementMethod getInstance() {
            if (sInstance == null)
                sInstance = new LocalLinkMovementMethod();

            return sInstance;
        }

        @Override
        public boolean onTouchEvent(TextView widget,
                                    Spannable buffer, MotionEvent event) {
            int action = event.getAction();

            if (action == MotionEvent.ACTION_UP ||
                    action == MotionEvent.ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();

                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();

                x += widget.getScrollX();
                y += widget.getScrollY();

                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);

                ClickableSpan[] link = buffer.getSpans(
                        off, off, ClickableSpan.class);

                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        link[0].onClick(widget);
                    } else if (action == MotionEvent.ACTION_DOWN) {
                        Selection.setSelection(buffer,
                                buffer.getSpanStart(link[0]),
                                buffer.getSpanEnd(link[0]));
                    }

                    if (widget instanceof TextViewFixTouchConsume){
                        ((TextViewFixTouchConsume) widget).linkHit = true;
                    }
                    return true;
                } else {
                    Selection.removeSelection(buffer);
                    Touch.onTouchEvent(widget, buffer, event);
                    return false;
                }
            }
            return Touch.onTouchEvent(widget, buffer, event);
        }
    }

    @Override
    public boolean hasFocus() {
        return false;
    }

}

差点以为没事了,这是测试发现了另一个问题,就是滑动ListView就会触发ListView 的Item的长按事件,当然不止我一个人遇到了这个问题,看这里:
http://my.oschina.net/djone/blog/145057

衍生问题–2、滑动ListView触发长按事件

我对android View事件有一些了解,但始终没达到了如指掌的地步,就像很多珠子串不起来。对于这一连串的bug,我也不是很清楚具体是怎么导致的,也不知道怎么改。但是我知道ListView的Item的执行了长按事件,我就在ListView的onTouch方法以及TextView 的onTouch方法,performLongClick等发放里设置了一些断点,然后滑动ListView,之后在调用栈里发现,ListView的长按是从TextView传过去的,然后我就有了主意,重写TextView.performLongClick()方法。
我在TextViewFixTouchConsume的hasFocus()方法后面添加了下面的代码:

    @Override
    public boolean performLongClick() {
        return false;
    }

之后,滑动导致的onItemLongPress()方法就正常了。暂时也没发现其他异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值