支持末尾携带标签的多行TextView

news2025/7/19 15:39:24

项目开发过程中,遇到个UI上的需求,本着不重复造轮子、敏捷开发的原则,于是乎网上找寻了一番,发现还是自己搞吧,搜不到这样的需求,先看下我们的效果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结有以下三点需要注意:

  • 末尾vip部分是整体,不能换行;
  • 单行或者不满2行时,后面vip部分直接显示在文字后面;
  • 超过2行后,文字部分显示…并且保证vip部分显示在最后。

那么这种效果,我们首先就排除了横向多个控件组合的方式,因为这样会导致末尾vip部分不是放在文本后面,而是显示在整个TextView的右侧了。

首先想到的就是用富文本啦,SpannableString大家都很熟悉了,没错就是它,后面vip部分是整体的话,那么我们就不能把它当成文字了,因为这样会遇到换行的情况,也不满足需求了,所以我们得把它处理成图片,这样我们就不用管它是否换行了,TextView会自动帮我们处理。但是我们vip部分是动态设置的,里面的+15取得是接口下发的值,那么这个地方我们肯定不能用写死的图了,那怎么处理呢?

有没有一种方案是把布局转换成图片呢?有!当然有!如下:

 private Bitmap createTaskTag(String num){
        View layout = LayoutInflater.from(getActivity()).inflate(R.layout.layout_task_tag_end, null);
        TextView tvNum = layout.findViewById(cn.emagsoftware.gamehall.R.id.tv_num);
        tvNum.setText(String.format("会员额外+%s云豆",num));
        layout.setDrawingCacheEnabled(true);
        layout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        layout.layout(0, 0,  ScreenUtils.dp2px(102), layout.getMeasuredHeight());
        Bitmap bitmap = Bitmap.createBitmap(layout.getDrawingCache());
        layout.destroyDrawingCache();
        return  bitmap;
    }

