android iconfont带图标的图文并茂的一种实现

android实现图文并茂方法很多。
这里针对,仅本地图标,需要对齐,任意位置,兼容换行导致后面空白的问题做的一种方案。

www.iconfont.cn,注册;
上传svg的icon;
下载项目得到iconfont.ttf;
Typeface.createFromAsset得到TF。
替换unicode码iconfont的16进制,比如  改成\ue602
通过CustomTypefaceSpan, RelativeSizeSpan等修改语句中的unicode部分,拼接到ssb中设置给textView。

其他:
iconfont 某些svg上传到iconfont出现图标不对的问题。
参考:
https://zhuanlan.zhihu.com/p/494625217
第1个,先做outline stroke,轮廓化描边;
第2个,将多个图层合并成一层;
第3个,使用插件fill rule editor调整。

效果图:
在这里插入图片描述

class CustomTypefaceSpan(family: String?, private val newType: Typeface) : TypefaceSpan(family) {

    override fun updateDrawState(ds: TextPaint) {
        applyCustomTypeFace(ds, newType)
    }

    override fun updateMeasureState(paint: TextPaint) {
        applyCustomTypeFace(paint, newType)
    }

    companion object {
        private fun applyCustomTypeFace(paint: Paint, tf: Typeface) {
            val oldStyle: Int
            val old = paint.typeface
            oldStyle = old?.style ?: 0
            val fake = oldStyle and tf.style.inv()
            if (fake and Typeface.BOLD != 0) {
                paint.isFakeBoldText = true
            }
            if (fake and Typeface.ITALIC != 0) {
                paint.textSkewX = -0.25f
            }
            paint.typeface = tf
        }
    }
}

//如果有必要cache就通过hashmap来cache
fun getOrCreateFontFace(context: Context, assetsPath: String?) : Typeface? {
    if(assetsPath.isNullOrEmpty()) return null
    return Typeface.createFromAsset(context.assets, assetsPath)
}

/**
 * 一部分一部分的拼接
 */
interface IIconFontPart

/**
 * 拼接文字
 */
data class IconFontNormalPart(val normalText:String) : IIconFontPart

/**
 * 拼接上一个unicode的iconFont
 */
open class IconFontIconPart(val unicode:Char, val colorStr:String? = null, val relativeSize:Float? = null) : IIconFontPart

/**
 * 要求TextView自身已经具有常规的字体;常规的textSize;常规的颜色。
 *
 * 再来设置结合iconFontPart。
 */
