android TextView 实现富文本显示

news2024/11/18 9:39:29

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 = 0
    myLabels?.forEachIndexed { index, myLabel ->
        if (myLabel!= null){
            msgContent.append(myLabel.title)
            indexMap[index] =startIndex
            startIndex += 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){
                //要让图片替代指定的文字就要用ImageSpan
                val 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
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1479099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Prometheus 安装指南

目录 介绍 安装 介绍 Prometheus是一款开源监控系统&#xff0c;适用于容器化和微服务。它使用多维数据模型&#xff0c;支持PromQL查询语言&#xff0c;可以通过多种方式采集数据。具备灵活的告警和通知机制&#xff0c;可集成图形工具创建仪表盘。通过本地存储高效保存时间…

ywtool check命令及ywtool clean命令

一.ywtool check命令 1.1 ywtool check -I 1.2 ywtool check all 1.3 ywtool check io 1.4 ywtool check elk 1.5 ywtool check php 1.6 ywtool check mysql 1.7 ywtool check nginx 1.8 ywtool check system 1.9 ywtool check docker_nbip [容器名称] 1.10 ywtool check 1.10…

buuctf_crypto_丢失的MD5+Quoted-printable+RSA

丢失的MD5 题目&#xff1a;&#xff08;就一python文件&#xff0c;MD5.py&#xff09; for i in range(32,127):for j in range(32,127):for k in range(32,127):mhashlib.md5()m.update(TASCchr(i)O3RJMVchr(j)WDJKXchr(k)ZM)desm.hexdigest()if e9032 in des and da in d…

吴恩达《机器学习》学习笔记

本笔记资料来源于 http://www.ai-start.com/ml2014/&#xff0c;该笔记来自于https://blog.csdn.net/dadapongi6/article/details/105668394&#xff0c;看了忘&#xff0c;忘了看&#xff0c;再看一遍。 时间统计&#xff1a;2024.2.29 5个番茄钟&#xff0c;从week1开始&…

游戏陪玩精品系统源码优化版3.0

下载地址&#xff1a;游戏陪玩精品系统源码优化版.zip 截图&#xff1a;

[Vulnhub]靶场 Web Machine(N7)

kali:192.168.56.104 主机探测: arp-scan -l 靶机ip:192.168.56.104 端口扫描 nmap -p- 192.168.56.106 看一下web 目录扫描 gobuster dir -u http://192.168.56.106 -x html,txt,php,bak,zip --wordlist/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt exp…

基于springboot实现街球社区网站系统项目【项目源码+论文说明】

基于springboot实现街球社区网站系统演示 摘要 本文主要讲述了基于SpringBootVue模式的街球社区网站的设计与实现。这里所谓的街球社区网站是通过类似于百度贴吧之类的网上论坛使得所有的街球爱好者有一个可以互相交流的平台,并使所有用户可以在社区进行教学视频的观看以及相关…

使用Fabric创建的canvas画布背景图片,自适应画布宽高

之前的文章写过vue2使用fabric实现简单画图demo&#xff0c;完成批阅功能&#xff1b;但是功能不完善&#xff0c;对于很大的图片就只能显示一部分出来&#xff0c;不符合我们的需求。这就需要改进&#xff0c;对我们设置的背景图进行自适应。 有问题的canvas画布背景 修改后的…

Python:练习:编写一个程序,写入一个美金数量,然后显示出如何用最少的20美元、10美元、5美元和1美元来付款

案例&#xff1a; python编写一个程序&#xff0c;写入一个美金数量&#xff0c;然后显示出如何用最少的20美元、10美元、5美元和1美元来付款&#xff1a; Enter a dollar amout:93 $20 bills: 4 $10 bills: 1 $5 bills:0 $1 bills:3 思考&#xff1a; 写入一个美金数量&…

leetcode 热题 100_字母异位词分组

题解一&#xff1a; 排序&#xff1a;对两个字母异位词&#xff0c;二者排序后的字符串完全一样&#xff0c;因此可以对所给字符串进行排序&#xff0c;以排序后的字符串作为HashMap哈希表的键值&#xff0c;将排序前的字符串作为值进行存储分组&#xff0c;最后返回。 import…

面试数据库篇(mysql)- 08事务

原理 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。 ACID是什么?可以详细说一下吗? 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全…

element-ui附件上传及在线查看详细总结,后续赋源码

一、附件上传 1、在element-ui上面复制相应代码 a、accept"image/*,.pdf,.docx,.xlsx,.doc,.xls" 是规定上传文件的类型&#xff0c;若是不限制&#xff0c;可以直接将accept‘all即可&#xff1b; b、:action"action" 这个属性就是你的上传附件的地址&am…

全网首个GDB移植手册【Howto:Porting the GUN Debugger】翻译

Howto:Porting the GUN Debugger ✍【作者】&#xff1a;电子科大不知名程序员 &#x1f4e3;【说明】&#xff1a;本文是自己在搭建mcore架构GDB时的参考的手册&#xff0c;具有很强的学习指导性&#xff0c;因原文档&#xff08;链接&#xff1a;https://www.embecosm.com/a…

【详识JAVA语言】运算符

什么是运算符 计算机的最基本的用途之一就是执行数学运算&#xff0c;比如&#xff1a; int a 10; int b 20;a b; a < b; 上述 和< 等就是运算符&#xff0c;即&#xff1a;对操作数进行操作时的符号&#xff0c;不同运算符操作的含义不同。 作为一门计算机语言&…

用于游戏开发的顶级 PYTHON 框架

一、说明 我们试图用python开发游戏&#xff0c;一旦产生这个念头&#xff0c;就伴随这样一个问题&#xff1a;当今用于构建游戏的领先 Python 框架有哪些&#xff1f;python下&#xff0c;支持游戏开发平台有哪些优势&#xff1f;我们在这篇博文中告诉你。 二、高级游戏平台简…

Ubuntu20.04 ssh终端登录后未自动执行.bashrc

sudo vim ~/.profile输入以下内容 if [ -n "$BASH_VERSION" ]; then if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi 执行 source ~/.profile重新测试 其他答案 如果你的~/.bashrc文件在Ubuntu中没有自动生效&#xff0c;…

【Sora原理与技术实战】Task1 Sora技术原理解析

本笔记来源&#xff1a;开源组织Datawhale24年组队学习 笔记链接&#xff1a;https://datawhaler.feishu.cn/wiki/LxSCw0EyRidru1kFkttc1jNQnnh 直播回看&#xff1a;https://www.bilibili.com/video/BV1wm411f7gf/ For the learner for the dreamer Sora技术原理解析 Sora具体…

Codeforces Round 930 (Div. 2)

Codeforces Round 930 (Div. 2) Codeforces Round 930 (Div. 2) A. Shuffle Party 题意&#xff1a; 给出长度为n的整数数组a&#xff0c; a i a_i ai​ i&#xff0c;对于k>2的下标进行运算&#xff0c;设d为k除本身外最大的除数&#xff0c; 操作为交换( a k a_k ak​…

智慧城市:打造宜居环境,引领未来可持续发展

随着科技的不断进步与创新&#xff0c;我们的城市正步入一个崭新的时代——智慧城市。智慧城市是指运用信息技术和大数据等现代科技手段&#xff0c;对城市基础设施、公共服务和社会管理进行智能化改造&#xff0c;实现城市各领域的智能化、信息化和高效化。今天&#xff0c;就…

.net 日志

一、Log4net 1、log4net写入文本 1、nuget引入log4net、Microsoft.Extensions.Logging.Log4Net.AspNetCore这2个 2、引入配置文件,可以直接去官网(log4net官网配置文件)复制下来,放到项目目录下面,设置成始终复制,因为这个文件最终要到我们项目运行目录下面去 3、要在pr…