既然图片已经有了,那么就简单了,直接上ImageSpan,这就结束了吗?
我们发现大于两行的时候,末尾直接变成了…不展示了,这就得处理文本展示的问题了,最后一行必须得预留出图片和…的位置,然后剩下的空间进行字符串的截取操作。如下:

    private CharSequence getNewTextByConfig() {
        if (TextUtils.isEmpty(mOrigText)) {
            return mOrigText;
        }

        mLayout = getLayout();
        if (mLayout != null) {
            mLayoutWidth = mLayout.getWidth();
        }

        if (mLayoutWidth <= 0) {
            if (getWidth() == 0) {
                if (mFutureTextViewWidth == 0) {
                    return mOrigText;
                } else {
                    mLayoutWidth = mFutureTextViewWidth - getPaddingLeft() - getPaddingRight();
                }
            } else {
                mLayoutWidth = getWidth() - getPaddingLeft() - getPaddingRight();
            }
        }

        mTextPaint = getPaint();

        mTextLineCount = -1;
        mLayout = null;
        mLayout = new DynamicLayout(mOrigText, mTextPaint, mLayoutWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        mTextLineCount = mLayout.getLineCount();

//        if (mTextLineCount <= mMaxLinesOnShrink) {
//            return mOrigText;
//        }
        int maxLine = 0;
        if (mTextLineCount > 1) {
            maxLine = mMaxLinesOnShrink - 1;
        }else{
            SpannableStringBuilder ssbShrink = new SpannableStringBuilder(mOrigText);
            ssbShrink.append("+");
            ssbShrink.setSpan(imgSpan1, ssbShrink.length() - 1, ssbShrink.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            return ssbShrink;
        }
        int indexEnd = getValidLayout().getLineEnd(maxLine);
        int indexStart = getValidLayout().getLineStart(maxLine);
        int indexEndTrimmed = indexEnd
                - getLengthOfString(mEllipsisHint)
                - getLengthOfString(mGapToExpandHint);

        if (indexEndTrimmed <= indexStart) {
            indexEndTrimmed = indexEnd;
        }

        int remainWidth = getValidLayout().getWidth() -
                (int) (mTextPaint.measureText(mOrigText.subSequence(indexStart, indexEndTrimmed).toString()) + 0.5) - bitmap1.getWidth();
        float widthTailReplaced = mTextPaint.measureText(getContentOfString(mEllipsisHint)
                + getContentOfString(mGapToExpandHint));

        int indexEndTrimmedRevised = indexEndTrimmed;
        if (remainWidth > widthTailReplaced) {
            int extraOffset = 0;
            int extraWidth = 0;
            while (remainWidth > widthTailReplaced + extraWidth) {
                extraOffset++;
                if (indexEndTrimmed + extraOffset <= mOrigText.length()) {
                    extraWidth = (int) (mTextPaint.measureText(
                            mOrigText.subSequence(indexEndTrimmed, indexEndTrimmed + extraOffset).toString()) + 0.5);
                } else {
                    break;
                }
            }
            indexEndTrimmedRevised += extraOffset - 1;
        } else {
            int extraOffset = 0;
            int extraWidth = 0;
            while (remainWidth + extraWidth < widthTailReplaced) {
                extraOffset--;
                if (indexEndTrimmed + extraOffset > indexStart) {
                    extraWidth = (int) (mTextPaint.measureText(mOrigText.subSequence(indexEndTrimmed + extraOffset, indexEndTrimmed).toString()) + 0.5);
                } else {
                    break;
                }
            }
            indexEndTrimmedRevised += extraOffset;
        }

        String fixText = removeEndLineBreak(mOrigText.subSequence(0, indexEndTrimmedRevised));
        SpannableStringBuilder ssbShrink = new SpannableStringBuilder(fixText);
        if (remainWidth <= widthTailReplaced) {
            ssbShrink.append(mEllipsisHint);
        }

        if (issetSpecialColor) {
            int lenth = ssbShrink.length();
            if (specialColorLenth <= lenth) {
                lenth = specialColorLenth;
            }
            ssbShrink.setSpan(colorSpan, specialColorStart, lenth, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        ssbShrink.append(getContentOfString(mGapToExpandHint));

        ssbShrink.append("+");
        ssbShrink.setSpan(imgSpan1, ssbShrink.length() - 1, ssbShrink.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        return ssbShrink;
    }

代码中同时也处理了如下bug:
在这里插入图片描述
总结:这个UI需求重要的不是代码部分,而是看到这种需求,如果去想,怎么去处理,当然这种做法可能不是最优的,但是处理起来我觉得是最方便的,如果有更好的方案,欢迎评论区留言。

因为只有一个类,所以就放在csdn上了。
完整资源下载

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

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

相关文章

$19服务:DTCStatusMask和statusofDTC bit 定义

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;当ECU产生DTC时&#xff0c;我们只知道有故障发生了&#xff0c;并不清楚该故障什么时候发生&#xff0c;现在是否已经恢复、发生过几次&#xff0c;恢复过几次等信息&#xff0c;基于此ISO发布的14229-1使用DTC状态…

[附源码]SSM计算机毕业设计志愿者管理系统论文2022JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【VTK+有限元后处理】可视化结果云图

构建vtkUnstructuredGrid对象 为了读取不同格式的有限元计算结果文件&#xff0c;我们先写一个FEDataModel类来管理有限元的几何拓扑和属性信息。 class FEDataModel:"""有限元数据模型类"""def __init__(self):self.nodes [] # 节点几何坐标…

斐波那契数列和斐波那契数

一、什么是斐波那契数列 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;因数学家莱昂纳多斐波那契&#xff08;Leonardo Fibonacci&#xff09;以兔子繁殖为例子而引入&#xff0c;故又称为“兔子数列”&#xff0c;指的是这…

【考研复试】计算机专业考研复试英语常见问题三(个人选择/学业规划篇)

相关链接&#xff1a; 【考研复试】计算机专业考研复试英语常见问题一&#xff08;家庭/家乡/学校篇&#xff09;【考研复试】计算机专业考研复试英语常见问题二&#xff08;研究方向/前沿技术/本科毕设篇&#xff09;【考研复试】计算机专业考研复试英语常见问题三&#xff0…

C++---哈希

目录 1. unordered系列关联式容器 1.1 unordered_map 1.1.1 unordered_map的介绍 1.1.2 unordered_map的接口说明 1.2 unordered_set 2. 底层结构 2.1 哈希概念 2.2 哈希冲突 2.3 哈希函数 2.4 哈希冲突解决 2.4.1 闭散列 2.4.2 开散列 3. 封装unorder_map和unord…

MySQL增删改查进阶 — 表的设计

文章目录表的设计1.设计思路2.实体固定关系的套路2.1 一对一关系2.2 一对多关系2.3 多对多关系3.总结表的设计 表的设计实际上要做的工作就是明确一个程序里&#xff0c;需要使用几个数据库&#xff0c;几个表&#xff0c;表里都有哪些列。 1.设计思路 先明确实体再明确实体…

6. Design A Web Crawler

title: Notes of System Design No.10 — Design a Web Crawler description: ‘Design a Web Crawler’ date: 2022-05-13 18:01:58 tags: 系统设计 categories: 系统设计 00. What is Web Crawler? Q &#xff1a;uh just for now lets just do html pagesbut your web cr…

Explaining Deepfake Detection by Analysing Image Matching 翻译

点击查看对应的代码 摘要 本文旨在解释深度伪造检测模型在仅由二进制标签做有监督时如何学习图像的伪迹特征。为此&#xff0c;从图像匹配的角度提出如下三个假设。1、深度伪造检测模型表明基于视觉概念的真/假图片既不与源图片相关也不与目标图片相关而是与伪迹图片相关。2、…

全链路压测效能10倍提升的压测工具实践笔记

背景 创业型公司或创新型项目往往团队资源有限&#xff0c;人员能力水平有限&#xff0c;难以投入专业自动化压测人员&#xff1b; 同时部分业务&#xff08;tob/toc场景&#xff09;长期有中小型活动场景带来小规模流量并发&#xff0c;需要产研能长期保障并及时感知和解决网…

GitHub Star70K登顶,字节内部数据结构与算法笔记,限时上线

为什么学算法 不得不说&#xff0c;现在几乎所有的大厂&#xff0c;比如Google、字节、BAT&#xff0c;面试的时候都喜欢考算法、让人现场写代码&#xff0c;那你有没有真正地想过&#xff0c;为什么这些大公司都喜欢考算法呢&#xff1f; 经常有人说&#xff0c;程序员35岁之…

Java毕业设计MVC:基于SSM实现计算机硬件评测交流平台

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java、前端、Pythone开发多年&#xff0c;做过高程&#xff0c;项目经理&#xff0c;架构师 主要内容&#xff1a;Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路 关注作者有好处 项目编号&…

(杂)网易云歌单导入到apple music

喜欢apple music的简洁&#xff0c;就想着把网易云的歌单捣鼓进去。 获取歌单歌曲列表&#xff1a;https://yyrcd.com/n2s/ 转移歌单&#xff1a;https://soundiiz.com/zh/&#xff0c;首次使用需要注册&#xff0c;免费版只能一次导入200首。 平台选择apple music 登录授权即可…

Linux下 man命令的使用 及 中文man手册的安装

文章目录1. man命令使用2. 安装中文man手册1. man命令使用 man命令是Linux下最核心的命令之一。而man命令也并不是英文单词“man”的意思&#xff0c;它是单词manual的缩写&#xff0c;即使用手册的意思。man命令会列出一份完整的说明。其内容包括命令语法、各选项的意义及相关…

第三章 线性模型

3.1 基本形式 给定由d个属性描述的示例x(x1; x2; x3; … ; xd)。线性模型试图学得一个通过属性的线性组合来进行预测的函数&#xff0c;即 3.2 线性回归 线性回归试图学得一个线性模型尽可能准确地预测实值输出标记。 对于如何确定w和b&#xff0c;均方误差是回归任务中最常…

这样做时间轴,让你的PPT更出彩!

文章目录**▌方法一&#xff1a;美化时间节点****▌方法二&#xff1a;利用图片中的“轴”****▌方法三&#xff1a;时间轴不一定需要“轴”****▌方法四&#xff1a;把时间轴拆成数页****▌总结**已剪辑自: https://zhuanlan.zhihu.com/p/56672211 嗨&#xff0c;大家好&#…

【Linux】一万七千字详解 —— 基本指令(二)

文章目录前言man 指令cp 指令mv 指令echo 指令(含输出重定向)cat 指令(含输入重定向)wc 指令more 指令less 指令head 和 tail 指令(含管道用法)date 指令cal 指令sort 指令find 和 which 和 whereis 指令alias 指令grep 指令top 指令zip 和 unzip 指令结语前言 今天的主要内容…

C语言源代码系列-管理系统之学生选修课程系统

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过…

想归隐啦——与自然生活为伴

目录 一、陶渊明-桃花源记 二、梭罗-瓦尔登湖 结庐在人境&#xff0c;而无车马喧。问君何能尔&#xff1f;心远地自偏。采菊东篱下&#xff0c;悠然见南山 一、陶渊明-桃花源记 晋太元中&#xff0c;武陵人捕鱼为业。缘溪行&#xff0c;忘路之远近。忽逢桃花林&#xff0c;夹…

第五届“传智杯”全国大学生计算机大赛(练习赛)传智杯 #5 练习赛] 平等的交易

[传智杯 #5 练习赛] 平等的交易 题目描述 你有 nnn 件道具可以买&#xff0c;其中第 iii 件的价格为 aia_iai​。 你有 www 元钱。你仅能用钱购买其中的一件商道具。当然&#xff0c;你可以拿你手中的道具换取其他的道具&#xff0c;只是这些商道具的价值之和&#xff0c;不…