下午产品经理提了个需求:要在服务端传来的文本中用应用内置浏览器打开超链接。
去网上搜了下,找到了个方法:
textView.setMovementMethod(LinkMovementMethod.getInstance());
很遗憾,发现这只是跳转到浏览器的方法。
又看了下网友们的方法,基本都要涉及到SpannableString的设置;或是自定义UrlSpan,重写它的onClick方法;有些还要遍历文本寻找以http开头的字符串。总之就是很麻烦。
这些方法很多最后都要用到textView.setText()的方法,但是因为我要解析图片,这个方法已经用掉了,无法重复使用:
textView.setText(Html.fromHtml(str, new MImageGetter(textView, getApplicationContext()), null));
所以这些方法都行不通。那行,换种思路,我看看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);
}
可以看到,执行跳转的语句是在:
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
}
这样就简单了,我就自定义了一个类WebLinkMethod来继承LinkMovementMethod 并重写onTouchEvent()这个方法:
public class WebLinkMethod extends LinkMovementMethod {
private static WebLinkMethod instance;
private Context context;
private WebLinkMethod(Context context) {
this.context = context;
}
public static MovementMethod getInstance(Context context) {
if (instance == null)
instance = new WebLinkMethod(context);
return instance;
}
@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);
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
// link[0].onClick(widget);
Intent intent = new Intent(context, H5Activity.class);
intent.putExtra("url", link[0].getURL());
context.startActivity(intent);
} 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);
}
}
说下几个改动(代码中注释的两行):
- 由于页面跳转我们需要用到原页面的上下文,于是修改了构造函数,加入了Context;
- URLSpan是ClickableSpan的子类,实现了getURL()的方法,所以这里要换成它我们才能取到链接地址,link[0]就是我们点击到的超链接字符串,通过link[0].getURL()我们可以获得它的链接地址传给下个页面;
- 有了上下文context和链接地址url,我们就可以把原来的跳转语句换成我们自己的,跳转到包含webView的界面就大功告成了。
最后的方案虽然很简单,但是网上的解答都不尽如人意。所以献丑记下解决的过程,希望能帮助到有需要的人~