处理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()); }