Android TextView с кликабельными ссылками: как захватить клики?
у меня есть TextView, который отображает базовый HTML, содержащий 2+ ссылки. Мне нужно захватить клики по ссылкам и открыть ссылки -- в моем собственном внутреннем веб-представлении (не в браузере по умолчанию.)
наиболее распространенный метод обработки рендеринга ссылок выглядит следующим образом:
String str_links = "<a href='http://google.com'>Google</a><br /><a href='http://facebook.com'>Facebook</a>";
text_view.setLinksClickable(true);
text_view.setMovementMethod(LinkMovementMethod.getInstance());
text_view.setText( Html.fromHtml( str_links ) );
однако это приводит к тому, что ссылки открываются во внутреннем веб-браузере по умолчанию (показывая "полное действие с использованием..." диалог.)
Я попытался реализовать onClickListener, который правильно срабатывает при нажатии ссылки, но я не знаю, как определить, какая ссылка была нажата...
text_view.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// what now...?
}
});
кроме того, я попытался создать пользовательский класс LinkMovementMethod и реализовать onTouchEvent...
public boolean onTouchEvent(TextView widget, Spannable text, MotionEvent event) {
String url = text.toString();
// this doesn't work because the text is not necessarily a URL, or even a single link...
// eg, I don't know how to extract the clicked link from the greater paragraph of text
return false;
}
идеи?
пример решения
Я придумал решение который анализирует ссылки из строки HTML и делает их кликабельными, а затем позволяет отвечать на запросы URL-АДРЕС.
7 ответов:
на основе еще один ответ, вот функция setTextViewHTML (), которая анализирует ссылки из строки HTML и делает их кликабельными, а затем позволяет вам отвечать на URL.
protected void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span) { int start = strBuilder.getSpanStart(span); int end = strBuilder.getSpanEnd(span); int flags = strBuilder.getSpanFlags(span); ClickableSpan clickable = new ClickableSpan() { public void onClick(View view) { // Do something with span.getURL() to handle the link click... } }; strBuilder.setSpan(clickable, start, end, flags); strBuilder.removeSpan(span); } protected void setTextViewHTML(TextView text, String html) { CharSequence sequence = Html.fromHtml(html); SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence); URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class); for(URLSpan span : urls) { makeLinkClickable(strBuilder, span); } text.setText(strBuilder); text.setMovementMethod(LinkMovementMethod.getInstance()); }
Это можно просто решить с помощью Spannable String.То, что вы действительно хотите сделать (бизнес-требование), немного неясно для меня, поэтому следующий код не даст точного ответа на вашу ситуацию, но я уверен, что он даст вам некоторое представление, и вы сможете решить свою проблему на основе следующего кода.
Как и вы, я также получаю некоторые данные через HTTP-ответ, и я добавил некоторые дополнительные подчеркнутый текст в моем случае "больше" и этот подчеркнутый текст откроет веб-браузер на click event.Надеюсь, это поможет вам.
TextView decription = (TextView)convertView.findViewById(R.id.library_rss_expan_chaild_des_textView); String dec=d.get_description()+"<a href='"+d.get_link()+"'><u>more</u></a>"; CharSequence sequence = Html.fromHtml(dec); SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence); UnderlineSpan[] underlines = strBuilder.getSpans(0, 10, UnderlineSpan.class); for(UnderlineSpan span : underlines) { int start = strBuilder.getSpanStart(span); int end = strBuilder.getSpanEnd(span); int flags = strBuilder.getSpanFlags(span); ClickableSpan myActivityLauncher = new ClickableSpan() { public void onClick(View view) { Log.e(TAG, "on click"); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(d.get_link())); mContext.startActivity(intent); } }; strBuilder.setSpan(myActivityLauncher, start, end, flags); } decription.setText(strBuilder); decription.setLinksClickable(true); decription.setMovementMethod(LinkMovementMethod.getInstance());
вы сделали следующее:
text_view.setMovementMethod(LinkMovementMethod.getInstance()); text_view.setText( Html.fromHtml( str_links ) );
вы пробовали в обратном порядке, как показано ниже?
text_view.setText( Html.fromHtml( str_links ) ); text_view.setMovementMethod(LinkMovementMethod.getInstance());
и без:
text_view.setLinksClickable(true);
у меня была та же проблема, но много текста, смешанного с несколькими ссылками и электронными письмами. Я думаю, что использование "autoLink" - это более простой и чистый способ сделать это:
text_view.setText( Html.fromHtml( str_links ) ); text_view.setLinksClickable(true); text_view.setAutoLinkMask(Linkify.ALL); //to open links
Вы можете установить Linkify.EMAIL_ADDRESSES или Linkify.WEB_URLS, если есть только один из них вы хотите использовать или установить из XML-макета
android:linksClickable="true" android:autoLink="web|email"
доступны следующие параметры: нет, веб, электронная почта, телефон, карта, все
способ чище и лучше решение, используя родной библиотека Linkify.
пример:
Linkify.addLinks(mTextView, Linkify.ALL);
решение
я реализовал небольшой класс, с помощью которого вы можете обрабатывать длинные клики на самом 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 ""; } } }
использование
String str_links = "<a href='http://google.com'>Google</a><br /><a href='http://facebook.com'>Facebook</a>"; text_view.setText( Html.fromHtml( str_links ) ); text_view.setMovementMethod(new TextViewClickMovement(this, context));
ссылки
надеюсь, что это helops! Вы можете найти код здесь.
Im, используя только textView, и установить span для url и обрабатывать нажмите кнопку.
Я нашел очень элегантное решение здесь, без linkify - в соответствии с тем, что я знаю, какую часть строки Я хочу linkify
ручка textview ссылка нажмите в моем приложении для android
в Котлин:
fun linkify(view: TextView, url: String, context: Context) { val text = view.text val string = text.toString() val span = ClickSpan(object : ClickSpan.OnClickListener { override fun onClick() { // handle your click } }) val start = string.indexOf(url) val end = start + url.length if (start == -1) return if (text is Spannable) { text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) text.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.orange)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } else { val s = SpannableString.valueOf(text) s.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) s.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.orange)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) view.text = s } val m = view.movementMethod if (m == null || m !is LinkMovementMethod) { view.movementMethod = LinkMovementMethod.getInstance() } } class ClickSpan(private val mListener: OnClickListener) : ClickableSpan() { override fun onClick(widget: View) { mListener.onClick() } interface OnClickListener { fun onClick() } }
и использование: linkify (yourTextView, urlString, context)