欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何利用Android仿微博正文鏈接交互效果

 更新時間:2022年04月24日 16:35:50   作者:笑慢  
最近在開發(fā)中遇到了各種坑,所以分享一下,希望能給大家貢獻(xiàn)點(diǎn)經(jīng)驗(yàn),下面這篇文章主要給大家介紹了關(guān)于如何利用Android仿微博正文鏈接交互效果的相關(guān)資料,需要的朋友可以參考下

前言

社區(qū)內(nèi)容經(jīng)常會有插入鏈接的需要,這時就產(chǎn)生了對鏈接的UI和點(diǎn)擊交互的需求,我們在微博中也經(jīng)常會在列表頁面和詳情頁面看到。下邊我們就此功能分析一下具體實(shí)現(xiàn)。

一、鏈接的匹配和顯示交互

首先我們先分析一下鏈接的組成部分,可以肯定的是需要一個顯示的標(biāo)題,我們可能會對這個標(biāo)題在UI表現(xiàn)上做些處理(常見的是一個鏈接的標(biāo)志和設(shè)置不同的顏色)來提示和吸引用戶的注意,另外還需要點(diǎn)擊時跳轉(zhuǎn)的鏈接,這條鏈接可以是內(nèi)部也可以是外部(這就屬于業(yè)務(wù)的需求)。關(guān)于鏈接的匹配方式可能會有不同的方案,我們這里選擇了使用a標(biāo)簽的匹配方式,也就是接口會把鏈接的數(shù)據(jù)以a標(biāo)簽的形式給我們,客戶端來進(jìn)行匹配數(shù)據(jù),下邊我們簡單的舉一個例子,接口返回數(shù)據(jù)如下:

"你說的<a  rel="external nofollow"  rel="external nofollow" >我是鏈接</a>11111<a  rel="external nofollow"  rel="external nofollow" >我也是鏈接</a>好開心啊,哈哈哈哈"

接下來我們對數(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)志和顏色替換原來的a標(biāo)簽插入正文數(shù)據(jù)中。接下來就是設(shè)置顯示的數(shù)據(jù):

val string = "你說的<a  rel="external nofollow"  rel="external nofollow" >我是鏈接</a>11111<a  rel="external nofollow"  rel="external nofollow" >我也是鏈接</a>好開心啊,哈哈哈哈"
lifecycleScope.launch {
    val contentString = LinkCheckHelper.computeLenFilterLink(string,this@MainActivity)
    tvLink.text = contentString
}

二、鏈接的點(diǎn)擊交互

參考微博的交互效果我們會發(fā)現(xiàn)當(dāng)我們觸摸鏈接內(nèi)容時其背景會由透明變?yōu)殒溄游淖值念伾?,手指抬起時又置回透明。細(xì)心的你肯定發(fā)現(xiàn)我們在剛才鏈接數(shù)據(jù)的插入時設(shè)置了MyLinkClickSpan做了對鏈接點(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)背景顏色的變化是需要對手指按下和抬起分別進(jìn)行處理,所以這時不可避免的我們就要對觸摸事件進(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)擊無效
        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) {
            //按下時記下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í)際上沒點(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,修改對應(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) {
                    //手指移動時 設(shè)置按下為false,對應(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è)置按下為false,對應(yīng)的鏈接文字背景顏色置回透明
                    mPressedSpan!!.setPressed(false)
                    //傳遞點(diǎn)擊事件回調(diào)
                    mPressedSpan!!.onClick(textView)
                }
                mPressedSpan = null
                Selection.removeSelection(spannable)
                touchSpanHint
            }
            else -> {
                if (mPressedSpan != null) {
                    //其它收拾 都設(shè)置按下為false,對應(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í)際上沒點(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
    }
}

代碼比較簡單,就是對點(diǎn)擊區(qū)域判斷是否為鏈接,然后根據(jù)手勢的操作分別設(shè)置給ClickSpan是否按下,來改變鏈接的背景顏色。

另外需要注意一點(diǎn)的是必須要調(diào)用下邊的代碼:

movementMethod = MyLinkMovementMethod.instance

此方法設(shè)置鏈接的點(diǎn)擊。 另外,不同的機(jī)型上系統(tǒng)會有一個鏈接的高亮顏色,我們需要調(diào)用

highlightColor = Color.TRANSPARENT

來取消掉。

這樣,鏈接的顯示和點(diǎn)擊交互就完成了。 具體效果:

點(diǎn)擊獲取源碼

總結(jié)

到此這篇關(guān)于如何利用Android仿微博正文鏈接交互效果的文章就介紹到這了,更多相關(guān)Android微博正文鏈接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決flutter 錯誤: 程序包androidx.lifecycle不存在問題

    解決flutter 錯誤: 程序包androidx.lifecycle不存在問題

    這篇文章主要介紹了解決flutter 錯誤: 程序包androidx.lifecycle不存在問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Android自定義實(shí)現(xiàn)開關(guān)按鈕代碼

    Android自定義實(shí)現(xiàn)開關(guān)按鈕代碼

    經(jīng)??梢钥吹揭恍┻x擇開個狀態(tài)的配置文件,但是外觀都不多好看。我感覺還是自定義的比較好,下面小編給大家介紹通過Android自定義實(shí)現(xiàn)開關(guān)按鈕代碼,感興趣的童鞋一起學(xué)習(xí)吧
    2016-05-05
  • Android仿手機(jī)QQ圖案解鎖功能

    Android仿手機(jī)QQ圖案解鎖功能

    這篇文章主要為大家詳細(xì)介紹了Android仿手機(jī)QQ圖案解鎖功能的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Android application捕獲崩潰異常怎么辦

    Android application捕獲崩潰異常怎么辦

    Android application捕獲崩潰異常怎么辦?這篇文章主要為大家詳細(xì)介紹了Android application捕獲崩潰異常的相關(guān)資料
    2017-06-06
  • Android彈出DatePickerDialog并獲取值的方法

    Android彈出DatePickerDialog并獲取值的方法

    這篇文章主要為大家詳細(xì)介紹了Android彈出DatePickerDialog并獲取值的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • 關(guān)于Android發(fā)送短信獲取送達(dá)報告的問題(推薦)

    關(guān)于Android發(fā)送短信獲取送達(dá)報告的問題(推薦)

    最近公司開發(fā)一個項(xiàng)目,要求app能夠發(fā)送短信并獲取送達(dá)報告。實(shí)現(xiàn)代碼非常簡單的,下面小編給大家分享關(guān)于Android發(fā)送短信獲取送達(dá)報告的問題,感興趣的朋友一起看看吧
    2017-03-03
  • Android清除應(yīng)用緩存的兩種方法

    Android清除應(yīng)用緩存的兩種方法

    這篇文章主要介紹了Android清除應(yīng)用緩存的兩種方法,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下
    2021-03-03
  • 這些小工具讓你的Android開發(fā)更高效

    這些小工具讓你的Android開發(fā)更高效

    這些小工具讓你的Android開發(fā)更高效,快來看看吧
    2016-06-06
  • FFmpeg?音頻可視化解碼流程詳解

    FFmpeg?音頻可視化解碼流程詳解

    這篇文章主要為大家介紹了FFmpeg?音頻可視化解碼流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Android第三方登錄之QQ登錄

    Android第三方登錄之QQ登錄

    這篇文章主要為大家詳細(xì)介紹了Android第三方登錄之QQ登錄,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評論