您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么利用Android仿微博正文鏈接交互效果”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么利用Android仿微博正文鏈接交互效果”吧!
首先我們先分析一下鏈接的組成部分,可以肯定的是需要一個(gè)顯示的標(biāo)題,我們可能會(huì)對(duì)這個(gè)標(biāo)題在UI表現(xiàn)上做些處理(常見(jiàn)的是一個(gè)鏈接的標(biāo)志和設(shè)置不同的顏色)來(lái)提示和吸引用戶(hù)的注意,另外還需要點(diǎn)擊時(shí)跳轉(zhuǎn)的鏈接,這條鏈接可以是內(nèi)部也可以是外部(這就屬于業(yè)務(wù)的需求)。關(guān)于鏈接的匹配方式可能會(huì)有不同的方案,我們這里選擇了使用a標(biāo)簽的匹配方式,也就是接口會(huì)把鏈接的數(shù)據(jù)以a標(biāo)簽的形式給我們,客戶(hù)端來(lái)進(jìn)行匹配數(shù)據(jù),下邊我們簡(jiǎn)單的舉一個(gè)例子,接口返回?cái)?shù)據(jù)如下:
"你說(shuō)的<a href="https://www.qq.com" rel="external nofollow" rel="external nofollow" >我是鏈接</a>11111<a href="https://www.baidu.com" rel="external nofollow" rel="external nofollow" >我也是鏈接</a>好開(kāi)心啊,哈哈哈哈"
接下來(lái)我們對(duì)數(shù)據(jù)的處理:
/** * 匹配a標(biāo)簽直接插入鏈接 */ suspend fun computeLenFilterLink(text: String, mContext: Context): SpannableStringBuilder = withContext(Dispatchers.Default) { var strings = SpannableStringBuilder(text) val pattern = "<a \s*href\s*=\s*(?:.*?)>(.*?)</a\s*>" val p = Pattern.compile(pattern) val matcher = p.matcher(strings) while (matcher.find()) { val str = matcher.group() val linkTitle = matcher.group(1) ?: "" //a標(biāo)簽鏈接正則匹配 val patternUrlString = "\s*(?i)href\s*=\s*("([^"]*")|'[^']*'|([^'">\s]+))" val patternUrl = Pattern.compile( patternUrlString, Pattern.CASE_INSENSITIVE ) //鏈接url val matcherUrL = patternUrl.matcher(strings) var linkUrl = "" while (matcherUrL.find()) { linkUrl = matcherUrL.group() linkUrl = linkUrl.replace("href\s*=\s*(['|"]*)".toRegex(), "") linkUrl = linkUrl.replace("['|"]".toRegex(), "") linkUrl = linkUrl.trim { it <= ' ' } break } //設(shè)置顏色 val sb = SpannableString("#$linkTitle") sb.setSpan( ForegroundColorSpan( ContextCompat.getColor(mContext, R.color.link_color) ), 0, sb.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) sb.setSpan( object : MyLinkClickSpan(mContext) { override fun onSpanClick(widget: View?) { //鏈接跳轉(zhuǎn) Toast.makeText(mContext, "點(diǎn)擊鏈=$linkUrl", Toast.LENGTH_SHORT).show() } }, 0, sb.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val start = strings.indexOf(str) strings.delete(start, start + str.length) //插入鏈接 strings.insert(start, sb) } return@withContext strings }
這里我們是用正則匹配的方式取出鏈接的標(biāo)題和跳轉(zhuǎn)鏈接,然后設(shè)置鏈接的標(biāo)志和顏色替換原來(lái)的a標(biāo)簽插入正文數(shù)據(jù)中。接下來(lái)就是設(shè)置顯示的數(shù)據(jù):
val string = "你說(shuō)的<a href="https://www.qq.com" rel="external nofollow" rel="external nofollow" >我是鏈接</a>11111<a href="https://www.baidu.com" rel="external nofollow" rel="external nofollow" >我也是鏈接</a>好開(kāi)心啊,哈哈哈哈" lifecycleScope.launch { val contentString = LinkCheckHelper.computeLenFilterLink(string,this@MainActivity) tvLink.text = contentString }
參考微博的交互效果我們會(huì)發(fā)現(xiàn)當(dāng)我們觸摸鏈接內(nèi)容時(shí)其背景會(huì)由透明變?yōu)殒溄游淖值念伾?,手指抬起時(shí)又置回透明。細(xì)心的你肯定發(fā)現(xiàn)我們?cè)趧偛沛溄訑?shù)據(jù)的插入時(shí)設(shè)置了MyLinkClickSpan做了對(duì)鏈接點(diǎn)擊的處理,那么背景顏色的改變就要在ClickSpan中的 updateDrawState(ds: TextPaint)方法中進(jìn)行處理,代碼如下:
class MyLinkClickSpan(private val context: Context) : ClickableSpan(), IPressedSpan { private var isPressed = false abstract fun onSpanClick(widget: View?) override fun onClick(widget: View) { if (ViewCompat.isAttachedToWindow(widget)) { onSpanClick(widget) } } override fun setPressed(pressed: Boolean) { isPressed = pressed } override fun updateDrawState(ds: TextPaint) { if (isPressed) { ds.bgColor = ContextCompat.getColor(context, R.color.link_bg_color) } else { ds.bgColor = ContextCompat.getColor(context, android.R.color.transparent) } ds.isUnderlineText = false } }
我們發(fā)現(xiàn)背景顏色的變化是需要對(duì)手指按下和抬起分別進(jìn)行處理,所以這時(shí)不可避免的我們就要對(duì)觸摸事件進(jìn)行處理:
class LinkTextView(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) { private var mPressedSpan: IPressedSpan? = null init { isFocusable = false isLongClickable = false // 有鏈接點(diǎn)擊需求不設(shè)置則點(diǎn)擊無(wú)效 movementMethod = MyLinkMovementMethod.instance highlightColor = Color.TRANSPARENT } override fun onTouchEvent(event: MotionEvent): Boolean { val text = text val spannable = Spannable.Factory.getInstance().newSpannable(text) if (event.action == MotionEvent.ACTION_DOWN) { //按下時(shí)記下clickSpan mPressedSpan = getPressedSpan(this, spannable, event) } return if (mPressedSpan != null) { //如果有clickSpan就走M(jìn)yLinkMovementMethod的onTouchEvent MyLinkMovementMethod.instance.onTouchEvent(this, getText() as Spannable, event) } else { super.onTouchEvent(event) } } private fun getPressedSpan( textView: TextView, spannable: Spannable, event: MotionEvent ): IPressedSpan? { var mTouchSpan: IPressedSpan? = null var x = event.x.toInt() var y = event.y.toInt() x -= textView.totalPaddingLeft x += textView.scrollX y -= textView.totalPaddingTop y += textView.scrollY val layout = textView.layout val line = layout.getLineForVertical(y) try { var off = layout.getOffsetForHorizontal(line, x.toFloat()) if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) { // 實(shí)際上沒(méi)點(diǎn)到任何內(nèi)容 off = -1 } val linkSpans = spannable.getSpans(off, off, IPressedSpan::class.java) if (!linkSpans.isNullOrEmpty()) { mTouchSpan = linkSpans[0] } return mTouchSpan } catch (e: IndexOutOfBoundsException) { Log.d(this.toString(), "getPressedSpan", e) } return null } }
class MyLinkMovementMethod : LinkMovementMethod() { override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean { return (sHelper.onTouchEvent(widget, buffer, event)) } companion object { val instance: MyLinkMovementMethod get() { if (sInstance == null) { sInstance = MyLinkMovementMethod() } return sInstance as MyLinkMovementMethod } private var sInstance: MyLinkMovementMethod? = null private val sHelper = SpanClickHelper() } }
class SpanClickHelper { private var mPressedSpan: IPressedSpan? = null fun onTouchEvent( textView: TextView, spannable: Spannable, event: MotionEvent ): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { mPressedSpan = getPressedSpan(textView, spannable, event) if (mPressedSpan != null) { //手指按下 設(shè)置按下為true,修改對(duì)應(yīng)的鏈接文字背景顏色 mPressedSpan!!.setPressed(true) //設(shè)置選中區(qū)域 Selection.setSelection( spannable, spannable.getSpanStart(mPressedSpan), spannable.getSpanEnd(mPressedSpan) ) } mPressedSpan != null } MotionEvent.ACTION_MOVE -> { val touchedSpan = getPressedSpan(textView, spannable, event) if (mPressedSpan != null && touchedSpan != mPressedSpan) { //手指移動(dòng)時(shí) 設(shè)置按下為false,對(duì)應(yīng)的鏈接文字背景顏色置回透明 mPressedSpan!!.setPressed(false) mPressedSpan = null //移除選中區(qū)域 Selection.removeSelection(spannable) } mPressedSpan != null } MotionEvent.ACTION_UP -> { var touchSpanHint = false if (mPressedSpan != null) { touchSpanHint = true //手指抬起時(shí) 設(shè)置按下為false,對(duì)應(yīng)的鏈接文字背景顏色置回透明 mPressedSpan!!.setPressed(false) //傳遞點(diǎn)擊事件回調(diào) mPressedSpan!!.onClick(textView) } mPressedSpan = null Selection.removeSelection(spannable) touchSpanHint } else -> { if (mPressedSpan != null) { //其它收拾 都設(shè)置按下為false,對(duì)應(yīng)的鏈接文字背景顏色置回透明 mPressedSpan!!.setPressed(false) } //移除選中區(qū)域 Selection.removeSelection(spannable) false } } } /** * 判斷手指是否點(diǎn)擊在鏈接上 */ private fun getPressedSpan( textView: TextView, spannable: Spannable, event: MotionEvent ): IPressedSpan? { var x = event.x.toInt() var y = event.y.toInt() x -= textView.totalPaddingLeft y -= textView.totalPaddingTop x += textView.scrollX y += textView.scrollY val layout = textView.layout val line = layout.getLineForVertical(y) try { var off = layout.getOffsetForHorizontal(line, x.toFloat()) if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) { // 實(shí)際上沒(méi)點(diǎn)到任何內(nèi)容 off = -1 } val link = spannable.getSpans( off, off, IPressedSpan::class.java ) var touchedSpan: IPressedSpan? = null if (link.isNotEmpty()) { touchedSpan = link[0] } return touchedSpan } catch (e: IndexOutOfBoundsException) { Log.d(this.toString(), "getPressedSpan", e) } return null } }
代碼比較簡(jiǎn)單,就是對(duì)點(diǎn)擊區(qū)域判斷是否為鏈接,然后根據(jù)手勢(shì)的操作分別設(shè)置給ClickSpan是否按下,來(lái)改變鏈接的背景顏色。
另外需要注意一點(diǎn)的是必須要調(diào)用下邊的代碼:
movementMethod = MyLinkMovementMethod.instance
此方法設(shè)置鏈接的點(diǎn)擊。 另外,不同的機(jī)型上系統(tǒng)會(huì)有一個(gè)鏈接的高亮顏色,我們需要調(diào)用
highlightColor = Color.TRANSPARENT
來(lái)取消掉。
這樣,鏈接的顯示和點(diǎn)擊交互就完成了。 具體效果:
到此,相信大家對(duì)“怎么利用Android仿微博正文鏈接交互效果”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。