处理textview链接点击我的android应用程序

我目前正在像这样在TextView中呈现HTMLinput:

tv.setText(Html.fromHtml("<a href='test'>test</a>")); 

所显示的HTML是通过外部资源提供给我的,所以我不能按照我的意思去改变,但是我可以用一些正则expression式来篡改HTML,把href的值改成别的东西。

我想要的是能够直接从应用程序中处理链接点击,而不是让链接打开一个浏览器窗口。 这完全可以实现吗? 我猜测有可能将href-value的协议设置为“myApp://”,然后注册一些让我的应用程序处理该协议的东西。 如果这确实是最好的方法,我想知道这是怎么做的,但我希望有一个更简单的方法来说,“当在这个文本框中点击链接,我想提出一个事件,接收链接的href值作为input参数“

差不多一年之后,我解决了一些特殊的问题。 由于我希望链接被我自己的应用程序处理,所以有一个解决scheme更简单一些。

除了默认的意图filter,我只是让我的目标活动监听ACTION_VIEW意图,特别是那些与schemecom.package.name

 <intent-filter> <category android:name="android.intent.category.DEFAULT" /> <action android:name="android.intent.action.VIEW" /> <data android:scheme="com.package.name" /> </intent-filter> 

这意味着以com.package.name://开头的链接将被我的活动处理。

所以我所要做的就是构build一个包含我想传达的信息的URL:

 com.package.name://action-to-perform/id-that-might-be-needed/ 

在我的目标活动中,我可以检索这个地址:

 Uri data = getIntent().getData(); 

在我的例子中,我可以简单地检查data的空值,因为当它不是null时,我会知道它是通过这样的链接调用的。 从那里,我从URL提取我需要的指示,以便能够显示适当的数据。

另一种方式,从Linkify借鉴一下,但允许您自定义您的处理。

自定义跨度类别:

 public class ClickSpan extends ClickableSpan { private OnClickListener mListener; public ClickSpan(OnClickListener listener) { mListener = listener; } @Override public void onClick(View widget) { if (mListener != null) mListener.onClick(); } public interface OnClickListener { void onClick(); } } 

