क्लिक करने योग्य लिंक के साथ Android TextView: क्लिक कैसे कैप्चर करें?


117

मेरे पास एक TextView है जो मूल HTML का प्रतिपादन कर रहा है, जिसमें 2+ लिंक हैं। मुझे लिंक पर क्लिक कैप्चर करने और लिंक खोलने की आवश्यकता है - मेरे अपने आंतरिक WebView में (डिफ़ॉल्ट ब्राउज़र में नहीं)।

लिंक रेंडरिंग को संभालने के लिए सबसे आम तरीका इस तरह लगता है:

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 पर प्रतिक्रिया करने देता है।


1
आप स्पैनबल स्ट्रिंग का उपयोग क्यों नहीं करते हैं। ??
रेनजिथ

1
वास्तव में, HTML एक दूरस्थ सर्वर द्वारा प्रदान किया जाता है, मेरे आवेदन द्वारा उत्पन्न नहीं होता है।
ज़ैन क्लेप्स

आपका उदाहरण समाधान बहुत उपयोगी है; उस दृष्टिकोण का उपयोग करके मैं क्लिक को अच्छी तरह से कैप्चर करता हूं और मापदंडों के साथ एक और गतिविधि लॉन्च कर सकता हूं, जिसके आधार पर लिंक पर क्लिक किया गया था। (समझने के लिए महत्वपूर्ण बिंदु था "कुछ के साथ करो span.getURL()"।) आप इसे उत्तर के रूप में भी पोस्ट कर सकते हैं, क्योंकि यह वर्तमान में स्वीकृत उत्तर से बेहतर है!
जोनिक

जवाबों:


223

एक अन्य उत्तर के आधार पर , यहाँ एक फ़ंक्शन 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());       
}

5
बढ़िया काम किया। इस दृष्टिकोण (अन्य उत्तरों के विपरीत) के साथ, मैं 1 पर क्लिक क्लिक करने में कामयाब रहा और 2) मापदंडों के साथ, एक और गतिविधि लॉन्च की, जिसके आधार पर लिंक पर क्लिक किया गया था।
जोनिक

सही ... मेरा दिन बचाया
maverickosama92

