TextView識別文本中的超鏈接并能點擊跳轉(zhuǎn)

需求:

如題所訴吁朦。需要識別出文本中的鏈接搁廓,并顯示成超鏈接的UI樣式。點擊能夠跳轉(zhuǎn)打開網(wǎng)頁(最好是自己app內(nèi)部的WebView联四。)

簡單實現(xiàn):

在 xml 里給TextView 設(shè)置:

<TextView 
    android:autoLink="web"
    android:linksClickable="true"
    android:textColorLink="@color/color_017EBD"
.../>

簡單,但是有很大的缺點:

  1. 鏈接下的下劃線去不掉撑教。
  2. 只能跳轉(zhuǎn)到系統(tǒng)默認的瀏覽器朝墩。
  3. 鏈接后面不佳空格有中文就識別不出來。

雖然它不好伟姐。但它是系統(tǒng)自帶的功能收苏,可以看看它的實現(xiàn)原理,可以關(guān)鍵地方改寫它愤兵。

原理:

根據(jù)所學(xué)知識鹿霸。想必也是ClickableSpan的一種實際使用罷了「讶椋可以先看看:ClickableSpan的一點點摸索 懦鼠。

關(guān)鍵類:

  1. URLSpan:ClickableSpan的子類,帶URL屹堰,下劃線去不掉葛闷。
  2. LinkMovementMethod:ClickableSpan生效所需,之前講了双藕。
  3. Linkify:一個識別文字中鏈接淑趾,地址,郵箱之類的工具類忧陪。

前面兩個看ClickableSpan那篇就理解了扣泊。重點看Linkify。

Linkify:

Linkify take a piece of text and a regular expression and turns all of the regex matches in the text into clickable links. This is particularly useful for matching things like email addresses, web URLs, etc. and making them actionable.

Linkify是一個輔助類嘶摊,通過RegEx樣式匹配延蟹,自動地在TextView類(和繼承的類)中創(chuàng)建超鏈接。

它的方法public方法有多個重載方法叶堆。只需要了解它的主要功能阱飘。

我們可以用它實現(xiàn)超鏈接的識別。寫個工具類虱颗。

工具類:

我們只需要自定義RegEx就可以實現(xiàn)我們自己的識別超鏈接規(guī)則:

object HyperLinkHelper {

    private const val REGEX = "(((http[s]?|ftp?|file?)://)?[a-zA-Z0-9.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9.\\-~!@#$%^&*+?:_/=<>]*)?)"

    /**
     * 轉(zhuǎn)換成鏈接沥匈。
     */
    fun transUrlSpan(text: CharSequence, @ColorInt highColor: Int = -0xa8946b): Spannable {
        val ss = SpannableString.valueOf(text)
        Linkify.addLinks(ss, Pattern.compile(REGEX), "")
        val urlSpans = ss.getSpans(0, ss.length,
                URLSpan::class.java) ?: return ss
        for (sp in urlSpans) {
            val start = ss.getSpanStart(sp)
            val end = ss.getSpanEnd(sp)
            ss.removeSpan(sp)
            ss.setSpan(UrlLinkSpan(sp.url.formatUrl(), highColor), start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
        }
        return ss
    }

    /**
     * 設(shè)置點擊事件。
     */
    fun setClickListener(spanned: Spanned, listener: UrlLinkSpan.OnClickListener) {
        spanned.getSpans(0, spanned.length, UrlLinkSpan::class.java)
                .forEach { it.listener = listener }
    }

    private fun String.formatUrl(): String {
        return if (indexOf("http") != 0
                && indexOf("ftp") != 0
                && indexOf("file") != 0) {
            "http://$this"
        } else {
            this
        }
    }
    // 并把URLSpan替換成樣式需要的Span忘渔。
    class UrlLinkSpan(val url: String, @ColorInt val highColor: Int) : ClickableSpan() {

        var listener: OnClickListener? = null

        interface OnClickListener {
            fun onUrlLinkClick(widget: View, url: String)
        }

        override fun onClick(widget: View) {
            listener?.onUrlLinkClick(widget, url)
        }

        override fun updateDrawState(ds: TextPaint) {
            super.updateDrawState(ds)
            ds.color = highColor
            ds.isUnderlineText = false
        }
    }
    
}

整一個 BindingAdapter 方法:

@BindingAdapter(value = ["binding_text_url_link", "binding_url_link_listener"], requireAll = true)
fun TextView.setUrlLinkText(text: CharSequence?, listener: HyperLinkHelper.UrlLinkSpan.OnClickListener) {
    if (this.text?.toString() != text) {
        this.text = if (text != null) {
            HyperLinkHelper.transUrlSpan(text, getColor(R.color.color_017EBD)).apply {
                HyperLinkHelper.setClickListener(this, listener)
                movementMethod = ClickLinkMovementMethod
            }
        } else {
            ""
        }
    }
}

ClickLinkMovementMethod 是為了解決長按的問題高帖。ClickableSpan的一點點摸索那篇文章有講。

擴展:

Linkify還可以定義如下接口:

  1. Match Filter:實現(xiàn)acceptMatch方法畦粮,來為RegEx樣式匹配添加額外的條件散址。當一個潛在的匹配發(fā)現(xiàn)時乖阵,acceptMatch被觸發(fā),匹配的開始點和結(jié)束點(包括被查找的整個文本)以參數(shù)的形式傳入预麸。
  2. Transform Filter:為格式化文本字符串提供更大的自由度瞪浸,允許你修改由鏈接文本自動生成的隱式URI。

有興趣自己去了解吏祸。

總結(jié):

要點:

  1. 正則識別鏈接
  2. 顯示樣式自定義对蒲,去掉下劃線。
  3. 點擊邏輯自定義犁罩,跳轉(zhuǎn)到自己的WebView齐蔽。
  4. 解決長按沖突問題两疚。
配一張圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末床估,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诱渤,更是在濱河造成了極大的恐慌丐巫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勺美,死亡現(xiàn)場離奇詭異递胧,居然都是意外死亡,警方通過查閱死者的電腦和手機赡茸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門缎脾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人占卧,你說我怎么就攤上這事遗菠。” “怎么了华蜒?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵辙纬,是天一觀的道長。 經(jīng)常有香客問我叭喜,道長贺拣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任捂蕴,我火速辦了婚禮譬涡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啥辨。我一直安慰自己昂儒,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布委可。 她就那樣靜靜地躺著渊跋,像睡著了一般腊嗡。 火紅的嫁衣襯著肌膚如雪蜂桶。 梳的紋絲不亂的頭發(fā)上秒紧,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音盆赤,去河邊找鬼蒿囤。 笑死客们,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的材诽。 我是一名探鬼主播底挫,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脸侥!你這毒婦竟也來了建邓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤睁枕,失蹤者是張志新(化名)和其女友劉穎官边,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體外遇,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡注簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了跳仿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诡渴。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖菲语,靈堂內(nèi)的尸體忽然破棺而出妄辩,到底是詐尸還是另有隱情,我是刑警寧澤谨究,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布恩袱,位于F島的核電站,受9級特大地震影響胶哲,放射性物質(zhì)發(fā)生泄漏畔塔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一鸯屿、第九天 我趴在偏房一處隱蔽的房頂上張望澈吨。 院中可真熱鬧,春花似錦寄摆、人聲如沸谅辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桑阶。三九已至柏副,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚣录,已是汗流浹背割择。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萎河,地道東北人荔泳。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像虐杯,于是被迫代替她去往敵國和親玛歌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353