7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TextView内の特定の文字列に対して手軽にタップイベントを追加する

Last updated at Posted at 2020-02-13

Androidアプリ内のTextViewに対してWebのハイパーリンクのようなタップイベントをつけたい事がたまによくあるので、通常のonClickイベントと同じ使い勝手でなるべく簡単に扱えるようKotlinの拡張関数を書いてみました

使用感イメージ

下記のスクリーンショットのように使うイメージです。
よくある利用シーンとしては「利用規約」や「プライバシーポリシー」の文言にタップイベントをつけたりするときとかですかね

利用側のコードもここまで簡潔に記述するのを目的にしてみます

MainActivity.kt
hyperLinkTextView.text = "Hello World!"
hyperLinkTextView.addHyperLink("Hello") {
    Toast.makeText(this, "'Hello' clicked!", Toast.LENGTH_SHORT).show()
}

ソースコード

実際のタップイベントや文字色の変更、下線を引いたりするのはSpannableStringを使用しますが、ベタ書きすると煩雑なコードになりやすいので今回の利用用途に特化した拡張関数を書いてみます。

HyperLinkExtension.kt
fun TextView.addHyperLink(linkText: String, callback: ((view: View) -> Unit)) {
    val spannableMessage = SpannableString(text)
    val pattern = Pattern.compile(linkText)
    val matcher = pattern.matcher(text)
    while (matcher.find()) {
        spannableMessage.setSpan(object : ClickableSpan() {
            override fun onClick(textView: View) {
                callback.invoke(textView)
            }
        }, matcher.start(), matcher.end(), Spanned.SPAN_INCLUSIVE_INCLUSIVE)
    }
    text = spannableMessage
    movementMethod = LinkMovementMethod.getInstance()
}

また、追加でこんな形の引数をとる拡張関数も同時に生やしておくと便利かもしれません

HyperLinkExtension.kt
fun TextView.addHyperLink(@StringRes vararg linkTextRes: Int, callback: ((view: View) -> Unit)) {
    linkTextRes.map { addHyperLink(it, callback) }
}

fun TextView.addHyperLink(@StringRes linkTextRes: Int, callback: ((view: View) -> Unit)) {
    addHyperLink(context.getString(linkTextRes), callback)
}

fun TextView.addHyperLink(vararg linkText: String, callback: ((view: View) -> Unit)) {
    linkText.map { addHyperLink(it, callback) }
}

制限

今回書いたコードでは利用側の簡潔さと引き換えに以下の制限が存在します。

  • 判定に含まれる文字列が複数ある場合はすべてタップイベントの対象となります。
  • 同じTextViewに対してaddHyperLink()を複数使用しタップイベントを適用する文字列が重なった場合は、先にaddHyperLink()したタップイベントが優先的に処理されます。
  • 内部的にsetText()を行なっているため、改めて外側からsetText()を行なった場合は条件に一致していたとしてもリンクが無効になります。

この辺りが要件に合わない場合は内部の判定の仕方や代入の方法を調整してみてください :bow:
あと正規表現とか駆使して判定したい方はMatcherあたりをごにょってもらえるといいかもしれないです。

参考

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?