अद्भुत है, लेकिन अगर आप इसे एक सूची दृश्य पर लागू करते हैं (मेरा मतलब है, प्रत्येक तत्व के आंतरिक
पाठ

@voghDev ListViews के साथ तब होता है जब एक View's focusableसच होता है। यह आमतौर पर Buttons / ImageButtons के साथ होता है । setFocusable(false)अपने पर कॉल करने का प्रयास करें TextView
सूफियान

text.setMovementMethod(LinkMovementMethod.getInstance());URLSpan का उपयोग न करें, तो सुनिश्चित करें
rajath

21

आपने निम्नानुसार किया है:

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

3
यह काम करता है लेकिन बिना किसी संकेत के कि उपयोगकर्ता ने लिंक पर क्लिक किया है, जब लिंक पर क्लिक किया जाता है तो मुझे एक क्यू / एनीमेशन / हाइलाइट की आवश्यकता होती है ... मुझे क्या करना चाहिए?
रोशनीबाग

@StarWars आप (StateList) [ developer.android.com/intl/pt-br/guide/topics/resources/… शुद्ध Android में उपयोग कर सकते हैं , लेकिन HTML के साथ मुझे नहीं पता।
ademar111190

20

यह केवल Spannable String का उपयोग करके हल किया जा सकता है। आप वास्तव में क्या करना चाहते हैं (व्यावसायिक आवश्यकता) मेरे लिए थोड़ा अस्पष्ट है, इसलिए निम्नलिखित कोड आपकी स्थिति का सटीक उत्तर नहीं देंगे, लेकिन मुझे यकीन है कि यह आपको कुछ विचार देगा आप निम्न कोड के आधार पर अपनी समस्या को हल करने में सक्षम होंगे।

जैसा कि आप करते हैं, मैं भी HTTP प्रतिक्रिया के माध्यम से कुछ डेटा प्राप्त कर रहा हूं और मैंने अपने मामले में "अतिरिक्त" में कुछ अतिरिक्त रेखांकित पाठ जोड़े हैं और यह रेखांकित पाठ क्लिक इवेंट पर वेब ब्राउज़र को खोल देगा। इससे आपको मदद मिलेगी।

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

महान! मैंने इसे अपने मामले के लिए संशोधित किया है। मैं कोड शामिल करने के लिए अपनी पोस्ट संपादित करूंगा।
ज़ैन क्लेप्स

वहाँ एक समान समाधान है कि xml के अंदर इस्तेमाल किया जा सकता है?
एंड्रॉइड डेवलपर

मुझे यह ओपी के संशोधित संस्करण (प्रश्न में) के साथ काम कर रहा है, इसके साथ नहीं। (इस संस्करण के साथ, क्लिकें सीधे "डायलॉग का उपयोग करके पूर्ण कार्रवाई करने के लिए" चली गईं।)
जोनिक

मैं इस तर्क का उपयोग करता था, हालांकि URLSpan के साथ अंडरलाइनपैन को बदलना था। SpannableStringBuilder से पुराने स्पैन को हटाने के लिए आवश्यक था।
रे

4
व्हाट्सएप वेरिएबल 'd' यहाँ है?
सलमान खान

15

मेरे पास एक ही समस्या है लेकिन बहुत सारे पाठ कुछ लिंक और ईमेल के साथ मिश्रित हैं। मुझे लगता है कि 'ऑटोलिंक' का उपयोग करना एक आसान और साफ-सुथरा तरीका है:

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

यदि आप उनमें से केवल एक का उपयोग करना चाहते हैं या XML लेआउट से सेट करना चाहते हैं तो आप Linkify.EMAIL_ADDRESSES या Linkify.WEB_URLS सेट कर सकते हैं।

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

उपलब्ध विकल्प हैं: कोई नहीं, वेब, ईमेल, फोन, मानचित्र, सभी


1
हाय अवरोधन करने के लिए किसी भी तरह से आशय एक लिंक के क्लिक के समय में निकाल दिया है
मनमोहन सोनी

1
इस उत्तर से 6 साल हो गए हैं .. बेशक यह नवीनतम एंड्रॉइड वर्जन में बदल गया होगा ^ ^ इसका मतलब यह नहीं है कि यह वापस काम नहीं करता है ^ ^
जॉर्डन

8

उपाय

मैंने एक छोटा वर्ग लागू किया है जिसकी मदद से आप 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));

लिंक

आशा है कि यह हेलो! आप यहां कोड पा सकते हैं ।


कृपया, लाइन से, फिर से अपना कोड जांचें text_view.setMovementMethod(new TextViewClickMovement(this, context));; एंड्रॉइड स्टूडियो शिकायत contextकर रहा है जिसे हल नहीं किया जा सकता है।
X09

यदि आप स्रोत कोड को बिटकॉइन से कॉपी करते हैं, तो आपको इस text_view.setMovementMethod (नया TextViewClickMovement (संदर्भ। यह)) जैसे संदर्भ और श्रोता के स्थान को बदलना चाहिए;
विक्टर अपोयन

यह पैरामीटर के लिए दो संदर्भ पार्सिंग होगा। काम नहीं किया सर। हालाँकि स्वीकृत उत्तर अब मेरे लिए काम कर रहा है
X09

आपके उत्तर के लिए धन्यवाद सर, इस तरह के लिए सबसे अच्छा है, धन्यवाद!
वुलोविक वकासिन

8

एक तरह से क्लीनर और बेहतर समाधान, देशी लिंक लाइब्रेरी का उपयोग कर ।

उदाहरण:

Linkify.addLinks(mTextView, Linkify.ALL);

1
क्या इस का उपयोग करके एक ऑनक्लिक घटना को ओवरराइड करना संभव है?
मिलिंद मेवाड़ा

7

