在Android TextView上自訂超連結

超連結,像是這串字。我想在所有類型的app上很常見。不過要怎麼在Android上實做呢?我可以自定義點擊事件嗎?

首先,Android的TextView本身就支援超連結文本,來看看我們怎麼玩弄SpannableStringBuilder吧。

val builder = SpannableStringBuilder("Fancy String")
builder.setSpan(
    object : ClickableSpan() {
        override fun onClick(widget: View) {
            // Handle clicked behavior
        }
    },
    0, // Started Index
    builder.length, // Ended Index
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 
)
textView.text = builder
textView.setMovementMethod(LinkMovementMethod.getInstance()); // 重要

從上述程式碼我們就會得到一串可以點擊的字串了!不過顏色怎麼辦?他似乎是跟著app的Style。我們能夠自己去自訂顏色嗎?

val builder = SpannableStringBuilder("Fancy String")
builder.setSpan(
    object : ClickableSpan() {
        override fun onClick(widget: View) {
            // Handle clicked behavior
        }
    },
    0, // Started Index
    builder.length, // Ended Index
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
builder.setSpan(
    new ForegroundColorSpan(Color.RED), // 紅色字串
    0,
    builder.length(),
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
textView.text = builder
textView.setMovementMethod(LinkMovementMethod.getInstance()); // 重要

使用SetSpan設定吧,與前面的ClickableSpan類似,增加一個顏色的Span在同樣的Index區域。

不過,話說回來,這是不是有點複雜?我們除了使用相對應的Span類別實做,還要去計算這串可以點擊的字串的Index。 這樣的作法在單一TextView裡面有較長且較多的可點擊字串的需求下維護會是一個很痛苦的開始。

不過呢,其實SpannableStringBuilder也是一個實做了CharSequence的類別,而我們在這個builder上的所有操作其實是可以輸入CharSequence的。 那麼這樣可以簡化我們的作法嗎?

val builder = SpannableStringBuilder("Fancy String")
builder.setSpan(
    object : ClickableSpan() {
        override fun onClick(widget: View) {
            // Handle clicked behavior
        }
    },
    0, // Started Index
    builder.length, // Ended Index
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

val builder2 = SpannableStringBuilder(builder) // 直接傳入Builder參數
builder2.append("abc"); // 接上其他字串,不使用builder會使前面的實做無效

textView.text = builder
textView.setMovementMethod(LinkMovementMethod.getInstance()); // 重要

我們直接new一個新的SpannableStringBuilder來繼續操作我們想要的操做,在建構這個類別時傳入上一個SpannableStringBuilder,這樣Index參數我們都只需要輸入整串長度就行了,我想在未來的維護上會是加分的。

空清單提示

不過,既然參數固定了,有不需要輸入的作法嗎?可以參考使用我的一個library Aura,提供了這樣的類別減少閱讀雜訊。

textView.text = AppendedStr( // Append two spannable Charsequence
    ColoredStr( // Setup color
        ClickableStr( // Clickable string
            ConstSource("Fancy String"),
            Runnable { 
                // Handle clicked behavior
            }
        ), RED
    ), "abc"
).value()
textView.movementMethod = LinkMovementMethod.getInstance()