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()
を行なった場合は条件に一致していたとしてもリンクが無効になります。
この辺りが要件に合わない場合は内部の判定の仕方や代入の方法を調整してみてください
あと正規表現とか駆使して判定したい方はMatcherあたりをごにょってもらえるといいかもしれないです。
参考
- 「TextView の一部のリンク化+クリックイベントの指定、をサクッと作る」
- TextViewのリンク化いろいろ