需求:
如題所訴吁朦。需要識別出文本中的鏈接搁廓,并顯示成超鏈接的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"
.../>
簡單,但是有很大的缺點:
- 鏈接下的下劃線去不掉撑教。
- 只能跳轉(zhuǎn)到系統(tǒng)默認的瀏覽器朝墩。
- 鏈接后面不佳空格有中文就識別不出來。
雖然它不好伟姐。但它是系統(tǒng)自帶的功能收苏,可以看看它的實現(xiàn)原理,可以關(guān)鍵地方改寫它愤兵。
原理:
根據(jù)所學(xué)知識鹿霸。想必也是ClickableSpan的一種實際使用罷了「讶椋可以先看看:ClickableSpan的一點點摸索 懦鼠。
關(guān)鍵類:
- URLSpan:ClickableSpan的子類,帶URL屹堰,下劃線去不掉葛闷。
- LinkMovementMethod:ClickableSpan生效所需,之前講了双藕。
- 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還可以定義如下接口:
- Match Filter:實現(xiàn)acceptMatch方法畦粮,來為RegEx樣式匹配添加額外的條件散址。當一個潛在的匹配發(fā)現(xiàn)時乖阵,acceptMatch被觸發(fā),匹配的開始點和結(jié)束點(包括被查找的整個文本)以參數(shù)的形式傳入预麸。
- Transform Filter:為格式化文本字符串提供更大的自由度瞪浸,允許你修改由鏈接文本自動生成的隱式URI。
有興趣自己去了解吏祸。
總結(jié):
要點:
- 正則識別鏈接
- 顯示樣式自定義对蒲,去掉下劃線。
- 點擊邏輯自定義犁罩,跳轉(zhuǎn)到自己的WebView齐蔽。
- 解決長按沖突問題两疚。