LinkMovementMethod那些坑

最近一个需求中,需要对Textview中的文本url进行高亮显示,并自定义点击操作。按照所学知识,开始码代码。

按照交互要求,Textview最多五行,自动显示省略号:

1
2
3
4
5
6
7
8
9
10
11
12
13
<TextView
android:id="@+id/link_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="10dp"
android:layout_marginRight="18dp"
android:ellipsize="end"
android:lineSpacingExtra="2dp"
android:maxLines="5"
android:singleLine="false"
android:textColor="@color/c_2_new"
android:textSize="@dimen/f_2" />

截取文本中的url字段,设置url的span

1
2
3
4
5
6
7
8
9
// ...
if (JudgmentUtil.isNotEmpty(url)) {
textUrlMsg.append(url).append(" ");
// 自定义span
UrlSpanListener clickableSpan = new UrlSpanListener(context, url);
textUrlMsg.setSpan(clickableSpan, startIndex,
textUrlMsg.length(),Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UrlSpanListener extends ClickableSpan {
private String mUrl; // 当前点击的链接
private final Context context;

public UrlSpanListener(Context context, String url) {
this.context = context;
mUrl = url;
}

@Override
public void onClick(@NonNull View widget) {
// 自定义点击操作
}

@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
// 设置颜色等span属性
ds.setColor(context.getResources().getColor(R.color.c_1));
}
}

给Textview设置文本,并设置movementmethod使span点击操作可以执行。

1
2
3
TextView linkText = itemView.findViewById(R.id.link_text);
linkText.setMovementMethod(LinkMovementMethod.getInstance());
linkText.setText(textUrlMsg);

写完代码,美滋滋等待build,想着分分钟解决了需求。然鹅,事情并没有想象的那么简单!!!跑完后发现,textview中的文本,在超过五行后自动省略,但是,并没有显示省略号,文本还会莫名出现向上动了半截字的画面!!!向下面一样

在问遍组内大佬,百度半天,谷歌半天没有结果后(ps:谷歌上说,用SpannableStringBuilder文本,会导致TextView算不准什么时候显示省略号,要自己截取文本添加省略号来实现,这都是骗!人!的!),一个大佬偶然说,你这textview好像就是向上滚动了,我记得textview有个设置不可滚动的属性,你看看设为none后可不可以用。听完火速打来xml开始寻找这个属性,寻找一圈后没找到大佬说的属性,但是给了我灵感,遂谷歌如何设置textview可滚动,谷歌说textview本身自带滚动,若要实现滚动效果,给textview设置movementmethod即可。

1
2
// 设置后textview便可以滚动
linkText.setMovementMethod(ScrollingMovementMethod.getInstance());

再顺藤摸瓜看了一眼,LinkMovementMethod继承自ScrollingMovementMethod,好了,自此知道了问题所在。

因为LinkMovementMethod继承自ScrollingMovementMethod,所以也继承了滚动属性,textview中的文本若可以滚动,便不会自动省略和显示省略号,又因为需求中的textview外层是recyclerview,大部分的滚动监听都被recyclerview捕获,个别触发了textview的滚动导致个别textview文字向上滚动了一点。去掉linkText.setMovementMethod(LinkMovementMethod.getInstance());后,文字可以正常省略了,也有省略号了,也不会自己滚动了,然鹅,span就不能点击了。既然这样,只好自己做点击事件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// textview点击监听
linkText.setOnTouchListener(new View.OnTouchListener() {

long startTime = 0;

@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
startTime = System.currentTimeMillis();
}
TextView tv = (TextView) v;
CharSequence text = tv.getText();
if (text instanceof SpannedString) {
if (action == MotionEvent.ACTION_UP) {
// 避免长按和点击冲突,如果超过300毫秒,认为是在长按,不执行点击操作
if (System.currentTimeMillis() - startTime > 300) {
return false;
}
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);

UrlSpanListener[] link = ((SpannedString) text).getSpans(off, off, UrlSpanListener.class);
if (link.length != 0) {
link[0].onClick(tv);
return true;
}
}
}

return false;
}

});

自此,问题完美解决,需求完美实现~(˘▾˘)~

0%