天天看點

Android 富文本 點選變色、事件傳遞

開發語言為Kotlin,還在使用java的小朋友,不妨試試

在android中富文本的使用近乎頻繁了,網上資料頗多,手上剛好在做一個帶有圈子子產品的項目

Android 富文本 點選變色、事件傳遞

貼代碼:

自定義類 DefaultClickSpan.kt

import android.support.v4.content.ContextCompat
import android.text.TextPaint
import android.text.style.ClickableSpan
import android.view.View
import android.widget.TextView
import com.qiufeng.educationteacher.R
import com.qiufeng.educationteacher.application.App
import com.qiufeng.educationteacher.ui.listener.ClickListener
import io.reactivex.Flowable
import java.util.concurrent.TimeUnit

class DefaultClickSpan(
        val params: Any? = null,
        val listener: ClickListener? = null,
        val color: Int = -1
) : ClickableSpan() {

    override fun updateDrawState(ds: TextPaint?) {
        super.updateDrawState(ds)
        ds?.let {
        	// 點選文本顔色
            it.color = RUtils.getColor(if (color == -1) R.color.color_blue_grey else color)
            // 去除下劃線
            it.isUnderlineText = false
        }
    }
    override fun onClick(widget: View?) {
        widget?.let {
            if (it is TextView) {
            	// 點選時,被點選區域的背景色
                it.highlightColor = RUtils.getColor(R.color.color_grey)
                // 啟動一個延時任務,200毫秒後恢複區域顔色
                Flowable.timer(200, TimeUnit.MILLISECONDS).doOnComplete {
                    (widget as TextView).highlightColor = RUtils.getColor(R.color.color_transparent)
                }.subscribe()
            }
            // 點選區域時需要傳遞的參數
            if (params != null && listener != null) {
                listener.onClick(params)
            }
        }
    }
}
           

點選事件監聽 ClickListener.kt

interface ClickListener {
    fun onClick(any:Any)
}
           

工具類 RUtils.kt

class RUtils {
    companion object {
        /**
         * 擷取資源顔色色值
         * @param rId
         */
        fun getColor(rId: Int): Int {
            return ContextCompat.getColor(App.context, rId)
        }
    }
}
           

Span工具類:SpannedUtils.kt

class SpannedUtils {
    companion object {
        /**
         * 拼接點選Span
         * @param sb 拼接源
         * @param clickStr 需要拼接的點選文字
         * @param params 點選事件響應傳回的資料
         * @param color 拼接字元的前景色
         * @param clickListener 點選事件監聽
         */
        fun appendClickableSpan(sb: SpannableStringBuilder,
                                clickStr: String,
                                params: Any? = null,
                                color: Int = -1,
                                clickListener: ClickListener? = null) {
            val oldLength = sb.length
            sb.append(clickStr)
            sb.setSpan(DefaultClickSpan(params, clickListener, color), oldLength, sb.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    }
}
           

前端頁面調用(使用資料為類似點贊人清單)

private fun setText(list:MutableList<String>){
	val sb = SpannableStringBuilder()
	val length = list.size
	(0 until length){
		SpannedUtils.appendClickableSpan(sb,list[it],list[it],object:ClickListener{
		    override fun onClick(any: Any) {
              showToast(any.toString())      
            }
		})
	}
	// 顔色、回傳參數、監聽可以不加
	// SpannedUtils.appendClickableSpan(sb,list[it])
	tvHeart.text = sb
	// 這句很重要,否則點選無效
	tvHeart.movementMethod = LinkMovementMethod.getInstance()
}
           

發現一個重要問題:

在設定點選事件的時候,如果涉及到頁面的跳轉等UI操作,可能會産生一個bug:

Only the original thread that created a view hierarchy can touch its views

錯誤提示:(有道詞典)隻有建立了視圖層次結構的原始線程才能通路它的視圖;跨線程修改UI的問題

解決: 修改DefaultClickSpan類

将點選事件做一下延遲就好,可以用上面的Flowable.timer,延遲事件跟 恢複背景色的時間一緻就可以

// 部分代碼
if (params != null && listener != null) {
    Flowable.timer(200, TimeUnit.MILLISECONDS)
            .doOnComplete {
                listener.onClick(params)
            }.subscribe()
}
           

注釋應該夠了,可有幫助?