TextView添加ClickableSpan和LinkMovementMethod之间的关系
来源:互联网 发布:上海师范大学网络支付 编辑:程序博客网 时间:2024/04/28 16:39
在Android中实现部分文字可点击及变色一般都会想到使用ClickableSpan,于是就有了下面的代码
TextView tv = (TextView) findViewById(R.id.textview);SpannableString ss = new SpannableString("Java一次编译到处运行并不适用Android");ss.setSpan(new MyClickSpan("java"),0,4,0);<pre name="code" class="java">tv.setText(ss);
运行发现并不生效,搜索发现需要添加下面的方法
tv.setMovementMethod(LinkMovementMethod.getInstance());再次运行,发现可以点击了,到这里基本满足的大部分的开发工作,但是作为苦逼的开发者又要满足产品经理(不光点击还要图文混排,oh my god,请给我时间),还要过QA那关。
先说点击的问题,后面再说图文混排。上面的代码目前来看貌似没有问题,但是笔者目前已经发现2个问题:
1.如果textview除了响应部分文件的点击事件外,其余部分还要响应其他操作就会有问题(会同事回调span的onclick和textview的click)
2.如果textview设置的maxLine,并且文字高度超过了maxline的值,那么就会出现文字可以滚动,即使设置了ellipsize的值,原因就是设置了LinkMovementMethod
这2个BUG的存在必然不能过测试,于是各种度娘和google,没有找到满意的答案,于是只能自己动手啃TextView源码。
由于点击必然涉及到onClick或者onTouch事件,先找onClick,但是在TextView没有找到onClick的相关代码。然后就看onTouchEvent方法,
于是找到下面的代码
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled() && mText instanceof Spannable && mLayout != null) { boolean handled = false; if (mMovement != null) { handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } final boolean textIsSelectable = isTextSelectable(); if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) { // The LinkMovementMethod which should handle taps on links has not been installed // on non editable text that support text selection. // We reproduce its behavior here to open links for these. ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(), getSelectionEnd(), ClickableSpan.class); if (links.length > 0) { links[0].onClick(this); handled = true; } }
代码中
if (mMovement != null) { handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); }
部分就是通过setMovementMethod方法设置的对象。由于我们设置的是LinkMovementMethod的一个实例,继续打开LinkMovementMethod源码查看其中onTouchEvent的代码。
@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])); } return true; } else { Selection.removeSelection(buffer); } } return super.onTouchEvent(widget, buffer, event); }一目了然的发现ClickableSpan的实现原理。
由于tv.setMovementMethod(LinkMovementMethod.getInstance());代码上述红色2个问题,再看源码,我们的解决方法如果就是直接给TextView设置onTouchEvent。
下面就是自己实现ClickableSpan点击的代码
tv.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); TextView tv = (TextView) v; CharSequence text = tv.getText(); if (text instanceof SpannableString) { if (action == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); x -= tv.getTotalPaddingLeft(); y -= tv.getTotalPaddingTop(); x += tv.getScrollX(); y += tv.getScrollY(); Layout layout = tv.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); ClickableSpan[] link = ((SpannableString)text).getSpans(off, off, ClickableSpan.class); if (link.length != 0) { link[0].onClick(tv); } else { //do textview click event } } } return true; } });
运行调试,发现再也没有上述的2个问题,大功告成。
虽然最终代码只有很少的一点,但是刚开始遇到问题不知道怎么解决的时候,阅读源码还是很辛苦的,所以平常解决问题多看看源码是有非常大的帮助的。感谢Google把Android开源。
附上MyClickSpan的代码,包含自定义可点击文字颜色:
private class MyClickSpan extends ClickableSpan { private String tag; public MyClickSpan(String tag){ this.tag = tag; } @Override public void onClick(View widget) { Toast.makeText(ClickSpanActivity.this, tag+" is clicked", Toast.LENGTH_SHORT).show(); } @Override public void updateDrawState(TextPaint ds) { ds.setColor(Color.RED); } }
在作者解决完问题后,在stackoverflow发现的同样的问题,附上链接,供大家参考:
https://stackoverflow.com/questions/5183645/android-clickablespan-in-clickable-textview/40214599#40214599
- TextView添加ClickableSpan和LinkMovementMethod之间的关系
- TextView添加ClickableSpan和LinkMovementMethod之间的关系
- ListView的Item里的TextView设置ClickableSpan和LinkMovementMethod导致ListView无法响应点击事件
- linkmovementmethod ClickableSpan
- SpannableString中的LinkMovementMethod和ClickableSpan的实现安卓可点击有颜色文字使用
- SpannableString中的LinkMovementMethod和ClickableSpan的实现安卓可点击有颜色文字使用
- TextView ClickableSpan 事件分发的两个坑
- TextView ClickableSpan 事件分发的两个坑
- TextView 中确定 ClickableSpan 的具体位置
- android TextView ClickableSpan 长按报错
- SpannableString中的ClickableSpan和Textview点击事件冲突解决
- 和之间的关系
- Listview item里面的textView.setMovementMethod(LinkMovementMethod.getInstance()) 让listview 点击生效解决方法
- 解决ListView里TextView设置LinkMovementMethod后导致其ItemClick失效的问题
- android中TextView 添加ClickableSpan后点击选中文字背景问题
- android中TextView 添加ClickableSpan后点击选中文字背景问题
- android中Button和TextView的关系
- ClickableSpan和TouchableSpan:
- Android Studio-设置快速修复错误提示代码 相当于eclipse F1 快捷键
- 所有的排序烦恼
- SpringMVC-提高篇-web.xml
- 国际化你的测试
- 第八周项目1-建立顺序串的算法库
- TextView添加ClickableSpan和LinkMovementMethod之间的关系
- android Tween动画新的理解
- Exception_android_No resource found that matches the given name...
- UI - 关于自动调整内边距问题的细节
- Hive UDF UDTF UDAF 函数
- 【网络】app(retorfit2+RxJava)+javaweb(服务器) retrofit2官方文档实践
- js 得到鼠标的positon 以及相应时间
- Linux运维+系统服务搭建----Could not retrieve mirrorlist
- python杨辉三角