Tıklanabilir Bağlantılarla Android TextView: Tıklamalar nasıl yakalanır?


117

2+ bağlantı içeren, temel HTML işleyen bir TextView var. Bağlantılara yapılan tıklamaları yakalamam ve bağlantıları açmam gerekiyor - kendi dahili Web Görünümümde (varsayılan tarayıcıda değil).

Bağlantı oluşturmanın üstesinden gelmenin en yaygın yöntemi şuna benzer:

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

Ancak bu, bağlantıların varsayılan dahili web tarayıcısında açılmasına neden olur ("İşlemi Şunu Kullanarak Tamamla ..." iletişim kutusunu gösterir).

Bağlantı tıklandığında doğru şekilde tetiklenen bir onClickListener uygulamayı denedim, ancak HANGİ bağlantının tıklandığını nasıl belirleyeceğimi bilmiyorum ...

text_view.setOnClickListener(new OnClickListener(){

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

});

Alternatif olarak, özel bir LinkMovementMethod sınıfı oluşturmayı ve onTouchEvent'i uygulamayı denedim ...

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

Fikirler?


Örnek çözüm

Bağlantıları bir HTML dizesinden ayrıştıran ve tıklanabilir hale getiren ve ardından URL'ye yanıt vermenizi sağlayan bir çözüm buldum .


1
Neden Spannable String kullanmıyorsun?
renjith

1
Gerçekte HTML, uygulamam tarafından oluşturulmayan uzak bir sunucu tarafından sağlanır.
Zane Claes

Örnek çözümünüz çok faydalıdır; Bu yaklaşımı kullanarak tıklamaları güzelce yakalarım ve hangi bağlantının tıklandığına bağlı olarak parametrelerle başka bir Etkinlik başlatabilirim. (Anlaşılması gereken anahtar nokta "Bir şeyler yapın span.getURL()" idi.) Şu anda kabul edilen cevaptan daha iyi olduğu için, bir cevap olarak bile gönderebilirsiniz!
Jonik

Yanıtlar:


223

Dayanarak başka bir yanıt , burada bir HTML dizesi dışına bağlantıları ayrıştırır ve onları tıklanabilir ve sonra URL'ye yanıt sağlar kılan bir fonksiyon setTextViewHTML () var.

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
Harika çalıştı. Bu yaklaşımla (diğer yanıtların aksine), 1) tıklamaları yakalamayı ve 2) hangi bağlantının tıklandığına bağlı olarak parametrelerle başka bir Etkinlik başlatmayı başardım.
Jonik

mükemmel ... günümü kurtardı
maverickosama92

Harika, ancak bunu bir ListView'a uygularsanız (yani, her öğenin iç TextView'ına) listeyi tıklanamaz hale getirir, ancak bağlantılar hala tıklanabilir
voghDev

@voghDev ListView, a View's focusabletrue olarak ayarlandığında s ile olur . Bu genellikle Buttons / ImageButtons ile olur . Aramayı deneyin setFocusable(false)Aşağıdaki yerlerde de TextView.
Sufian

text.setMovementMethod(LinkMovementMethod.getInstance());URLSpan kullanmıyorsanız kullandığınızdan emin olun
rajath

21

Aşağıdaki gibi yaptınız:

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

aşağıda gösterildiği gibi ters sırayla denediniz mi?

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

Ve olmadan:

text_view.setLinksClickable(true);

3
Çalışıyor ancak kullanıcının bağlantıya tıkladığına dair herhangi bir sinyal olmadan, bağlantı tıklandığında bir işarete / animasyona / vurgulamaya ihtiyacım var ... ne yapmalıyım?
ışın kılıcı

