Skip to content

Instantly share code, notes, and snippets.

@ksoichiro
Created August 19, 2012 11:22
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save ksoichiro/3394338 to your computer and use it in GitHub Desktop.
Save ksoichiro/3394338 to your computer and use it in GitHub Desktop.
Android: Clickable URL and clickable TextView
package com.blogspot.ksoichiro.linktest;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class LinkUtils {
public static final Pattern URL_PATTERN =
Pattern.compile("((https?|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+))");
public interface OnClickListener {
void onLinkClicked(final String link);
void onClicked();
}
static class SensibleUrlSpan extends URLSpan {
/** Pattern to match. */
private Pattern mPattern;
public SensibleUrlSpan(String url, Pattern pattern) {
super(url);
mPattern = pattern;
}
public boolean onClickSpan(View widget) {
boolean matched = mPattern.matcher(getURL()).matches();
if (matched) {
super.onClick(widget);
}
return matched;
}
}
static class SensibleLinkMovementMethod extends LinkMovementMethod {
private boolean mLinkClicked;
private String mClickedLink;
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_UP) {
mLinkClicked = false;
mClickedLink = null;
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) {
SensibleUrlSpan span = (SensibleUrlSpan) link[0];
mLinkClicked = span.onClickSpan(widget);
mClickedLink = span.getURL();
return mLinkClicked;
}
}
super.onTouchEvent(widget, buffer, event);
return false;
}
public boolean isLinkClicked() {
return mLinkClicked;
}
public String getClickedLink() {
return mClickedLink;
}
}
public static void autoLink(final TextView view, final OnClickListener listener) {
autoLink(view, listener, null);
}
public static void autoLink(final TextView view, final OnClickListener listener,
final String patternStr) {
String text = view.getText().toString();
if (TextUtils.isEmpty(text)) {
return;
}
Spannable spannable = new SpannableString(text);
Pattern pattern;
if (TextUtils.isEmpty(patternStr)) {
pattern = URL_PATTERN;
} else {
pattern = Pattern.compile(patternStr);
}
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
SensibleUrlSpan urlSpan = new SensibleUrlSpan(matcher.group(1), pattern);
spannable.setSpan(urlSpan, matcher.start(1), matcher.end(1),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
view.setText(spannable, TextView.BufferType.SPANNABLE);
final SensibleLinkMovementMethod method = new SensibleLinkMovementMethod();
view.setMovementMethod(method);
if (listener != null) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (method.isLinkClicked()) {
listener.onLinkClicked(method.getClickedLink());
} else {
listener.onClicked();
}
}
});
}
}
}
package com.blogspot.ksoichiro.linktest;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String testStr = "URLの形式のみブラウザで開きます。http://www.yahoo.co.jp/とhttp://d.hatena.ne.jp/をリンク表示にしてクリック可能にしました。";
TextView textView = new TextView(this);
textView.setTextSize(20);
textView.setText(testStr);
LinkUtils.autoLink(textView, new LinkUtils.OnClickListener() {
@Override
public void onLinkClicked(final String link) {
Log.i("SensibleUrlSpan", "リンククリック:" + link);
}
@Override
public void onClicked() {
Log.i("SensibleUrlSpan", "ビュークリック");
}
});
setContentView(textView);
}
}
@christophesmet
Copy link

Hey, I updated the URL_PATTER for a better match.
https://gist.github.com/christophesmet/99950463a3f21cb273a8

@noneorone
Copy link

11-05 09:43:58.044: E/MessageQueue-JNI(1535): java.lang.ClassCastException: android.text.style.URLSpan cannot be cast to com.zzxy.bbczjh.util.LinkUtils$SensibleUrlSpan
11-05 09:43:58.044: E/MessageQueue-JNI(1535): at com.zzxy.bbczjh.util.LinkUtils$SensibleLinkMovementMethod.onTouchEvent(LinkUtils.java:75)

@pandey-adarsh147
Copy link

Not working in ListView adapter any Idea?

@yesidlazaro
Copy link

java.lang.ClassCastException: android.text.style.URLSpan cannot be cast to com.twnel.android.util.LinkUtils$SensibleUrlSpan
at com.twnel.android.util.LinkUtils$SensibleLinkMovementMethod.onTouchEvent(LinkUtils.java:78)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment