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,一经查实,立即删除!

相关文章

YourPHPCMS checkEmail SQL注入漏洞复现

0x01 产品简介 Yourphp企业网站管理系统是一款完全开源免费的PHP+MYSQL系统。核心采用了ThinkPHP框架,同时也作为开源软件发布。 集众多开源项目于一身的特点,使本系统从安全,效率,易用及可扩展性上更加突出。程序内置

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;使得我…

Python+onlyoffice 实现在线word编辑

onlyoffice部署 version: "3" services:onlyoffice:image: onlyoffice/documentserver:7.5.1container_name: onlyofficerestart: alwaysenvironment:- JWT_ENABLEDfalse#- USE_UNAUTHORIZED_STORAGEtrue#- ONLYOFFICE_HTTPS_HSTS_ENABLEDfalseports:- "8080:8…

【北京迅为】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…

docker系统详解哟 以及相关命令 Centos Kali安装相关详解 Docker-Compose 亲测

目录 who Is Docker 概念 centos7 安装docker kali安装docker docker安装nginx Docker常用命令 容器得常用命令 Docker-Compose install 常用docker-compose命令 who Is Docker 软件的打包技术&#xff0c;就是将算乱的多个文件打包为一个整体&#xff0c;打包技术在没…

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

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

Windows 和 Linux 系统命令行操作详解:从文件管理到进程监控

1.切换盘符与目录操作 在命令行中&#xff0c;切换盘符和目录是最常见的操作。尽管 DOS 和 Linux 在这些操作上有所不同&#xff0c;但它们都能实现相似的功能。 (1)切换盘符 ①DOS命令&#xff1a;在 DOS 中&#xff0c;切换盘符非常简单&#xff0c;使用 盘符名:&#xff…

【数据库】关系代数和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不同只能建立…

echarts全屏,vue

echarts实现全屏并且不失真&#xff0c;全屏图片需要自己换 html&#xff1a; <!-- 图表全屏盒子 --> <div style"position: relative;" ref"charts_orders"><!-- 图表 --><div class"chart_box" v-show"sho…

杂谈随笔-关于unity开发游戏

最近有在做unity的游戏开发&#xff0c;都是自学&#xff0c;甚至没有完整的课程体系…… 在犹豫要不要出系列教程&#xff0c;帮助新手快速入门的同时算是巩固一下基础知识。 那这篇文章先谈谈我对于引擎开发游戏的一些小观点&#xff0c;算是做了这么十几个星期的微不足道的…

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

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

[光源控制] UI调节光源亮度参数失效

📢博客主页:https://loewen.blog.csdn.net📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢本文由 丶布布原创,首发于 CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览: 一. 前言二. 串口调试助手辅助排查接线问题二. …

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

文章目录 前言一、单例模式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; 如…

18 设计模式之迭代器模式(书籍遍历案例)

一、什么是迭代器模式 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;允许客户端通过统一的接口顺序访问一个集合对象中的元素&#xff0c;而无需暴露集合对象的内部实现。这个模式主要用于访问聚合对象&#xff08;如集合、数组等&…

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

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