android TextView中ClickableSpan与文本自由复制(TextIsSelectable)冲突问题
2014-09-02 17:13
507 查看
上一篇文章解决了UrlSpan与文本中url链接识别和设置的问题,超链接可以正常显示了。但由于用户期望TextView中的文章内容可以自由复制,这个需求嘛实现起来就是设置下textView.setTextIsSelectable(true)就可以了。
天真的以为没啥问题,版本转测。立马出现一个严重问题,第一次点击超链接打开的网址与实际不符,第二次点击超链接才能打开正确的网址,或者说每次点击超链接文本打开的网址都是上一次点击区域内的超链接url。
贴TextView::onTouchEvent的源码
-java代码
可以看到onTouchEvent()方法中涉及ClickableSpan处理的有两个关键点
关键点1.执行MovementMothed::onTouchEvent()方法(上面源码中蓝色部分)------mMovement.onTouchEvent(this, (Spannable) mText, event);
关键点2.上面源码中蓝色部分,在TextView的LinksClickable、AutoLink、TextIsSelectable属性都已设置情况下,获取当前选择区域内的ClickableSpan并执行其onClick()方法;
对于1,支持ClickableSpan使用的MovementMothed为LinkMovementMethod。而支持文本自由复制的MovementMothed为ArrowKeyMovementMethod。
设置了TextView的textIsSelectable属性后,TextView会自动设置器mMovement成员为ArrowKeyMovementMethod实例。此时文本才可以自由复制。也即上面关键点1中执行的是ArrowKeyMovementMethod::onTouchEvent()方法:长按时弹出复制、粘贴、选择菜单。
而我也正好设置了TextView的autoLink属性,故关键点2的if判断通过,但此时TextView中当前选择文本为记录的用户上次点击位置,故打开的链接也就是用户上次点击位置对应的链接url了,若用户上次点击位置没有超链接,就不会弹出浏览器打开链接的页面。
当然若去掉TextView的autoLink属性,用户点击超链接将不会产生任何操作。
总结:TextView的超链接点击打开和文本自由复制两个功能是互斥的,没办法同时支持。终于只要为啥百度贴吧
帖子中不能自由复制文本了。。。
两个功能都很重要,不可能删除哪一个。相比较之下文本自由复制功能是帖子内容TextView中所有文本都需要支持的功能,而超链接点击打开功能仅仅是超链接部分文本需要的。故我选择设置TextVie的textIsSelectable属性,即默认支持文本自由复制(此时不能修改mMovement为ArrowKeyMovementMethod外的其他MovementMothed,不然自由复制功能将会失效)。然后重写TextView::onTouchEvent()方法来支持超链接点击打开功能。具体如下:
-java代码
上面的TextView::handleLinkMovementMethod()方法,其实就是从LinkMovementMethod::onTouchEvent()方法,稍微修改了下。主要就是两个逻辑:1.ACTION_DOWN时,将点击位置的超链接选中;2.ACTION_UP时,在非长按情况下,执行ClickableSpan的点击处理。
然后就完美解决了TextView的超链接点击打开和文本自由复制两个功能是互斥问题。
天真的以为没啥问题,版本转测。立马出现一个严重问题,第一次点击超链接打开的网址与实际不符,第二次点击超链接才能打开正确的网址,或者说每次点击超链接文本打开的网址都是上一次点击区域内的超链接url。
贴TextView::onTouchEvent的源码
-java代码
01 | @Override |
02 | public boolean onTouchEvent(MotionEvent event) { |
03 | 。。。 |
04 | final boolean superResult = super .onTouchEvent(event); |
05 | if ((mMovement != null || onCheckIsTextEditor()) && isEnabled() |
06 | && mText instanceof Spannable && mLayout != null ) { |
07 | boolean handled = false ; |
08 |
09 | if (mMovement != null ) { |
10 | handled |= mMovement.onTouchEvent( this , (Spannable) mText, event); |
11 | } |
12 |
13 | if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && mTextIsSelectable) { |
14 | // The LinkMovementMethod which should handle taps on links has not been installed |
15 | // on non editable text that support text selection. |
16 | // We reproduce its behavior here to open links for these. |
17 | ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(), |
18 | getSelectionEnd(), ClickableSpan. class ); |
19 |
20 | if (links.length != 0 ) { |
21 | links[ 0 ].onClick( this ); |
22 | handled = true ; |
23 | } |
24 | } |
25 | 。。。 |
26 | if (handled) { |
27 | return true ; |
28 | } |
29 | } |
30 | return superResult; |
31 | } |
关键点1.执行MovementMothed::onTouchEvent()方法(上面源码中蓝色部分)------mMovement.onTouchEvent(this, (Spannable) mText, event);
关键点2.上面源码中蓝色部分,在TextView的LinksClickable、AutoLink、TextIsSelectable属性都已设置情况下,获取当前选择区域内的ClickableSpan并执行其onClick()方法;
对于1,支持ClickableSpan使用的MovementMothed为LinkMovementMethod。而支持文本自由复制的MovementMothed为ArrowKeyMovementMethod。
设置了TextView的textIsSelectable属性后,TextView会自动设置器mMovement成员为ArrowKeyMovementMethod实例。此时文本才可以自由复制。也即上面关键点1中执行的是ArrowKeyMovementMethod::onTouchEvent()方法:长按时弹出复制、粘贴、选择菜单。
而我也正好设置了TextView的autoLink属性,故关键点2的if判断通过,但此时TextView中当前选择文本为记录的用户上次点击位置,故打开的链接也就是用户上次点击位置对应的链接url了,若用户上次点击位置没有超链接,就不会弹出浏览器打开链接的页面。
当然若去掉TextView的autoLink属性,用户点击超链接将不会产生任何操作。
总结:TextView的超链接点击打开和文本自由复制两个功能是互斥的,没办法同时支持。终于只要为啥百度贴吧
帖子中不能自由复制文本了。。。
两个功能都很重要,不可能删除哪一个。相比较之下文本自由复制功能是帖子内容TextView中所有文本都需要支持的功能,而超链接点击打开功能仅仅是超链接部分文本需要的。故我选择设置TextVie的textIsSelectable属性,即默认支持文本自由复制(此时不能修改mMovement为ArrowKeyMovementMethod外的其他MovementMothed,不然自由复制功能将会失效)。然后重写TextView::onTouchEvent()方法来支持超链接点击打开功能。具体如下:
-java代码
01 | public class CustomTextView extends TextView { |
02 |
03 | private long mLastActionDownTime = - 1 ; |
04 | public CustomTextView(Context context) { |
05 | super (context); |
06 | } |
07 |
08 | public CustomTextView(Context context, AttributeSet attrs) { |
09 | super (context, attrs); |
10 | } |
11 |
12 | public CustomTextView(Context context, AttributeSet attrs, int defStyle) { |
13 | super (context, attrs, defStyle); |
14 | } |
15 |
16 | @Override |
17 | public boolean onTouchEvent(MotionEvent event) { |
18 | CharSequence text = getText(); |
19 | if (text != null && text instanceof Spannable) { |
20 | handleLinkMovementMethod( this , (Spannable)text, event); |
21 | } |
22 |
23 | return super .onTouchEvent(event); |
24 | } |
25 |
26 | private boolean handleLinkMovementMethod(TextView widget, Spannable buffer, MotionEvent event) { |
27 | int action = event.getAction(); |
28 |
29 | if (action == MotionEvent.ACTION_UP || |
30 | action == MotionEvent.ACTION_DOWN) { |
31 | int x = ( int ) event.getX(); |
32 | int y = ( int ) event.getY(); |
33 |
34 | x -= widget.getTotalPaddingLeft(); |
35 | y -= widget.getTotalPaddingTop(); |
36 |
37 | x += widget.getScrollX(); |
38 | y += widget.getScrollY(); |
39 |
40 | Layout layout = widget.getLayout(); |
41 | int line = layout.getLineForVertical(y); |
42 | int off = layout.getOffsetForHorizontal(line, x); |
43 |
44 | ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan. class ); |
45 |
46 | if (link.length != 0 ) { |
47 | if (action == MotionEvent.ACTION_UP) { |
48 | long actionUpTime = System.currentTimeMillis(); |
49 | if (actionUpTime - mLastActionDownTime > ViewConfiguration.getLongPressTimeout()) { |
50 | //长按事件,取消LinkMovementMethod处理,即不处理ClickableSpan点击事件 |
51 | return false ; |
52 | } |
53 | link[ 0 ].onClick(widget); |
54 | Selection.removeSelection(buffer); |
55 | } else if (action == MotionEvent.ACTION_DOWN) { |
56 | Selection.setSelection(buffer, buffer.getSpanStart(link[ 0 ]), buffer.getSpanEnd(link[ 0 ])); |
57 | mLastActionDownTime = System.currentTimeMillis(); |
58 | } |
59 | } |
60 | } |
61 |
62 | return false ; |
63 | } |
64 | } |
然后就完美解决了TextView的超链接点击打开和文本自由复制两个功能是互斥问题。
相关文章推荐
- TextView中ClickableSpan与文本自由复制(TextIsSelectable)冲突问题
- TextView 复制 textIsSelectable不起作用的问题
- TextView中UrlSpan与文本中的超链接冲突问题
- android TextView中UrlSpan与文本中的超链接冲突问题
- android TextView中UrlSpan与文本中的超链接冲突问题
- TextView 显示多行文本时只显示了3行的问题
- 解决:因为复制包含SVN文件造成的版本冲突问题
- 灵格斯与word2007或2010冲突,复制时word关闭问题的解决
- Android文本EditorText相关问题
- 破解PDF加密,自由复制文本
- 解决服务器复制中SID冲突问题
- android TextView多行文本(超过3行)使用ellipsize属性无效问题的解决方法
- 使用dbms_rectifier_diff解决高级复制中的数据冲突问题
- 溢出文本text-overflow的使用问题
- 溢出文本text-overflow的使用问题分析及解决
- 文库自由复制(纯文本)
- 解决chrome使用text-shadow(文本阴影)css时汉字发虚的问题
- VM虚拟机复制后,MAC地址冲突问题
- Android TextView支持拷贝&粘帖 textIsSelectable
- 解决服务器复制中SID冲突问题