android TextView 實現富文本顯示,實現抖音直播間公屏消息案例
使用:
val tvContent: TextView = helper.getView(R.id.tvContent)//自己根據UI業務要求,可以控制 圖標顯示 大小val levelLabel = MyImgLabel( bitmap = 自己業務上的bitmap )val labelNickName = MyLabel(title = "昵稱",color = 自己給個顏色,myLabelClick = object : MyLabelClick {override fun click() {//點擊了昵稱事件}})val labelContent = MyLabel(title = "消息內容",color = 自己給個顏色)tvContent.setMySpannable(levelLabel, labelNickName, labelContent)//如果需要顯示多個 圖標 imgLabelxxx 是 MyImgLabel//MyLabel 與 MyImgLabel 擺放位置是根據自己的業務需求 擺放的,這只是個例子
tvContent.setMySpannable(levelLabel, labelNickName, imgLabelxxx,labelContent,imgLabelxxx)
代碼
/*** 點擊事件*/
interface MyLabelClick{fun click()
}
? 文本標簽?
open class MyLabel(var title:String,var textStyleIsBold:Boolean? = false, //字體是否加粗@ColorInt var color:Int,var myLabelClick: MyLabelClick?=null
)
圖標標簽
class MyImgLabel(var bitmap:Bitmap,var imgLabelClick: MyLabelClick? = null
) : MyLabel(title = "level", color = 0, myLabelClick = imgLabelClick)
?自定義 ImageSpan?
class CenteredImageSpan : ImageSpan {constructor(context: Context, drawableRes: Int) : super(context, drawableRes) {}constructor(context: Context, bitmap: Bitmap) : super(context, bitmap) {}override fun draw(@NonNull canvas: Canvas, text: CharSequence?,start: Int, end: Int, x: Float,top: Int, y: Int, bottom: Int, @NonNull paint: Paint) {val b: Drawable = getDrawable()val fm: Paint.FontMetricsInt = paint.getFontMetricsInt()val transY: Int = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2 //計算y方向的位移canvas.save()canvas.translate(x, transY.toFloat()) //繪制圖片位移一段距離b.draw(canvas)canvas.restore()}
}
?代碼塊
fun MyLabel.setSpannableColorAndClick(spannableString: SpannableString, myLabel: MyLabel, startIndex:Int, endIndex:Int){spannableString.setSpan(object: ClickableSpan(){override fun onClick(widget: View) {LogUtils.d("點擊事件")myLabel.myLabelClick?.click()}override fun updateDrawState(ds: TextPaint) {super.updateDrawState(ds)ds.color = myLabel.color//取消默認的下劃線ds.isUnderlineText = false}},startIndex,endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)}fun TextView.setMySpannable(spannableString: SpannableString){text = spannableString//中途遇到點擊后字體顯示高亮,取消高亮highlightColor = Color.parseColor("#00000000")//最后設置可點擊,必須實現,否則只能顯示樣式,無法實現點擊效果movementMethod = LinkMovementMethod.getInstance()
}fun TextView.setMySpannable(vararg myLabels: MyLabel?){text = getMySpannableString(context,*myLabels)//中途遇到點擊后字體顯示高亮,取消高亮highlightColor = Color.parseColor("#00000000")//最后設置可點擊,必須實現,否則只能顯示樣式,無法實現點擊效果movementMethod = LinkMovementMethod.getInstance()
}private fun getMySpannableString(context: Context,vararg myLabels: MyLabel?):SpannableString{//step1:得到全部顯示的內容var msgContent = StringBuffer()val indexMap:MutableMap<Int,Int> = mutableMapOf()var startIndex = 0myLabels?.forEachIndexed { index, myLabel ->if (myLabel!= null){msgContent.append(myLabel.title)indexMap[index] =startIndexstartIndex += myLabel.title.length}}val spannableString = SpannableString(msgContent)log("spannableString:${spannableString}")//step2:設置顏色以及點擊事件myLabels?.forEachIndexed { index, myLabel ->if (myLabel!= null){val startIndex = indexMap[index]val endIndex = startIndex?.plus(myLabel.title.length)if (myLabel is MyImgLabel){//要讓圖片替代指定的文字就要用ImageSpanval imageSpan = myLabel.bitmap?.let {CenteredImageSpan(context, it)}spannableString.setSpan(imageSpan, startIndex!!, endIndex!!, ImageSpan.ALIGN_BASELINE)}else{if (myLabel.textStyleIsBold == true){val styleSpan = StyleSpan(Typeface.BOLD)spannableString.setSpan(styleSpan,startIndex!!, endIndex!!,Spanned.SPAN_INCLUSIVE_EXCLUSIVE)}}myLabel.setSpannableColorAndClick(spannableString= spannableString,myLabel =myLabel,startIndex = startIndex!!,endIndex = endIndex!!)}}return spannableString
}