我有文本“Android是一个软件堆栈”。在这个文本中,我想设置“堆栈”文本为可点击。所以,如果你点击它,它将重定向到一个新的活动(不在浏览器中)。

我试过了,但没有找到解决办法。


当前回答

使用URLSpan类获取url

val spans: Array<URLSpan> = result.getSpans(0, result.length, URLSpan::class.java)

方法

fun TextView.createClickable(string: String, action:(String)->Unit ) {
      text = HtmlCompat.fromHtml(string, HtmlCompat.FROM_HTML_MODE_LEGACY)
      val result = SpannableString(text)
      val spans = result.getSpans(0, result.length, URLSpan::class.java)
      for (span in spans) {
          val link:Pair<String, View.OnClickListener> = Pair(span.url, View.OnClickListener {
              action(span.url)
          })
          val start = result.getSpanStart(span)
          val end = result.getSpanEnd(span)
          val flags = result.getSpanFlags(span)
          result.removeSpan(span)
          val clickableSpan: ClickableSpan = object : ClickableSpan() {
              override fun onClick(textView: View) {
                  textView.invalidate()
                  link.second.onClick(textView)
              }
              override fun updateDrawState(textPaint: TextPaint) {
                  super.updateDrawState(textPaint)
                  textPaint.isUnderlineText = false
              }
          }
          result.setSpan(clickableSpan, start, end, flags)
          this.movementMethod = LinkMovementMethod.getInstance()
          this.setText(result, TextView.BufferType.SPANNABLE)
      }
  }

Use

示例文本:Android是一个软件堆栈,它非常棒

包装你的可点击的文本内锚标签

比如:Android是一个软件<a href='https://example.com/stack'> Stack </a>和它' <a href='https://example.com/awesome'> Awesome </a>。

 val str = "Android is a Software <a href='https://example.com/stack'> Stack </a> and it' <a href='https://example.com/awesome'> Awesome </a>."

textView.createClickable(str) {
    when(it) {
        "https://example.com/stack"->{
  startActivity(Intent(this,StackActivity::class.java))
                  }
        "https://example.com/awesom"->{
            startActivity(Intent(this,AwesomeActivity::class.java))
        }
    }
}

其他回答

Kotlin上复杂但通用的解决方案

  /*
    * Receive Pair of Text and Action and set it clickable and appearing as link
    * */
