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()方法就正常了。暂时也没发现其他异常。