android 富文本及展示更多组件

在这里插入图片描述
模拟微博 #热贴 和 @用户 的这种 富文本形式组件,不说了, 直接上代码

package com.tongtong.feat_watch.viewimport android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.isVisible
import com.didi.drouter.annotation.Router
import com.google.android.material.chip.ChipGroup
import com.google.gson.Gson
import com.tongtong.feat_watch.R
import com.tongtong.feat_watch.databinding.ViewRichBinding
import com.tongtong.feat_watch.ui.topic.bean.ContentBean
import com.tongtong.feat_watch.ui.topic.bean.ContentType
import com.tongtong.lib_router.TTRouter
import com.tongtong.lib_util.ToastUtils
import org.json.JSONException
import org.json.JSONObject
import timber.log.Timber/*** @date: 2024/11/7* desc:* author: shuhuai* version:*/
class ContentView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) :FrameLayout(context, attrs, defStyleAttr) {//文本颜色private var textColor: Int//字体大小private var textSize: Float//折叠后显示的行数private val showLines: Intprivate var isBold: Booleanprivate var urltextColor: Int//是否可折叠private var expandEnable: Booleanprivate lateinit var binding: ViewRichBindingprivate var moreBack: (v: TextView?) -> Unit = {}init {val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView)textColor = typedArray.getColor(R.styleable.ExpandTextView_contentTextColor,context.resources.getColor(R.color.color_131314))urltextColor = typedArray.getColor(R.styleable.ExpandTextView_urlTextColor,context.resources.getColor(R.color.color_5D34D0))showLines = typedArray.getColor(R.styleable.ExpandTextView_showLines, Int.MAX_VALUE)textSize = typedArray.getDimension(R.styleable.ExpandTextView_contentTextSize, 16f)expandEnable = typedArray.getBoolean(R.styleable.ExpandTextView_expandEnable, false)isBold = typedArray.getBoolean(R.styleable.ExpandTextView_isBold, false)typedArray.recycle()initView()}private fun initView() {binding = ViewRichBinding.inflate(LayoutInflater.from(context), this, true)bindContent()}fun bindContent(s: String = "",array: List<ContentBean> = arrayListOf(),moreback: (view: TextView?) -> Unit = {}): ContentView {try {if (!::binding.isInitialized) {return this}this.moreBack = morebackif (s?.isEmpty() == true && array.isNullOrEmpty()) isVisible = truebuild(content(s, array))} catch (e: Exception) {Timber.e(e)}return this;}private fun content(str: String = "", array: List<ContentBean>): String {var s = ""if (array.isEmpty()) {s = "$str"} else {array.forEach {when (it.type()) {ContentType.TEXT -> s += "${it.content}"ContentType.TOPIC -> s += "<a href='{type:1,desc:\"话题详情\", id:${it.id}}'>#${it.content}</a>"ContentType.USER -> s += "<a href='{type:0, desc:\"用户\", id:${it.id}}'>@${it.content}</a>"}}}return s;}fun build(content: String? = "") {binding.content.setUrlColor(urltextColor).setUrlBlod(isBold).setText(content).setTextColor(textColor).setTextSize(textSize).setShowLines(showLines).setExpandEnable(expandEnable).setSpanClickable(true, object :CustomTextView.TextSpanClickListener {//设置有标签或@某人的点击, 默认为falseoverride fun onTextSpanClick(data: String?) {try {val jsonObject = JSONObject(data)val type = jsonObject.getInt("type")val id = jsonObject.getString("id")when (type) {0 -> {if (id.isNotEmpty()) {TTRouter.goOtherUser(id.toLong())}}1 -> {if (id.isNotEmpty()) {TTRouter.goTopicManagerN(id)}}}} catch (e: JSONException) {e.printStackTrace()}}}).setMore(moreBack).requestLayout()}fun setTextColor(textColor: Int): ContentView {binding.content.setTextColor(textColor)return this}fun setTextSize(textSize: Float): ContentView {binding.content.setTextSize(textSize)return this}fun setUrlColor(color: Int): ContentView {this.urltextColor = colorreturn this}fun setUrlBlod(bold: Boolean): ContentView {this.isBold = boldreturn this}}
package com.tongtong.feat_watch.viewimport android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.text.Html
import android.text.SpannableStringBuilder
import android.text.TextPaint
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.URLSpan
import android.text.util.Linkify
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.chip.ChipGroup
import com.tongtong.feat_watch.R/*** @author: shuhuai* @desc:* @date: 2024/12/4* @version:* @remark*/
class CustomTextView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) :ChipGroup(context, attrs, defStyleAttr) {//是否可折叠private var expandEnable = false//文本内容private var text: String? = null//文本颜色private var textColor: Intprivate var mtextColor: Intprivate var urltextColor: Int//字体大小private var textSize: Floatprivate var mtextSize: Float//折叠后显示的行数private var showLines: Int//内容文本private var mTextView: NonScrollingTextView? = nullprivate var moreTextView: NonScrollingTextView? = null//true, 表示拦截标签span点击事件//false, 表示普通文本private var spanClickable: Booleanprivate var isBold: Boolean//spanString点击的回调interface TextSpanClickListener {fun onTextSpanClick(data: String?)}private var mTextSpanClick: TextSpanClickListener? = nullprivate fun init(context: Context) {removeAllViews()chipSpacingVertical = 0chipSpacingHorizontal = 0val params =LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)layoutParams = params//内容显示文本mTextView = NonScrollingTextView(context)//        mTextView.setText(text);mTextView!!.textSize = textSizemTextView!!.setTextColor(textColor)mTextView!!.ellipsize = TextUtils.TruncateAt.ENDmTextView!!.layoutParams = paramsmTextView!!.maxLines = showLines//根据SpanClickable状态来设置文本setTextBySpanClickableStatus()mTextView!!.movementMethod = LinkMovementMethod.getInstance()addView(mTextView)moreTextView = NonScrollingTextView(context)moreTextView!!.setText("...展开");moreTextView!!.textSize = mtextSizemoreTextView!!.setTextColor(mtextColor)addView(moreTextView)}init {val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView)textColor = typedArray.getColor(R.styleable.ExpandTextView_contentTextColor,context.resources.getColor(R.color.color_131314))mtextColor = typedArray.getColor(R.styleable.ExpandTextView_moreTextColor,context.resources.getColor(R.color.extended_color))urltextColor = typedArray.getColor(R.styleable.ExpandTextView_urlTextColor,context.resources.getColor(R.color.color_5D34D0))textSize = typedArray.getDimension(R.styleable.ExpandTextView_contentTextSize, 16f)mtextSize = typedArray.getDimension(R.styleable.ExpandTextView_moreTextSize, 16f)spanClickable = typedArray.getBoolean(R.styleable.ExpandTextView_spanClickable, false)showLines = typedArray.getColor(R.styleable.ExpandTextView_showLines, Int.MAX_VALUE)expandEnable = typedArray.getBoolean(R.styleable.ExpandTextView_expandEnable, false)isBold = typedArray.getBoolean(R.styleable.ExpandTextView_isBold, false)typedArray.recycle()init(context)}@SuppressLint("RestrictedApi")override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)if (expandEnable) {moreTextView!!.visibility = if (mTextView!!.lineCount > showLines) VISIBLE else GONE}else {moreTextView!!.visibility = GONE}}/*** 将dip或dp值转换为px值,保证尺寸大小不变*/private fun dip2px(context: Context, dipValue: Float): Int {val scale = context.resources.displayMetrics.densityreturn (dipValue * scale + 0.5f).toInt()}fun setText(text: String?): CustomTextView {this.text = textsetTextBySpanClickableStatus()return this}fun setTextColor(textColor: Int): CustomTextView {this.textColor = textColormTextView!!.setTextColor(textColor)return this}fun setTextSize(textSize: Float): CustomTextView {this.textSize = textSizemTextView!!.textSize = textSizereturn this}fun setExpandEnable(able: Boolean): CustomTextView {this.expandEnable = ablereturn this}fun setShowLines(lines: Int): CustomTextView {this.showLines = linesmTextView?.maxLines = linesreturn this}fun setSpanClickable(spanClickable: Boolean,textSpanClick: TextSpanClickListener): CustomTextView {this.spanClickable = spanClickablemTextSpanClick = textSpanClicksetTextBySpanClickableStatus()return this}fun setUrlColor(color: Int): CustomTextView {this.urltextColor = colorreturn this}fun setUrlBlod(bold: Boolean): CustomTextView {this.isBold = boldreturn this}/*** 格式化超链接文本内容并设置点击处理*/private fun getClickableHtml(html: String?): CharSequence {val spannedHtml = Html.fromHtml(html)val clickableHtmlBuilder = SpannableStringBuilder(spannedHtml)val urls = clickableHtmlBuilder.getSpans(0, spannedHtml.length,URLSpan::class.java)for (span in urls) {setLinkClickable(clickableHtmlBuilder, span)}return clickableHtmlBuilder}/*** 设置点击超链接对应的处理内容*/private fun setLinkClickable(clickableHtmlBuilder: SpannableStringBuilder, urlSpan: URLSpan) {val start = clickableHtmlBuilder.getSpanStart(urlSpan)val end = clickableHtmlBuilder.getSpanEnd(urlSpan)val flags = clickableHtmlBuilder.getSpanFlags(urlSpan)clickableHtmlBuilder.setSpan(object : ClickableSpan() {override fun onClick(view: View) {if (mTextSpanClick != null) {//取出a标签的href携带的数据, 并回调到调用处//href的数据类型根据个人业务来定, demo是传的json字符串mTextSpanClick!!.onTextSpanClick(urlSpan.url)}}override fun updateDrawState(ds: TextPaint) {super.updateDrawState(ds)ds.linkColor = Color.TRANSPARENTds.color = urltextColords.isUnderlineText = falseds.isFakeBoldText = isBold}}, start, end, flags)}/*** 根据SpanClickable的状态来设置文本*/private fun setTextBySpanClickableStatus() {if (!TextUtils.isEmpty(text)) {if (spanClickable) {mTextView!!.autoLinkMask = Linkify.ALLmTextView!!.text = getClickableHtml(text)} else {mTextView!!.text = text}}}fun setMore(moreback: (view: TextView?) -> Unit = {}): CustomTextView {moreTextView?.setOnClickListener{moreback.invoke(moreTextView)}return this}}

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ExpandTextView"><!--内容文本颜色--><attr name="contentTextColor" format="reference|color"/><attr name="moreTextColor" format="reference|color"/><attr name="urlTextColor" format="reference|color"/><!--内容文本字体大小--><attr name="contentTextSize" format="dimension"/><attr name="moreTextSize" format="dimension"/><!--按钮文本颜色--><attr name="btnTextColor" format="reference|color"/><!--按钮折叠的描述--><attr name="btnExpandText" format="string"/><!--按钮展开的描述--><attr name="btnSpreadText" format="string"/><!--按钮字体大小--><attr name="btnTextSize" format="dimension"/><!--最大显示行数--><attr name="showLines" format="integer"/><!--是否显示箭头图标--><attr name="showIcon" format="boolean"/><!--箭头资源--><attr name="iconRes" format="reference"/><!--动画时长--><attr name="animationDuration" format="integer"/><!--@或者#话题#是否自定义点击事件--><attr name="spanClickable" format="boolean"/><!--是否可折叠--><attr name="expandEnable" format="boolean"/><attr name="isBold" format="boolean"/></declare-styleable>
</resources>

这个组件还有一点问题, 就是 富文本形式下,限制行数,不展示… 需要优化下,可以考虑下从html标签入手,或者直接自己画一个,这个就稍微麻烦点
后面有时间再优化吧

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

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

相关文章

gitlab 生成并设置 ssh key

一、介绍 &#x1f3af; 本文主要介绍 SSH Key 的生成方法&#xff0c;以及如何在GitLab上添加SSH Key。GitLab 使用SSH协议与Git 进行安全通信。当您使用 SSH密钥 对 GitLab远程服务器进行身份验证时&#xff0c;您不需要每次都提供您的用户名和密码。SSH使用两个密钥&#x…

保姆级教程Docker部署Nacos镜像

目录 1、创建挂载目录 2、拉取 Nacos 镜像 3、临时启动并复制文件 4、创建Nacos表结构 5、修改Nacos配置 6、正式启动 Nacos 7、登录Nacos 1、创建挂载目录 在宿主机上创建一个目录用于配置文件映射&#xff0c;这个目录将作为数据卷挂载到容器内部&#xff0c;使得我…

【北京迅为】iTOP-4412全能版使用手册-第六十七章 USB鼠标驱动详解

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

【银河麒麟操作系统真实案例分享】内存黑洞导致服务器卡死分析全过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 现象描述 机房显示器连接服务器后黑屏&#xff…

Java项目实战II基于微信小程序的旅游社交平台(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着移动互联网的迅猛发展&#xff0c;旅游已经成为人…

【数据库】关系代数和SQL语句

一 对于教学数据库的三个基本表 学生S(S#,SNAME,AGE,SEX) 学习SC(S#,C#,GRADE) 课程(C#,CNAME,TEACHER) &#xff08;1&#xff09;试用关系代数表达式和SQL语句表示&#xff1a;检索WANG同学不学的课程号 select C# from C where C# not in(select C# from SCwhere S# in…

IS-IS二

目录 ISIS建立邻接关系的基本条件&#xff1a; 1、接口链路类型一致 2、广播型链路上&#xff0c;接口类型一致 3、Hello包级别和类型一致 4、L1区域的ID要一致&#xff0c;L2的邻居区域ID不做要求 5、L1-2在区域ID相同下&#xff0c;即建立L1也建立L2区域ID不同只能建立…

️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南20241206

&#x1f6e0;️ 在 Windows WSL 上部署 Ollama 和大语言模型的完整指南 &#x1f4dd; 引言 随着大语言模型&#xff08;LLM&#xff09;和人工智能的飞速发展&#xff0c;越来越多的开发者尝试在本地环境中部署大模型进行实验。然而&#xff0c;由于资源需求高、网络限制多…

设计模式の单例工厂原型模式

文章目录 前言一、单例模式1.1、饿汉式静态常量单例1.2、饿汉式静态代码块单例1.3、懒汉式单例&#xff08;线程不安全&#xff09;1.4、懒汉式单例&#xff08;线程安全&#xff0c;同步代码块&#xff09;1.5、懒汉式单例&#xff08;线程不安全&#xff0c;同步代码块&#…

net.sf.jsqlparser.statement.select.SelectItem

今天一启动项目&#xff0c;出现了这个错误&#xff0c;仔细想了想&#xff0c;应该是昨天合并代码&#xff0c;导致的mybatis-plus版本冲突&#xff0c;以及分页PageHelper版本不兼容 可以看见这个我是最下边的Caused by 报错信息&#xff0c;这个地方提示我 net .sf.jsqlpar…

第427场周赛: 转换数组、用点构造面积最大的矩形 Ⅰ、长度可被 K 整除的子数组的最大元素和、用点构造面积最大的矩形 Ⅱ

Q1、转换数组 1、题目描述 给你一个整数数组 nums&#xff0c;它表示一个循环数组。请你遵循以下规则创建一个大小 相同 的新数组 result &#xff1a; 对于每个下标 i&#xff08;其中 0 < i < nums.length&#xff09;&#xff0c;独立执行以下操作&#xff1a; 如…

CV工程师专用键盘开源项目硬件分析

1、前言 作为一个电子发烧友&#xff0c;你是否有遇到过这样的问题呢。当我们去查看函数定义的时候&#xff0c;需要敲击鼠标右键之后选择go to definition。更高级一些&#xff0c;我们使用键盘的快捷键来查看定义&#xff0c;这时候可以想象一下&#xff0c;你左手按下ALT&a…

SpringBoot3配置文件

一、统一配置管理概述: SpringBoot工程下&#xff0c;进行统一的配置管理&#xff0c;你想设置的任何参数(端口号、项目根路径、数据库连接信息等等)都集中到一个固定位置和命名的配置文件(application.properties或application.yml)中 配置文件应该放置在Spring Boot工程的s…

【机器学习】任务十一:Keras 模块的使用

1.Keras简介 1.1 什么是Keras&#xff1f; Keras 是一个开源的深度学习框架&#xff0c;用 Python 编写&#xff0c;构建于 TensorFlow 之上。它以简单、快速和易于使用为主要设计目标&#xff0c;适合初学者和研究者。 Keras 提供了高层次的 API&#xff0c;帮助用户快速构…

【新品发布】ESP32-P4开发板 —— 启明智显匠心之作,为物联网及HMI产品注入强劲动力

核心亮点&#xff1a; ESP32-P4开发板&#xff0c;是启明智显精心打造的一款高性能物联网开发板。它专为物联网项目及HMI&#xff08;人机界面&#xff09;产品而设计&#xff0c;旨在为您提供卓越的性能和稳定可靠的运行体验。 强大硬件配置&#xff1a; 双核400MHz RISC-V处…

在Ubuntu22.04.5上安装Docker-CE

文章目录 1. 查看Ubuntu版本2. 安装Docker-CE2.1 安装必要的系统工具2.2 信任Docker的GPG公钥2.3 写入软件源信息2.4 安装Docker相关组件2.5 安装指定版本Docker-CE2.5.1 查找Docker-CE的版本2.5.2 安装指定版本Docker-CE 3. 启动与使用Docker3.1 启动Docker服务3.2 查看Docker…

SSM01-MyBatis框架(一文学会MyBatis)

Mybatis框架 一、Mybatis框架简介 1.1 传统JDBC的缺陷 &#xff08;1&#xff09;数据库连接创建、释放频繁会造成系统资源浪费 【MyBatis通过在核心配置文件中配置数据路连接池解决此问题】 &#xff08;2&#xff09; SQL语句在代码中硬编码(PreparedStatement向占位符传…

uniapp中导入uview或者uview plus

关于SCSS uview-plus依赖SCSS&#xff0c;您必须要安装此插件&#xff0c;否则无法正常运行。 如果您的项目是由HBuilder X创建的&#xff0c;相信已经安装scss插件&#xff0c;如果没有&#xff0c;请在HX菜单的 工具->插件安装中找到"scss/sass编译"插件进行安…

深度解析 Ansible:核心组件、配置、Playbook 全流程与 YAML 奥秘(上)

文章目录 一、ansible的主要组成部分二、安装三、相关文件四、ansible配置文件五、ansible 系列 一、ansible的主要组成部分 ansible playbook&#xff1a;任务剧本&#xff08;任务集&#xff09;&#xff0c;编排定义ansible任务集的配置文件&#xff0c;由ansible顺序依次执…

【CC2530开发基础篇】光敏和热敏传感器

一、前言 1.1 开发背景 本实验通过CC2530单片机接入光敏传感器和热敏传感器&#xff0c;进行数据采集与检测&#xff0c;并将检测结果通过串口终端输出。光敏传感器和热敏传感器是常见的环境感知设备&#xff0c;分别用于测量光强和温度。在实际应用中&#xff0c;这些传感器…