fun TextView.setClickableText(vararg textToSpanAndClickAction: Pair<String, (String) -> Unit>) {
    val builder = SpannableStringBuilder(text.toString())

    textToSpanAndClickAction.forEach { argPair ->
    val clickableSpan = object : ClickableSpan() {
        override fun onClick(widget: View) {
            argPair.second.invoke(argPair.first)
        }
    }

    this.text.toString().let { fullText ->
        val indexOfFirst = fullText.indexOf(argPair.first)
        val indexOfLast = indexOfFirst + argPair.first.length
        if (indexOfFirst < 0){
            //No match found
            return
        }else{
            builder.setSpan(
                clickableSpan,
                indexOfFirst,
                indexOfLast,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
    }
}

this.text = builder
    this.movementMethod = LinkMovementMethod.getInstance()
}

Kotlin Spannable

大胆的,

mySpannable.setSpan(new StyleSpan(Typeface.BOLD),termStart,termStop,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

使用URLSpan类获取url

val spans: Array<URLSpan> = result.getSpans(0, result.length, URLSpan::class.java)

方法

fun TextView.createClickable(string: String, action:(String)->Unit ) {
      text = HtmlCompat.fromHtml(string, HtmlCompat.FROM_HTML_MODE_LEGACY)
      val result = SpannableString(text)
      val spans = result.getSpans(0, result.length, URLSpan::class.java)
      for (span in spans) {
          val link:Pair<String, View.OnClickListener> = Pair(span.url, View.OnClickListener {
              action(span.url)
          })
          val start = result.getSpanStart(span)
          val end = result.getSpanEnd(span)
          val flags = result.getSpanFlags(span)
          result.removeSpan(span)
          val clickableSpan: ClickableSpan = object : ClickableSpan() {
              override fun onClick(textView: View) {
                  textView.invalidate()
                  link.second.onClick(textView)
              }
              override fun updateDrawState(textPaint: TextPaint) {
                  super.updateDrawState(textPaint)
                  textPaint.isUnderlineText = false
              }
          }
          result.setSpan(clickableSpan, start, end, flags)
          this.movementMethod = LinkMovementMethod.getInstance()
          this.setText(result, TextView.BufferType.SPANNABLE)
      }
  }

Use

示例文本:Android是一个软件堆栈,它非常棒

包装你的可点击的文本内锚标签

比如:Android是一个软件<a href='https://example.com/stack'> Stack </a>和它' <a href='https://example.com/awesome'> Awesome </a>。

 val str = "Android is a Software <a href='https://example.com/stack'> Stack </a> and it' <a href='https://example.com/awesome'> Awesome </a>."

textView.createClickable(str) {
    when(it) {
        "https://example.com/stack"->{
  startActivity(Intent(this,StackActivity::class.java))
                  }
        "https://example.com/awesom"->{
            startActivity(Intent(this,AwesomeActivity::class.java))
        }
    }
}

Phan Van Linh回答的版本。

请注意它有一些小的改动。

fun makeLinks(textView: TextView, links: Array<String>, clickableSpans: Array<ClickableSpan>) {
    val spannableString = SpannableString(textView.text)

    for (i in links.indices) {
        val clickableSpan = clickableSpans[i]
        val link = links[i]

        val startIndexOfLink = textView.text.indexOf(link)

        spannableString.setSpan(clickableSpan, startIndexOfLink, startIndexOfLink + link.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    }

    textView.movementMethod = LinkMovementMethod.getInstance()
    textView.setText(spannableString, TextView.BufferType.SPANNABLE)
}

fun setupClickableTextView() {
    val termsOfServicesClick = object : ClickableSpan() {
        override fun onClick(p0: View?) {
            Toast.makeText(applicationContext, "ToS clicked", Toast.LENGTH_SHORT).show()
        }
    }

    val privacyPolicyClick = object : ClickableSpan() {
        override fun onClick(p0: View?) {
            Toast.makeText(applicationContext, "PP clicked", Toast.LENGTH_SHORT).show()
        }
    }

    makeLinks(termsTextView, arrayOf("terms", "privacy policy"), arrayOf(termsOfServicesClick, privacyPolicyClick))
}

这里有一个Kotlin解决方案,与本地化工作得更好:

data class LinkedText(@StringRes val textRes: Int, val clickListener: View.OnClickListener? = null)

fun TextView.setPartiallyLinkedText(vararg texts: LinkedText) {
    this.text = texts.joinToString(" ") { context.getString(it.textRes) }
    val spannableString = SpannableString(this.text)
    var startIndexOfLink = -1
    texts.forEach { text ->
        val string = context.getString(text.textRes)
        if (text.clickListener != null) {
            val clickableSpan = object : ClickableSpan() {
                override fun updateDrawState(textPaint: TextPaint) {
                    textPaint.color = textPaint.linkColor
                    textPaint.isUnderlineText = true
                }
                override fun onClick(view: View) {
                    Selection.setSelection((view as TextView).text as Spannable, 0)
                    view.invalidate()
                    text.clickListener.onClick(view)
                }
            }
            startIndexOfLink = this.text.toString().indexOf(string, startIndexOfLink + 1)
            spannableString.setSpan(
                clickableSpan, startIndexOfLink, startIndexOfLink + string.length,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
            )
        }
    }
    this.movementMethod = LinkMovementMethod.getInstance()
    this.setText(spannableString, TextView.BufferType.SPANNABLE)
}

像这样使用它:

textView.setPartiallyLinkedText(
    LinkedText(R.string.not_linked_text),
    LinkedText(R.string.linked_text) {
        Toast.makeText(context, "You clicked", Toast.LENGTH_LONG).show()
    },
)