fun TextView.setIconFont(vararg parts:IIconFontPart) {
    val sb = StringBuilder()
    parts.forEach {
        if (it is IconFontNormalPart) {
            sb.append(it.normalText)
        } else if (it is IconFontIconPart) {
            sb.append(it.unicode)
        }
    }
    val text = sb.toString()
    val ss = SpannableStringBuilder(text)

    var len = 0
    val iconTf = getOrCreateFontFace(globalContext, "fonts/iconfont.ttf")!!
    parts.forEach {
        if (it is IconFontNormalPart) {
            len += it.normalText.length
        } else if (it is IconFontIconPart) {
            ss.setSpan(CustomTypefaceSpan("", iconTf), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            if (it.colorStr != null) {
                ss.setSpan(ForegroundColorSpan(Color.parseColor(it.colorStr)), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            }
            if (it.relativeSize != null) {
                ss.setSpan(RelativeSizeSpan(it.relativeSize), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            }
            len += 1 //最后
        }
    }
    setText(ss)
}

那么使用上就会很简单了。

val blank = IconFontNormalPart(" ")
 binding.textView.setIconFont(
     IconFontNormalPart("小明和小红 "),
     IconFontIconPart('\ue602'),
     blank,
     IconFontIconPart('\ue604'),
     blank,
     IconFontIconPart('\ue604', relativeSize = 1.25f),
     IconFontNormalPart("一起去打水 "),
     IconFontIconPart('\ue604', colorStr = "#ff8899"),)

自动换行右侧空白问题

英文的自动换行可能会导致文字显示过长,而自动换行后右侧空出太多,如下图:
请添加图片描述
解决方案给基础TextView继承后添加代码:

//解决换行右侧太多的问题
var fixWrapEndSpace = false

//https://stackoverflow.com/questions/50287198/textview-remove-space-after-line-break
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    if (fixWrapEndSpace) {
        var maxWidth = ceil(getMaxLineWidth(layout)).toInt()
        maxWidth += paddingRight + paddingLeft
        setMeasuredDimension(maxWidth, measuredHeight)
    }
}

private fun getMaxLineWidth(layout: Layout): Float {
    var maximumWidth = 0.0f
    val lines = layout.lineCount
    for (i in 0 until lines) {
        maximumWidth = max(layout.getLineWidth(i), maximumWidth)
    }

    return maximumWidth
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文献阅读:通过高通量原位成对测序实现亚细胞分辨率的空间多组学

文献介绍 文献题目: Spatial multi-omics at subcellular resolution via high-throughput in situ pairwise sequencing 研究团队: 曹罡(深圳理工大学)、戴金霞(华中农业大学) 发表时间: 2024…

Nginx-http_limit_req_module模块

文章目录 前言一、ngx_http_limit_req_module模块二、指令1.limit_req_zone2.limit_req3.limit_req_log_level4.limit_req_status 实验burst取默认0的情况burst不取默认值 总结 前言 如何限制每个客户端每秒处理请求数 一、ngx_http_limit_req_module模块 生效阶段&#xff1…

node.js外卖小程序-计算机毕业设计源码81838

摘要 自从计算机发展开始,计算机软硬件相关技术的发展速度越来越快,在信息化高速发展的今天,计算机应用技术似乎已经应用到了各个领域。在餐饮行业,除了外卖以外就是到店里就餐,在店里就餐如果需要等待点餐的话&…

Vue的民族民俗文化分享平台-计算机毕业设计源码22552

基于Vue的民族民俗文化分享平台设计与实现 摘 要 本文介绍了一种基于Vue.js前端框架和Express后端框架的民族民俗文化分享平台的设计和实现。该平台旨在通过线上方式,促进民族民俗文化的传播与分享,增强公众对多元文化的了解和认同。 平台为普通用户提供…

谷粒商城学习笔记-14-项目结构创建提交到码云

一,码云上创建工程仓库 1,,点击右上角加号,选择新建仓库 2,填充必要信息 ①仓库名称,可以理解为工程名称。 ②仓库介绍,添加关于仓库的说明。 ③仓库权限设置,如果是公司项目,一般…

跨境电商入场第一步!先收集整理这些数据,看清自己该如何入场!【纯经验分享】

23年、24年确实无愧于“品牌出海元年”的称号,23年出海四小龙——速卖通、TikTokshop、Temu、Shein在海外的爆发让大家看到了海外市场的活动;而24年则有更多的国内品牌将目光瞄向了海外市场,年后开工到今天基本上每天都有客户来咨询出海相关的…

Spring Cloud 是什么?(Spring Cloud 组件介绍)

什么是 Spring Cloud? Spring Cloud 是微服务系统架构的一站式解决方案,是各个微服务架构落地技术的集合体,让架构师、 开发者在使用微服务理念构建应用系统的时候, 面对各个环节的问题都可以找到相应的组件来处理,比…

C语言中32位浮点数的格式

以 GNU C为例,它遵循 IEEE 754-2008标准中制定的浮点表示规范。在该规范中定义了 5种不同大小的基础二进制浮点格式,包括:16位,32位,64位,128位,256位。其中,32位的格式被用作标准 C…

springboot马拉松赛事志愿者管理系统-计算机毕业设计源码80251

摘 要 随着马拉松运动的兴起和发展,马拉松赛事的组织和管理面临着越来越多的挑战,其中志愿者的招募、培训和管理是至关重要的一环。传统的人力资源管理方式已经无法满足大型马拉松赛事对志愿者团队的需求,因此基于现代信息技术的马拉松赛事志…

MongoDB如何安装并配置公网地址实现Navicat远程连接本地数据库

文章目录 前言1. 安装Docker2. 使用Docker拉取MongoDB镜像3. 创建并启动MongoDB容器4. 本地连接测试5. 公网远程访问本地MongoDB容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 前言 本文主要介绍如何在Linux Ubuntu系统快速部署MongoDB&#…

ssm“落雪”动漫网站-计算机毕业设计源码81664

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据新增流程 3.2.2 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

开发必备基础知识【Linux环境变量文件合集】

开发必备基础知识【Linux环境变量文件合集】 在Linux系统中,环境配置文件用于定制用户的Shell环境,包括定义环境变量、设置命令别名、定义启动脚本等。不同的Shell(如bash、zsh)有着各自对应的配置文件。 .bashrc:每新…

使用myCobot280和OAK-D OpenCV DepthAI摄像头制作一个实时脸部跟踪的手机支架!

引言 由于YouTube和Netflix的出现,我们开始躺着看手机。然而,长时间用手拿着手机会让人感到疲劳。这次我们制作了一个可以在你眼前保持适当距离并调整位置的自动移动手机支架,让你无需用手拿着手机。请务必试试! 准备工作 这次我们…

[FreeRTOS 基础知识] 互斥量 概念

文章目录 基础知识互斥量互斥量与信号量区别优先级反转优先级继承小结 基础知识 [FreeRTOS 基础知识] 信号量 概念 互斥量 互斥量(Mutex,全称:Mutual Exclusion),在计算机科学中,是一种用于防止多个进程同…

C++20中的指定初始化器(designated initializers)

指定初始化器(designated initializers, 指定初始值设定项)语法如下:C风格指定初始化器语法,初始化数据成员的一种便捷方式 T object { .des1 arg1, .des2 { arg2 } ... }; T object { .des1 arg1, .des2 { arg2 } ... }; 说明: 1.每个指…

leetcode力扣_排序问题

215.数组中的第K个最大元素 鉴于已经将之前学的排序算法忘得差不多了,只会一个冒泡排序法了,就写了一个冒牌排序法,将给的数组按照降序排列,然后取nums[k-1]就是题目要求的,但是提交之后对于有的示例显示”超出时间限制…

竞赛 深度学习LSTM新冠数据预测

文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 …

C++deque容器

文章目录 deque容器概念deque操作deque对象的带参数构造deque头部和末尾的添加移除操作deque的数据存取deque与迭代器deque赋值deque插入deque删除 deque容器概念 deque是双端数组,而vector是单端的。 deque头部和尾部添加或移除元素都非常快速, 但是在中部安插元…

Ros2中goal_handle状态SUCCEED及ACCEPTED及CANCLED在rclpy中的死循环(彻底解决版本)

承接上文,遇到了在动作通信开发中,使用rclpy编写代码进行feedback等操作,但所有逻辑均编写完后,却无法将goal_handle提交为succeed状态,之前的解决方案是更改自己重写的execute()函数名为my_execute()并且在提交SUCCEE…

树莓派学习笔记18:IIC驱动_PCA9685(16路舵机驱动模块)误发

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Python 版本3.7.3: ​ IIC驱动_PCA9685(16路舵机驱动模块) 文章提供测试代码讲解,整体代码贴出、测试效果图 目录 开启树莓…