您的位置:首页 > 移动开发 > Android开发

android TextView中ClickableSpan与文本自由复制(TextIsSelectable)冲突问题

2014-09-02 17:13 507 查看
上一篇文章解决了UrlSpan与文本中url链接识别和设置的问题,超链接可以正常显示了。但由于用户期望TextView中的文章内容可以自由复制,这个需求嘛实现起来就是设置下textView.setTextIsSelectable(true)就可以了。

天真的以为没啥问题,版本转测。立马出现一个严重问题,第一次点击超链接打开的网址与实际不符,第二次点击超链接才能打开正确的网址,或者说每次点击超链接文本打开的网址都是上一次点击区域内的超链接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
}
可以看到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代码

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::handleLinkMovementMethod()方法,其实就是从LinkMovementMethod::onTouchEvent()方法,稍微修改了下。主要就是两个逻辑:1.ACTION_DOWN时,将点击位置的超链接选中;2.ACTION_UP时,在非长按情况下,执行ClickableSpan的点击处理。

然后就完美解决了TextView的超链接点击打开和文本自由复制两个功能是互斥问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: