Android实现超出固定行数折叠文字“查看全文“、“收起全文“

先上效果图

在这里插入图片描述

分析问题

网上有很多关于这个的代码,实现都过于复杂了,github上甚至还看到一篇文章600多行代码,结果一跑起来全是bug。还是自己写吧!!!
如果我们需要换行的"查看全文"、"收起全文"效果那没什么号说的,因为可以直接用两个TextView然后通过判断超过行数还是没有超过行数来判断显示还是隐藏即可。这没有什么难度,这里我们需要实现同一TextView实现。
要在 TextView 的部分文字上添加颜色和点击事件,您可以使用 SpannableStringClickableSpan 来实现。
为了避免重复代码,那我们肯定是自定义View实现,新建一个ScalingTextView,继承AppCompatTextView

class ScalingTextView(context: Context, attrs: AttributeSet?) :AppCompatTextView(context, attrs) {}

然后我们需要几个参数

<!--一段测试文字-->
<string name="scaling_str">这是一段ScalingTextView的折叠测试文字,测试多行显示的时候是否可以”查看全文“、”收起全文“这个功能是否正常呢,但是这个问题必须要超过两行才行,因此我现在每打的一个字都是在凑字数,你懂了吧!!!</string>

SpannableStringClickableSpan 对象

    private var spannableString: SpannableString? = null// 创建 ClickableSpan 对象val clickableSpan = object : ClickableSpan() {override fun onClick(widget: View) {// 在这里处理点击事件toggleText()}override fun updateDrawState(ds: TextPaint) {// 设置点击文字的颜色ds.color = Color.BLUE// 如果不希望点击文字有下划线,可以注释下面这行代码ds.isUnderlineText = true}}
    fun toggleText() {if (isCollapsed) {// 展开文本maxLines = Integer.MAX_VALUEisCollapsed = false} else {// 折叠文本maxLines = maxLinesCollapsedisCollapsed = true}}

当然还有些其他便于设置的参数,例如:

    private var maxLinesCollapsed: Int = 2//默认折叠行数private var isCollapsed: Boolean = falseprivate var mOriginText: String //文本内容private @ColorInt var mOriginTextColor: Int//折叠文字颜色private val DEFAULT_OPEN_SUFFIX = "查看全文"private val DEFAULT_CLOSE_SUFFIX = "收起全文"private val ellipsis = "..."

当然这些参数我们需要通过xml里直接配置,不用每次都set一堆方法对吧,所以添加自定义属性

    <declare-styleable name="scaling_text_view"><attr name="content_text" format="string"></attr><attr name="content_text_color" format="color"></attr></declare-styleable>

然后获取这几个自定义参数,大家可以自行增加,这里只为演示内容

    init {val typedValue = context.obtainStyledAttributes(attrs, R.styleable.scaling_text_view)mOriginText = typedValue.getString(R.styleable.scaling_text_view_content_text).toString()mOriginTextColor = typedValue.getColor(R.styleable.scaling_text_view_content_text_color,ContextCompat.getColor(context,R.color.themeColor)).toInt()}

最后我们如何实现功能呢?我们可以从几个方向去分析:

  • 在文字结尾追加上“...”省略号和 "查看全文""收起全文",这个不难
  • 当超出最大限制行数的时候我们需要截取掉多余内容,并且为“...”省略号和 "查看全文""收起全文"空出位置
  • "查看全文""收起全文"添加颜色
  • 最后为 "查看全文""收起全文"添加点击事件
  • 最后的最后刷新文本内容

那么我们可以重写onMeasure
这里我们用到一个方法getLineEnd

            val lineEndIndex = layout.getLineEnd(maxLinesCollapsed - 1)val newText = text.subSequence(0, lineEndIndex - ellipsis.length + 1 - DEFAULT_OPEN_SUFFIX.length + 1).toString().trim { it <= ' ' } + ellipsis + DEFAULT_OPEN_SUFFIX

创建SpannableString对象

spannableString = SpannableString(newText);
 //设置点击事件spannableString?.setSpan(clickableSpan,newText.lastIndexOf(DEFAULT_OPEN_SUFFIX),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX) + DEFAULT_OPEN_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)//设置文本颜色spannableString?.setSpan(ForegroundColorSpan(mOriginTextColor),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX) + DEFAULT_OPEN_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

最后设置刷新文本

            text = spannableStringmovementMethod = LinkMovementMethod.getInstance()super.onMeasure(widthMeasureSpec, heightMeasureSpec)

好了,我们搞定了,完整代码

class ScalingTextView(context: Context, attrs: AttributeSet?) :AppCompatTextView(context, attrs) {private var maxLinesCollapsed: Int = 2private var isCollapsed: Boolean = falseprivate val TAG: String = ScalingTextView::class.java.simpleNameprivate var mOriginText: Stringprivate @ColorInt var mOriginTextColor: Intprivate val DEFAULT_OPEN_SUFFIX = "查看全文"private val DEFAULT_CLOSE_SUFFIX = "收起全文"private val ellipsis = "..."private var spannableString: SpannableString? = nullinit {val typedValue = context.obtainStyledAttributes(attrs, R.styleable.scaling_text_view)mOriginText = typedValue.getString(R.styleable.scaling_text_view_content_text).toString()mOriginTextColor = typedValue.getColor(R.styleable.scaling_text_view_content_text_color,ContextCompat.getColor(context,R.color.themeColor)).toInt()text = mOriginText}// 创建 ClickableSpan 对象val clickableSpan = object : ClickableSpan() {override fun onClick(widget: View) {// 在这里处理点击事件toggleText()}override fun updateDrawState(ds: TextPaint) {// 设置点击文字的颜色ds.color = Color.BLUE// 如果不希望点击文字有下划线,可以注释下面这行代码ds.isUnderlineText = true}}fun toggleText() {if (isCollapsed) {// 展开文本maxLines = Integer.MAX_VALUEisCollapsed = false} else {// 折叠文本maxLines = maxLinesCollapsedisCollapsed = true}}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)if (layout.lineCount <= maxLinesCollapsed && spannableString == null) {//原文本等于或者小于默认折叠行数的时候不追加点击事件等return}if (layout != null && layout.lineCount > maxLinesCollapsed && isCollapsed) {val lineEndIndex = layout.getLineEnd(maxLinesCollapsed - 1)val newText = text.subSequence(0, lineEndIndex - ellipsis.length + 1 - DEFAULT_OPEN_SUFFIX.length + 1).toString().trim { it <= ' ' } + ellipsis + DEFAULT_OPEN_SUFFIXspannableString = SpannableString(newText);//设置点击事件spannableString?.setSpan(clickableSpan,newText.lastIndexOf(DEFAULT_OPEN_SUFFIX),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX) + DEFAULT_OPEN_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)//设置文本颜色spannableString?.setSpan(ForegroundColorSpan(mOriginTextColor),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX),newText.lastIndexOf(DEFAULT_OPEN_SUFFIX) + DEFAULT_OPEN_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)text = spannableStringmovementMethod = LinkMovementMethod.getInstance()super.onMeasure(widthMeasureSpec, heightMeasureSpec)}else if (layout != null && !isCollapsed) {val newText = mOriginText + DEFAULT_CLOSE_SUFFIXspannableString = SpannableString(newText);//设置点击事件spannableString?.setSpan(clickableSpan,newText.lastIndexOf(DEFAULT_CLOSE_SUFFIX),newText.lastIndexOf(DEFAULT_CLOSE_SUFFIX) + DEFAULT_CLOSE_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)//设置文本颜色spannableString?.setSpan(ForegroundColorSpan(mOriginTextColor),newText.lastIndexOf(DEFAULT_CLOSE_SUFFIX),newText.lastIndexOf(DEFAULT_CLOSE_SUFFIX) + DEFAULT_CLOSE_SUFFIX.length,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)text = spannableStringmovementMethod = LinkMovementMethod.getInstance()super.onMeasure(widthMeasureSpec, heightMeasureSpec)}}}

xml里使用,这里默认是展开的,你们默认隐藏的话自己实现

        <com.github.demo.wight.ScalingTextViewandroid:id="@+id/scalingTextView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="12dp"app:content_text="@string/scaling_str"app:content_text_color="@color/themeColor"" />

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.14 作业 ARM

.text .globl _gcd_gcd:mov r0,#9mov r1,#15cmp r0,r1 比较r0和r1寄存器中的值beq stopsubhi r0,r0,r1subcc r1,r1,r0stop:b stop .end用for循环实现1~100之间和&#xff1a; .text .globl _start_start:mov r0,#0 总和mov r1,#1 从1开始mov r2,#100 到100结束bl add_loopa…

安装elasticsearch

一、docker安装elasticsearch 1、下载镜像 docker pull elasticsearch:6.5.4 2、启动容器 docker run -p 9200:9200 -p 9300:9300 --name elasticsearch \ -e "discovery.typesingle-node" \ -e "cluster.nameelasticsearch" \ -e "ES_JAVA_OPTS-Xm…

软件测试基础篇——Docker

1、docker技术概述 docker描述&#xff1a;docker是一项虚拟化的容器技术&#xff08;类似于虚拟机&#xff09;&#xff0c;docker技术给使用者提供一个平台&#xff0c;在该平台上可以利用提供的容器&#xff0c;对每一个应用程序进行单独的封装隔离&#xff0c;每一个应用程…

IC人必看| 模拟IC方向面试常考问题及答案汇总(二)

有不少小伙伴说还想要更多模拟IC方向的面试题目&#xff0c;这不就来了&#xff01;&#xff08;文末可领全部面试题目&#xff09; 1. Bandgap 里有几种反馈&#xff1f;原理是&#xff1f; 正反馈和负反馈。 2. 负反馈种类&#xff1f;负反馈的优点&#xff1f; 种类&am…

【深度学习】【风格迁移】Zero-shot Image-to-Image Translation

论文&#xff1a;https://arxiv.org/abs/2302.03027 代码&#xff1a;https://github.com/pix2pixzero/pix2pix-zero/tree/main 文章目录 Abstract1. Introduction相关工作3. Method Abstract 大规模文本到图像生成模型展示了它们合成多样且高质量图像的显著能力。然而&#x…

代码质量检查工具SonarQube

Devops流水线之SonarQube 文章目录 Devops流水线之SonarQube1. 软件功能介绍及用途2. 软件环境搭建与使用2.1 使用方法2.2 SonarQube相关属性说明2.3 Sonar配置文件内容说明 3. 使用环节4. 检查方法 1. 软件功能介绍及用途 SonarQube是一个用于代码质量管理的开源平台&#xf…

网络安全进阶学习第十五课——Oracle SQL注入

文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异&#xff1a;三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…

项目知识点记录

1.使用druid连接池 使用properties配置文件&#xff1a; driverClassName com.mysql.cj.jdbc.Driver url jdbc:mysql://localhost:3306/book?useSSLtrue&setUnicodetrue&charsetEncodingUTF-8&serverTimezoneGMT%2B8 username root password 123456 #初始化链接数…

Syncfusion Essential Edit for WPF Crack

Syncfusion Essential Edit for WPF Crack 在任何WPF应用程序中启用语法高亮显示。 Syncfusion Essential Edit for WPF是一款具有所有基本功能的编辑器&#xff0c;如文本编辑、剪切、复制和粘贴。它允许用户从各种文件格式打开文件并将其保存为各种文件格式。Syncfusion Esse…

Streamlit项目: 轻松搭建部署个人博客网站

文章目录 1 前言1.1 探索 Streamlit&#xff1a;轻松创建交互式应用1.2 最全 Streamlit 教程专栏 2 我的个人博客网站已上线&#xff01;2.1 一个集成了智能中医舌诊-中e诊专栏的博客网站2.2 前期准备2.3 使用 Streamlit Cloud 运行 3 知识点讲解3.1 实现多页面&#xff1a;两种…

黑马项目一阶段面试 项目介绍篇

我完成了一个外卖项目&#xff0c;名叫苍穹外卖&#xff0c;是跟着黑马程序员的课程来自己动手写的。 项目基本实现了外卖客户端、商家端的后端完整业务。 商家端分为员工管理、文件上传、菜品管理、分类管理、套餐管理、店铺营业状态、订单下单派送等的管理、数据统计等&…

chatGPT应用于房地产行业

作为 2023 年的房地产专业人士&#xff0c;您无疑认识到技术对行业的重大影响。近年来&#xff0c;一项技术进步席卷了世界——人工智能。人工智能彻底改变了房地产业务的各个方面&#xff0c;从简化管理任务到增强客户互动。 在本文中&#xff0c;我们将探讨几种巧妙的人工智…

命令提示符之操作基础(Windows)

打开命令提示符 方法一 打开指定文件的文件夹&#xff0c;在路径栏里输入“cmd”&#xff0c;回车&#xff0c;就进入控制台了。默认路径就是指定文件夹的路径。 方法二 打开指定的文件夹&#xff0c;按住shift键&#xff0c;在空白处右击&#xff0c;在菜单栏中选择“在此处打…

力扣 322. 零钱兑换

题目来源&#xff1a;https://leetcode.cn/problems/coin-change/description/ C题解&#xff08;来源代码随想录&#xff09;&#xff1a;题目中说每种硬币的数量是无限的&#xff0c;可以看出是典型的完全背包问题。动规五部曲分析如下&#xff1a; 确定dp数组以及下标的含义…

Synopsys EDA数字设计与仿真

参考如下文章安装Synopsys EDA开发工具 https://blog.csdn.net/tugouxp/article/details/132255002?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132255002%22%2C%22source%22%3A%22tugouxp%22%7D Synopsys EDA工具的结构 下…

面试总结-webpack/git

说说你对webpack的理解 webpack 是一个静态模块打包器&#xff0c;整个打包过程就像是一条生产线&#xff0c;把资源从入口放进去&#xff0c;经过一系列的加工&#xff08;loader&#xff09;&#xff0c;最终转换成我们想要的结果&#xff0c;整个加工过程还会有监控&#x…

接口测试和功能测试的区别

接口测试和功能测试的区别&#xff1a; 2023最新Jmeter接口测试从入门到精通&#xff08;全套项目实战教程&#xff09; 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者…

利用SimpleDateFormat或者LocalDateTime生成格式为“yyyy-MM-dd HH:mm:ss“的当前时间

java程序&#xff1a; // 利用LocalDateTime生成格式为"yyyy-MM-dd HH:mm:ss"的当前时间 DateTimeFormatter formatter DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime now LocalDateTime.now(); String time1 now.format(format…

(7)(7.4) 集结航点

文章目录 7.4.1 概述 7.4.2 设置集结航点 7.4.3 飞行示例 7.4.4 附录 7.4.1 概述 通常情况下&#xff0c;当固定翼或旋翼飞机进入"返回发射"(Return to Launch (RTL))模式&#xff08;通常由自动驾驶仪失控保护触发&#xff09;(failsafe)时&#xff0c;默认行为…

Spring Task入门案例

Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 定位&#xff1a;定时任务框架 作用&#xff1a;定时自动执行某段Java代码 强调&#xff1a;只要是需要定时处理的场景都可以使用Spring Task 1. cron表达式 cron表达式…