Michael Evans

A bunch of technobabble.

Clickable Links in Android ListViews

| Comments

As part of my newest app (Hacker News for Android), I needed to add the ability to click on links in a ListView, and have them open in the browser, while maintaining the ability for the user to click on the ListView row itself, and have the row listen to the click.

If you try using Linkify and LinkMovementMethod, which you’d use on normal links in a TextView, you’ll find that you can no longer use the ListView properly.

The solution I’m using is slightly modified from this answer on StackOverflow here.

LinkifiedTextView.java
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import android.content.Context;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;

public class LinkifiedTextView extends TextView {
  public LinkifiedTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
      TextView widget = (TextView) this;
      Object text = widget.getText();
      if (text instanceof Spanned) {
          Spannable buffer = (Spannable) text;

          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;
              }
          }

      }

      return false;
  }
}

Just drop this in as a replacement for your normal TextView, and you’ll have the clicks on links in your text intercepted. Hope this helps a fellow developer!


Like this post? Any other Android tips you’d like to hear about? Let me know on Twitter or Google Plus, or leave a comment.

Comments