最近在做项目研发过程中有这样一个需求:ListView的Item里的子控件TextView要设置超链接、指定文字高亮显示,然后点击超链接后跳转到指定URL的网页。实现超链接的跳转这很容易,只要通过对TextView设置SpannableString对象即可,即如下代码:

TextView tv = (TextView)findViewById(R.id.tv);
SpannableString sp = new SpannableString("此处为tv里显示的内容");
sp.setSpan( new  URLSpan( "http://www.baidu.com" ), 3 ,  8 , Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(sp);
tv.setMovementMethod(LinkMovementMethod.getInstance());

通过这样的设置后已经能够实现对超链接点击跳转到指定URL的网页了,可麻烦又来了,你会发现点击ListView里的Item时没有任何反应,这是什么原因呢?还记得上面在设置超链接的时候有这样一条语句:tv.setMovementMethod(LinkMovementMethod.getInstance());我们现在把这条语句注释掉看看……run App,结果点击Item的时候能够响应了,可惜超链接又失效了,任你点击也没有响应了,哎,真可谓是“鱼和熊掌不可兼得”。现在已经断定Item失效的原因何在了,那为什么这条语句就会导致Item点击失效呢?源码往往是揭开程序中各种奇芭问题的尖锐利器,让我们跟踪下源码吧……打开TextView的源码文件,截取的关键代码片段如下:

wKioL1LHkTyjKLGQAACZ9Ux9MIg927.jpg

该代码片段是TextView源码的第1421行(Android4.2.2版本),我们发现不能通过重写此方法来解决我们所面临的问题,再继续跟踪LinkMovementMethod这个类吧,关键源码如下:

  @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);
        //获取textview里的超链接对象,并用ClickableSpan数组盛装
        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; //处理结束后返回true,这里一定要注意:当返回值为true时则表示点击事件已经被,就再也不会继续传递了
        } else {
            Selection.removeSelection(buffer);
        }
    }
    return super.onTouchEvent(widget, buffer, event);
}

上述源码为LinkMovementMethod类的第188行。发现了Item点击失效的原因是因为onTouch方法总是返回true而并没有根据不同的情况来作处理,此时大家可能会想直接对这个方法重写就能解决问题了。我和大家想的也一样,对该方法重写,并根据情况返回true或false,遗憾的是还是没有效果,并且在重写的方法里看不到输出的日志信息,暂时不明其中原因,请高手赐教!眼看就要看到光明了,可还是被打击了……不要灰心,面包总是会有的,如果你不放弃的话,哈哈。换个keywords google一下,终于找到国外的一个网站,上面的文章标题是:

wKiom1LHlTzxAlYtAAA2uld9Jrk531.jpg

此时此刻的心情真是如获至宝呀……接着看下去……发现有高人是这样解决的:

wKioL1LHldywWR1tAAE4WGfZtn4031.jpg

发现这位高人本质思路是我们开始分析的本质思路一样,只不过他把要处理的代码放在了TextView的onTouch方法里,然后根据不同的情况返回true或false.在getView()方法里对相应的TextView进行设置吧,Run app……见证奇迹的时刻到了……哈哈,结果真的能行了,耶哦!

原文链接:http://stackoverflow.com/questions/8558732/listview-textview-with-linkmovementmethod-makes-list-item-unclickable

经过此劫,我发现了为什么国外的技术总是比国内要先进得多了,我在此并不是贬低同僚,不支持国产,而是在国内的技术普遍都是你抄我,我抄你的,有的甚至连原文链接都不放……大家可以看看国外这个技术网站人家是怎么解决问题的,真是按部就班,循序渐进的,不仅让你知其然还知其所以然。

欢迎加入QQ讨论群:285077071