帮手function:

 public static void clickify(TextView view, final String clickableText, final ClickSpan.OnClickListener listener) { CharSequence text = view.getText(); String string = text.toString(); ClickSpan span = new ClickSpan(listener); int start = string.indexOf(clickableText); int end = start + clickableText.length(); if (start == -1) return; if (text instanceof Spannable) { ((Spannable)text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else { SpannableString s = SpannableString.valueOf(text); s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); view.setText(s); } MovementMethod m = view.getMovementMethod(); if ((m == null) || !(m instanceof LinkMovementMethod)) { view.setMovementMethod(LinkMovementMethod.getInstance()); } } 

用法:

  clickify(textView, clickText,new ClickSpan.OnClickListener() { @Override public void onClick() { // do something } }); 

如果文本视图中有多个链接。 例如,textview有“https://”和“tel no”,我们可以自定义LinkMovement方法,并根据模式处理单词的点击。 附加的是定制的链接移动方法。

 public class CustomLinkMovementMethod extends LinkMovementMethod { private static Context movementContext; private static CustomLinkMovementMethod linkMovementMethod = new CustomLinkMovementMethod(); public boolean onTouchEvent(android.widget.TextView widget, android.text.Spannable buffer, android.view.MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_UP) { 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); URLSpan[] link = buffer.getSpans(off, off, URLSpan.class); if (link.length != 0) { String url = link[0].getURL(); if (url.startsWith("https")) { Log.d("Link", url); Toast.makeText(movementContext, "Link was clicked", Toast.LENGTH_LONG).show(); } else if (url.startsWith("tel")) { Log.d("Link", url); Toast.makeText(movementContext, "Tel was clicked", Toast.LENGTH_LONG).show(); } else if (url.startsWith("mailto")) { Log.d("Link", url); Toast.makeText(movementContext, "Mail link was clicked", Toast.LENGTH_LONG).show(); } return true; } } return super.onTouchEvent(widget, buffer, event); } public static android.text.method.MovementMethod getInstance(Context c) { movementContext = c; return linkMovementMethod; } 

这应该从以下方式从textview中调用:

 textViewObject.setMovementMethod(CustomLinkMovementMethod.getInstance(context)); 

这是一个更通用的解决scheme,基于@Arun的答案

 public abstract class TextViewLinkHandler extends LinkMovementMethod { public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { if (event.getAction() != MotionEvent.ACTION_UP) return super.onTouchEvent(widget, buffer, event); 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); URLSpan[] link = buffer.getSpans(off, off, URLSpan.class); if (link.length != 0) { onLinkClick(link[0].getURL()); } return true; } abstract public void onLinkClick(String url); } 

要使用它只需实现TextViewLinkHandler类的onLinkClick 。 例如:

  textView.setMovementMethod(new TextViewLinkHandler() { @Override public void onLinkClick(String url) { Toast.makeText(textView.getContext(), url, Toast.LENGTH_SHORT); } }); 

其非常简单的添加此行到您的代码:

 tv.setMovementMethod(LinkMovementMethod.getInstance()); 

我已经实现了一个小的类的帮助下,你可以处理长时间的TextView本身点击和点击TextView中的链接。

布局

 TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="all"/> 

TextViewClickMovement.java

 import android.content.Context; import android.text.Layout; import android.text.Spannable; import android.text.method.LinkMovementMethod; import android.text.style.ClickableSpan; import android.util.Patterns; import android.view.GestureDetector; import android.view.MotionEvent; import android.widget.TextView; public class TextViewClickMovement extends LinkMovementMethod { private final String TAG = TextViewClickMovement.class.getSimpleName(); private final OnTextViewClickMovementListener mListener; private final GestureDetector mGestureDetector; private TextView mWidget; private Spannable mBuffer; public enum LinkType { /** Indicates that phone link was clicked */ PHONE, /** Identifies that URL was clicked */ WEB_URL, /** Identifies that Email Address was clicked */ EMAIL_ADDRESS, /** Indicates that none of above mentioned were clicked */ NONE } /** * Interface used to handle Long clicks on the {@link TextView} and taps * on the phone, web, mail links inside of {@link TextView}. */ public interface OnTextViewClickMovementListener { /** * This method will be invoked when user press and hold * finger on the {@link TextView} * * @param linkText Text which contains link on which user presses. * @param linkType Type of the link can be one of {@link LinkType} enumeration */ void onLinkClicked(final String linkText, final LinkType linkType); /** * * @param text Whole text of {@link TextView} */ void onLongClick(final String text); } public TextViewClickMovement(final OnTextViewClickMovementListener listener, final Context context) { mListener = listener; mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener()); } @Override public boolean onTouchEvent(final TextView widget, final Spannable buffer, final MotionEvent event) { mWidget = widget; mBuffer = buffer; mGestureDetector.onTouchEvent(event); return false; } /** * Detects various gestures and events. * Notify users when a particular motion event has occurred. */ class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent event) { // Notified when a tap occurs. return true; } @Override public void onLongPress(MotionEvent e) { // Notified when a long press occurs. final String text = mBuffer.toString(); if (mListener != null) { Log.d(TAG, "----> Long Click Occurs on TextView with ID: " + mWidget.getId() + "\n" + "Text: " + text + "\n<----"); mListener.onLongClick(text); } } @Override public boolean onSingleTapConfirmed(MotionEvent event) { // Notified when tap occurs. final String linkText = getLinkText(mWidget, mBuffer, event); LinkType linkType = LinkType.NONE; if (Patterns.PHONE.matcher(linkText).matches()) { linkType = LinkType.PHONE; } else if (Patterns.WEB_URL.matcher(linkText).matches()) { linkType = LinkType.WEB_URL; } else if (Patterns.EMAIL_ADDRESS.matcher(linkText).matches()) { linkType = LinkType.EMAIL_ADDRESS; } if (mListener != null) { Log.d(TAG, "----> Tap Occurs on TextView with ID: " + mWidget.getId() + "\n" + "Link Text: " + linkText + "\n" + "Link Type: " + linkType + "\n<----"); mListener.onLinkClicked(linkText, linkType); } return false; } private String getLinkText(final TextView widget, final Spannable buffer, final MotionEvent event) { 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) { return buffer.subSequence(buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0])).toString(); } return ""; } } } 

用法

 TextView tv = (TextView) v.findViewById(R.id.textview); tv.setText(Html.fromHtml("<a href='test'>test</a>")); textView.setMovementMethod(new TextViewClickMovement(this, context)); 

链接

希望这个帮助! 你可以在这里find代码。

我使用的最好的方式,它总是为我工作

 android:autoLink="web" 

只是为了使用我创build的库来分享替代解决scheme。 用Textoo ,这可以实现如下:

 TextView locNotFound = Textoo .config((TextView) findViewById(R.id.view_location_disabled)) .addLinksHandler(new LinksHandler() { @Override public boolean onClick(View view, String url) { if ("internal://settings/location".equals(url)) { Intent locSettings = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(locSettings); return true; } else { return false; } } }) .apply(); 

或者使用dynamicHTML源代码:

 String htmlSource = "Links: <a href='http://www.google.com'>Google</a>"; Spanned linksLoggingText = Textoo .config(htmlSource) .parseHtml() .addLinksHandler(new LinksHandler() { @Override public boolean onClick(View view, String url) { Log.i("MyActivity", "Linking to google..."); return false; // event not handled. Continue default processing ie link to google } }) .apply(); textView.setText(linksLoggingText); 

谁在这里寻找更多的select是一个

 // Set text within a `TextView` TextView textView = (TextView) findViewById(R.id.textView); textView.setText("Hey @sarah, where did @jim go? #lost"); // Style clickable spans based on pattern new PatternEditableBuilder(). addPattern(Pattern.compile("\\@(\\w+)"), Color.BLUE, new PatternEditableBuilder.SpannableClickedListener() { @Override public void onSpanClicked(String text) { Toast.makeText(MainActivity.this, "Clicked username: " + text, Toast.LENGTH_SHORT).show(); } }).into(textView); 

资源: CodePath

我通过使用例如将TextView的颜色更改为蓝色:

 android:textColor="#3399FF" 

在xml文件中。 这里解释如何使下划线。

然后使用它的onClick属性来指定一个方法(我猜你可以调用setOnClickListener(this)另一种方式),例如:

 myTextView.setOnClickListener(new OnClickListener() { public void onClick(View v) { doSomething(); } }); 

在这种方法中,我可以像平常那样做任何我想做的事情,比如发起一个意图。 请注意,您仍然必须执行正常的myTextView.setMovementMethod(LinkMovementMethod.getInstance()); 东西,就像在你的活动的onCreate()方法中一样。

这个答案延伸了Jonathan S的出色解决scheme:

您可以使用以下方法从文本中提取链接:

 private static ArrayList<String> getLinksFromText(String text) { ArrayList links = new ArrayList(); String regex = "\(?\b((http|https)://www[.])[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*[-A-Za-z0-9+&@#/%=~_()|]"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(text); while (m.find()) { String urlStr = m.group(); if (urlStr.startsWith("(") && urlStr.endsWith(")")) { urlStr = urlStr.substring(1, urlStr.length() - 1); } links.add(urlStr); } return links; } 

这可以用来删除clickify()方法中的一个参数:

 public static void clickify(TextView view, final ClickSpan.OnClickListener listener) { CharSequence text = view.getText(); String string = text.toString(); ArrayList<String> linksInText = getLinksFromText(string); if (linksInText.isEmpty()){ return; } String clickableText = linksInText.get(0); ClickSpan span = new ClickSpan(listener,clickableText); int start = string.indexOf(clickableText); int end = start + clickableText.length(); if (start == -1) return; if (text instanceof Spannable) { ((Spannable) text).setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else { SpannableString s = SpannableString.valueOf(text); s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); view.setText(s); } MovementMethod m = view.getMovementMethod(); if ((m == null) || !(m instanceof LinkMovementMethod)) { view.setMovementMethod(LinkMovementMethod.getInstance()); } } 

ClickSpan的一些更改:

 public static class ClickSpan extends ClickableSpan { private String mClickableText; private OnClickListener mListener; public ClickSpan(OnClickListener listener, String clickableText) { mListener = listener; mClickableText = clickableText; } @Override public void onClick(View widget) { if (mListener != null) mListener.onClick(mClickableText); } public interface OnClickListener { void onClick(String clickableText); } } 

现在你可以简单地在TextView上设置文本,然后添加一个监听器:

 TextViewUtils.clickify(textWithLink,new TextUtils.ClickSpan.OnClickListener(){ @Override public void onClick(String clickableText){ //action... } }); 

示例:假设您已经在textview中设置了一些文本,并且想要提供特定文本expression式的链接:“单击#facebook将带您到facebook.com”

在布局xml中:

 <TextView android:id="@+id/testtext" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 

在活动中:

 String text = "Click on #facebook will take you to facebook.com"; tv.setText(text); Pattern tagMatcher = Pattern.compile("[#]+[A-Za-z0-9-_]+\\b"); String newActivityURL = "content://ankit.testactivity/"; Linkify.addLinks(tv, tagMatcher, newActivityURL); 

还要创build一个标签提供者:

 public class TagProvider extends ContentProvider { @Override public int delete(Uri arg0, String arg1, String[] arg2) { // TODO Auto-generated method stub return 0; } @Override public String getType(Uri arg0) { return "vnd.android.cursor.item/vnd.cc.tag"; } @Override public Uri insert(Uri arg0, ContentValues arg1) { // TODO Auto-generated method stub return null; } @Override public boolean onCreate() { // TODO Auto-generated method stub return false; } @Override public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) { // TODO Auto-generated method stub return null; } @Override public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) { // TODO Auto-generated method stub return 0; } } 

在清单文件中,作为提供者和testing活动的条目:

 <provider android:name="ankit.TagProvider" android:authorities="ankit.testactivity" /> <activity android:name=".TestActivity" android:label = "@string/app_name"> <intent-filter > <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.cursor.item/vnd.cc.tag" /> </intent-filter> </activity> 

现在当你点击#facebook时,它会调用testactivtiy。 在testing活动中,您可以获取数据:

 Uri uri = getIntent().getData(); 
 public static void setTextViewFromHtmlWithLinkClickable(TextView textView, String text) { Spanned result; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { result = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY); } else { result = Html.fromHtml(text); } textView.setText(result); textView.setMovementMethod(LinkMovementMethod.getInstance()); }