@StarWars (StateList) [ developer.android.com/intl/pt-br/guide/topics/resources/… 'i saf android'de kullanabilirsiniz, ancak HTML ile bilmiyorum.
ademar111190

20

Bu, Spannable String kullanılarak basitçe çözülebilir. Gerçekten yapmak istediğiniz şey (İş Gereksinimi) benim için biraz belirsizdir, bu nedenle aşağıdaki kod durumunuza kesin bir cevap vermeyecektir, ancak bunun size bir fikir vereceğinden eminim ve Sorununuzu aşağıdaki koda göre çözebileceksiniz.

Yaptığınız gibi, HTTP yanıtı yoluyla da bazı veriler alıyorum ve benim durumumda "daha fazla" bazı ilave altı çizili metin ekledim ve bu altı çizili metin, tıklama olayında web tarayıcısını açacak. Umarım bu size yardımcı olacaktır.

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

Harika! Bunu durumum için değiştirdim. Gönderimi kodu içerecek şekilde düzenleyeceğim.
Zane Claes

xml içinde kullanılabilecek benzer bir çözüm var mı?
android geliştiricisi

Bununla değil, OP'nin değiştirilmiş versiyonuyla (söz konusu) çalıştırdım. (Bu sürümle, tıklamalar doğrudan "işlemi kullanarak" iletişim kutusunu tamamlamaya gitti.)
Jonik

Bu mantığı kullandım, ancak UnderlineSpan'i URLSpan ile değiştirmek zorunda kaldım.Ayrıca, eski aralıkları SpannableStringBuilder'dan kaldırmam gerekiyordu.
Ray

4
Burada değişken 'd' nedir?
Salman Khan

15

Aynı sorunu yaşadım, ancak çok sayıda metin birkaç bağlantı ve e-posta ile karıştırıldı. 'AutoLink'i kullanmanın bunu yapmanın daha kolay ve temiz bir yolu olduğunu düşünüyorum:

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

XML düzeninden kullanmak veya ayarlamak istediğiniz yalnızca bir tane varsa Linkify.EMAIL_ADDRESSES veya Linkify.WEB_URLS'yi ayarlayabilirsiniz.

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

Kullanılabilir seçenekler şunlardır: yok, web, e-posta, telefon, harita, tümü


1
Merhaba, bir bağlantıya tıklandığında ateşlenen amacı engellemenin herhangi bir yolu var mı
Manmohan Soni

1
Bu cevabın üzerinden 6 yıl geçti .. Elbette en son Android sürümünde değişmiş olabilir ^^ O zamanlar çalışmadığı anlamına gelmez ^^
Jordi

8

Çözüm

TextView üzerindeki uzun tıklamaları ve TextView'daki bağlantılara Taps'ı kaldırabileceğiniz küçük bir sınıf uyguladım.

Yerleşim

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

kullanım

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

Bağlantılar

Umarım bu helops! Kodu burada bulabilirsiniz .


Lütfen kodunuzu tekrar kontrol edin text_view.setMovementMethod(new TextViewClickMovement(this, context));. Android Studio contextçözülemediğinden şikayet ediyor .
X09

Bitbucket'tan kaynak kodunu kopyalarsanız, text_view.setMovementMethod (new TextViewClickMovement (bağlam. This)) gibi bağlam ve dinleyicinin yerini değiştirmelisiniz;
Victor Apoyan

Bu, parametreler için iki bağlamı ayrıştırıyor olacak. İşe yaramadı efendim. Kabul edilen cevap şimdi benim için çalışıyor
X09

Cevabınız için teşekkür ederim efendim, bu tür için en iyisi, teşekkürler!
Vulovic Vukasin


7

Kotlin'de kolay bir genişletme işlevi yaptım URLSpan elemanlarına yeni geri arama uygulayarak TextView içinde yakalamak url bağlantı tıklama için.

strings.xml (metinde örnek bağlantı)

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

"HandleUrlClicks" i çağırmadan önce yayılmış metninizin TextView olarak ayarlandığından emin olun

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

Bu, uzantı işlevidir:

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

Ben buna böyle diyorum:

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

Harika! _______
Valentin Yuryev

5

Kotlin kullanıyorsanız, bu durum için basit bir uzantı yazdım :

/**
 * 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
}

Kullanımı:

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

1

Alternatif, çok daha basit bir yaklaşım (benim gibi tembel geliştiriciler için;)

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 
}

Gerekirse, niyetin şema / mime tipini de kontrol edebilirsiniz


0

Yalnızca textView kullanıyorum ve url için aralık belirle ve tıklamayı ele al.

Bağlantısız olarak burada çok zarif bir çözüm buldum - buna göre dizenin hangi bölümünü bağlamak istediğimi biliyorum

android uygulamamdaki metin görünümü bağlantısını tıklayın

kotlin'de:

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

ve kullanım: linkify (yourTextView, urlString, bağlam)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.