web-dev-qa-db-de.com

Android TextView mit anklickbaren Links: Wie werden Klicks erfasst?

Ich habe eine Textansicht, die einfaches HTML darstellt und 2 + Links enthält. Ich muss Klicks auf die Links erfassen und die Links öffnen - in meinem eigenen internen WebView (nicht im Standardbrowser).

Die gebräuchlichste Methode zum Verarbeiten von Link-Rendering scheint folgendermaßen zu sein:

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

Dadurch werden die Links jedoch im standardmäßigen internen Webbrowser geöffnet (das Dialogfeld "Aktion abschließen ..." wird angezeigt).

Ich habe versucht, einen onClickListener zu implementieren, der beim Klicken auf den Link ordnungsgemäß ausgelöst wird, aber ich weiß nicht, auf welchen Link geklickt wurde.

text_view.setOnClickListener(new OnClickListener(){

    public void onClick(View v) {
        // what now...?
    }

});

Alternativ habe ich versucht, eine benutzerdefinierte LinkMovementMethod-Klasse zu erstellen und onTouchEvent zu implementieren ...

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

Ideen?


Beispiellösung

Ich habe a solution gefunden, die Links aus einer HTML-Zeichenfolge heraus analysiert, anklickbar macht und Ihnen dann die Antwort auf die URL ermöglicht.

100
Zane Claes

Basierend auf einer anderen Antwort , folgt hier eine Funktion setTextViewHTML (), die die Links aus einem HTML-String heraus analysiert, anklickbar macht und dann auf die URL reagieren lässt.

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());       
}
183
Zane Claes

Dies kann einfach mit Spannable String gelöst werden. Was Sie wirklich tun möchten (Business Requirement), ist für mich ein wenig unklar, so dass der folgende Code keine exakte Antwort auf Ihre Situation gibt, aber ich bin mir ziemlich sicher, dass er Ihnen eine Idee und eine Idee geben wird Sie können Ihr Problem anhand des folgenden Codes lösen.

Während Sie dies tun, erhalte ich auch einige Daten per HTTP-Antwort, und ich habe in meinem Fall einen zusätzlichen unterstrichenen Text hinzugefügt "more". Dieser unterstrichene Text öffnet den Webbrowser auf click event.Hope das wird dir helfen.

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());
18

Du hast wie folgt gemacht:

text_view.setMovementMethod(LinkMovementMethod.getInstance());
text_view.setText( Html.fromHtml( str_links ) );

haben Sie es in umgekehrter Reihenfolge versucht (siehe unten)?

text_view.setText( Html.fromHtml( str_links ) );
text_view.setMovementMethod(LinkMovementMethod.getInstance());

und ohne:

text_view.setLinksClickable(true);
13
ademar111190

Ich hatte das gleiche Problem, aber eine Menge Text vermischte sich mit wenigen Links und E-Mails ... Ich denke, die Verwendung von 'autoLink' ist eine einfachere und sauberere Methode:

  text_view.setText( Html.fromHtml( str_links ) );
  text_view.setLinksClickable(true);
  text_view.setAutoLinkMask(Linkify.ALL); //to open links

Sie können Linkify.EMAIL_ADDRESSES oder Linkify.WEB_URLS festlegen, wenn es nur einen von ihnen gibt

  Android:linksClickable="true"
  Android:autoLink="web|email"

Die verfügbaren Optionen sind: Keine, Web, E-Mail, Telefon, Karte, alle

9
Jordi

Eine saubere und bessere Lösung, wenn Sie die native Linkify-Bibliothek verwenden.

Beispiel:

Linkify.addLinks(mTextView, Linkify.ALL);
7
Fidel Montesino

Lösung

Ich habe eine kleine Klasse implementiert, mit deren Hilfe Sie lange Klicks auf TextView selbst und Taps auf die Links in der TextView ausführen können.

Layout

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

Verwendungszweck

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

Links

Hoffe das hilft! Sie finden den Code hier .

7
Victor Apoyan

Ich verwende nur textView und setze den Abstand für die URL und das Klicken des Handles.

Ich habe hier eine sehr elegante Lösung gefunden, ohne linkify - demnach weiß ich, welchen Teil der Zeichenkette ich verknüpfen möchte

Textview-Link in meiner Android-App bearbeiten

in kotlin:

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()
    }
}

und Verwendung: linkify (yourTextView, urlString, context)

0
Peter