Сделайте строку кликабельной, подчеркнутой в TextView

Я хочу сделать строку "эта ссылка" подчеркнутой и кликабельной, но я не знаю, как этого добиться.

XML-файл:

<string name="submitText">Before you submit, please check out &lt;u>this link&lt;/u></string>

В моем фрагменте:

tvSubmit.setText(Html.fromHtml(getString(R.string.submitText)));

Я не хочу, чтобы вся строка была кликабельной, только подчеркнутый раздел. Я не могу использовать горизонтальный LinearLayout с 2 ячейками, потому что на меньших устройствах строка не будет иметь продолжения, она будет разбита на 2 ячейки.

Что я пробовал:

tvSubmit.setMovementMethod(LinkMovementMethod.getInstance());
Spannable sp = (Spannable) tvSubmit.getText();
ClickableSpan click = new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        showLink();
    }
};
sp.setSpan(click, 0, sp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Приведенный выше код подчеркивает всю строку, а также цвет текста меняется на голубой.

8 ответов

Решение

Проблема в том, что вы устанавливаете диапазон для всей строки (sp.setSpan(click, 0, sp.length()). Чтобы исправить, вы должны установить кликабельный диапазон только на this link, Я сделал то же самое таким образом:

<string name="submitText">Before you submit, please check out %1$s</string>
<string name="this_link">this link</string>

в вашей деятельности

String thisLink = getString(R.string.thisLink);
String yourString = getString(R.string.submitText, thisLink);
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(yourString);
spannableStringBuilder.setSpan(click,
                startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

где startIndex и endIndex являются индексом thisLink в yourString, Я разделил две строки, потому что было легче искать индексы, особенно если вам приходится иметь дело с переводами. Для расчета startIndex вы можете использовать yourString.indexOf(thisLink)и endIndex это startIndex + the length of thisLink, Я оставлю вам обычные чеки (отрицательные индексы и все остальное, что может вызвать IndexOutBoundException)

Вы можете определить в вашем strings.xml

<string name="submitText">Before you submit, please check out <a href="actual url">this link</a>
 <TextView  
       android:id="@+id/tvSubmit"  
       android:layout_width="wrap_content"  
       android:layout_height="wrap_content"  
       android:layout_alignParentTop="true"  
       android:autoLink=@String/submitText //link the content of web  
       android:textColorLink="#576586" //change the color of the link  
       android:textColor="#555555" />

в деятельности // образец

String webLinkText = <a href="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png"><img src="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png" alt="Screenshot-Mozilla Firefox (Private Browsing)" width="293" height="254" class="alignnone size-full wp-image-291" /></a>  
 tvSubmit = (TextView) findViewById(R.id.tvSubmit);  
 tvSubmit.setText(Html.fromHtml(webLinkText))); 

проверьте здесь для более подробного ответа

В моем случае мне нужно было иметь локализованную строку, часть которой была бы интерактивной. Что я сделал, так это определил строку в своих ресурсах (по одной для каждой локали) с подчеркнутой интерактивной частью, например:

      <string name="my_message">Blah blah blah <u>call us</u> blah blah.</string>

Затем я создал расширение, чтобы найти подчеркнутый текст и добавить к нему интерактивный диапазон, например:

      fun CharSequence.makeUnderlineClickable(listener: (index: Int) -> Unit): SpannableString {
    val spannedString = SpannableString(this)
    spannedString.getSpans(0, length, UnderlineSpan::class.java)?.forEachIndexed { index, underlineSpan ->
        val clickableSpan = object : ClickableSpan() {
            override fun onClick(widget: View) {
                listener.invoke(index)
            }

            override fun updateDrawState(ds: TextPaint) {
                ds.isUnderlineText = true
            }
        }
        spannedString.setSpan(
            clickableSpan,
            spannedString.getSpanStart(underlineSpan),
            spannedString.getSpanEnd(underlineSpan),
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
    return spannedString 
}

Вы можете использовать это так:

          textView.text = resources.getText(R.string.my_message).makeUnderlineClickable { index ->
        //handle click here
    }
    textView.movementMethod = LinkMovementMethod.getInstance()
    textView.highlightColor = Color.TRANSPARENT

Вы можете попробовать функцию расширения Kotlin, чтобы создавать текстовые, а также кликабельные с подчеркиванием.

      fun TextView.colorSpannableStringWithUnderLineOne(
    prefixString: String,
    postfixString: String,
    callback: (Int) -> Unit
) {
    val spanTxt = SpannableStringBuilder()
    spanTxt.append("$prefixString ")
    spanTxt.append(" $postfixString")
    spanTxt.setSpan(object : ClickableSpan() {
        override fun onClick(widget: View) {
            callback(0)
            widget.invalidate()
        }

        override fun updateDrawState(ds: TextPaint) {
            ds.color = ContextCompat.getColor(context, R.color.highlight)
            ds.isUnderlineText = true
        }
    }, prefixString.length, spanTxt.length, 0)
    this.movementMethod = LinkMovementMethod.getInstance()
    this.setText(spanTxt, TextView.BufferType.SPANNABLE)
}

`

Как это использовать:

      textViewLink.colorSpannableStringWithUnderLineOne(
                "Normal string",
                "Clickable string",
                callback = {
                    JLog.e("==>","Clicked")
                })
  textViewLink.invalidate()

Только что сделал эту функцию, чтобы помочь мне добавить интерактивный текст в текстовое представление:

public static SpannableStringBuilder getSpannablableClickFor(CharSequence initialText, ClickableSpan click, String textToAppend) {
    String description = String.format("%s %s", initialText, textToAppend);
    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(description);
    int start = description.indexOf(textToAppend);
    int end = start + textToAppend.length();
    spannableStringBuilder.setSpan(click,
            start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    spannableStringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    spannableStringBuilder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    final ForegroundColorSpan fcs = new ForegroundColorSpan(Color.WHITE);
    spannableStringBuilder.setSpan(fcs, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    return spannableStringBuilder;
}
sp.setSpan(click, 0, sp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

изменить значение 0: с позиции начала ссылки в тексте, пример изменить значение с: 10, если вы установите 0, это будет гиперссылка на весь текст.

Вы можете попробовать с приведенным ниже кодом..

String str = "Before you submit, please check out this link";
tvSubmit.setMovementMethod(LinkMovementMethod.getInstance());
tvSubmit.setText(setClickablePart(str), BufferType.SPANNABLE);

Для обработки события Click в Textview с помощью метода ниже

 private SpannableStringBuilder setClickablePart(String str)
    {
        SpannableStringBuilder m_spannableStringBuilder = new SpannableStringBuilder(str);

        int m_index = str.indexOf("this");

        final String clickString = str.substring(m_index, str.length());
        m_spannableStringBuilder.setSpan(new ClickableSpan()
        {
            @Override
            public void onClick(View widget)
            {
                Toast.makeText(MainActivity.this, clickString, Toast.LENGTH_SHORT).show();
            }
        }, m_index, str.length(), 0);

        return m_spannableStringBuilder;
    }
Другие вопросы по тегам