मैं एक आसान विस्तार समारोह में की गई Kotlin URLSpan तत्वों के लिए एक नया कॉलबैक लगाने से एक TextView में पकड़ यूआरएल लिंक क्लिक करें।

strings.xml (पाठ में उदाहरण लिंक)

<string name="link_string">this is my link: <a href="https://www.google.com/">CLICK</a></string>

सुनिश्चित करें कि आपके द्वारा हैंडल किए गए टेक्स्ट को हैंडल करने से पहले TextView पर सेट कर दिया गया है

textView.text = getString(R.string.link_string)

यह विस्तार समारोह है:

/**
 * Searches for all URLSpans in current text replaces them with our own ClickableSpans
 * forwards clicks to provided function.
 */
fun TextView.handleUrlClicks(onClicked: ((String) -> Unit)? = null) {
    //create span builder and replaces current text with it
    text = SpannableStringBuilder.valueOf(text).apply {
        //search for all URL spans and replace all spans with our own clickable spans
        getSpans(0, length, URLSpan::class.java).forEach {
            //add new clickable span at the same position
            setSpan(
                object : ClickableSpan() {
                    override fun onClick(widget: View) {
                        onClicked?.invoke(it.url)
                    }
                },
                getSpanStart(it),
                getSpanEnd(it),
                Spanned.SPAN_INCLUSIVE_EXCLUSIVE
            )
            //remove old URLSpan
            removeSpan(it)
        }
    }
    //make sure movement method is set
    movementMethod = LinkMovementMethod.getInstance()
}

मैं इसे कैसे कहता हूं:

textView.handleUrlClicks { url ->
    Timber.d("click on found span: $url")
}

बहुत बढ़िया! _______
वैलेंटाइन यूरीव

5

यदि आप कोटलिन का उपयोग कर रहे हैं, तो मैंने इस मामले के लिए एक सरल एक्सटेंशन लिखा है :

/**
 * Enables click support for a TextView from a [fullText] String, which one containing one or multiple URLs.
 * The [callback] will be called when a click is triggered.
 */
fun TextView.setTextWithLinkSupport(
    fullText: String,
    callback: (String) -> Unit
) {
    val spannable = SpannableString(fullText)
    val matcher = Patterns.WEB_URL.matcher(spannable)
    while (matcher.find()) {
        val url = spannable.toString().substring(matcher.start(), matcher.end())
        val urlSpan = object : URLSpan(fullText) {
            override fun onClick(widget: View) {
                callback(url)
            }
        }
        spannable.setSpan(urlSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    }
    text = spannable
    movementMethod = LinkMovementMethod.getInstance() // Make link clickable
}

उपयोग:

yourTextView.setTextWithLinkSupport("click on me: https://www.google.fr") {
   Log.e("URL is $it")
}

1

एक वैकल्पिक, एक तरह से सरल दृष्टिकोण (अपने जैसे आलसी डेवलपर्स के लिए;)

abstract class LinkAwareActivity : AppCompatActivity() {

    override fun startActivity(intent: Intent?) {
        if(Intent.ACTION_VIEW.equals(intent?.action) && onViewLink(intent?.data.toString(), intent)){
            return
        }

        super.startActivity(intent)
    }

    // return true to consume the link (meaning to NOT call super.startActivity(intent))
    abstract fun onViewLink(url: String?, intent: Intent?): Boolean 
}

यदि आवश्यक हो, तो आप इरादे की योजना / मापांक के लिए भी जांच कर सकते हैं


0

Im केवल textView का उपयोग कर, और url और हैंडल क्लिक के लिए स्पैन सेट करें।

मुझे यहां बहुत ही सुंदर समाधान मिला, बिना किसी लिंक के - इसके अनुसार मुझे पता है कि मैं स्ट्रिंग का कौन सा भाग लिंक करना चाहता हूं

मेरे एंड्रॉइड ऐप में टेक्स्टव्यू लिंक पर क्लिक करें

कोटलिन में:

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

और उपयोग: लिंक (yourTextView, urlString, संदर्भ)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.