Android textview展示富文本内容

image.png

今天实现的内容,就是上图的效果,通过Span方式展示图片,需要支持文字颜色改变、加粗。支持style=\"color:green; font-weight:bold;\"展示。尤其style标签中的font-sizefont-weight是在原生中不被支持的。

所以我们今天需要使用自定义的方式来实现实现。

val result = "<spanExt>攀钢钒钛所属行业为\n" +  
"<spanExt style=\"color:#333333; font-weight:bold; font-size:18px;\">其他采掘</spanExt>;\n" +  
"</spanExt>\n" +  
"<img src=\"https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png\">" +  
"<br/>\n" +  
"<spanExt>当日攀钢钒钛行情整体表现<spanExt style=\"color:green; font-weight:bold;\">弱于</spanExt>所属行业表现;</spanExt>\n" +  
"<br/>\n" +  
"<img src=\"https://hbimg.huaban.com/4829401262ba0574fe8328b9f7f4b871d53850df7cd4-wVjrLB_fw658\">" +  
"<spanExt>攀钢钒钛所属概念中<spanExt style=\"color:#333333; font-weight:bold; \">有色金属</spanExt>表现相对优异;</spanExt>\n" +  
"<br/>\n" +  
"<img src=\"https://bkimg.cdn.bcebos.com/pic/50da81cb39dbb6fd526675ca147cbc18972bd507999d?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2U5Mg==,g_7,xp_5,yp_5/format,f_auto\">" +  
"<spanExt>其涨跌幅在有色金属中位列<spanExt style=\"color:#F43737; font-weight:bold; \">81</spanExt>/<spanExt style=\"color:black; font-weight:bold; \">122</spanExt></spanExt>"

计划渲染的目标内容,这里的spanExt是由于系统本身是支持span标签,但是对于span标签的支持不够完善,我需要自定义一个新的标签,但是html解析的时候,是识别的标签,所以需要将展示内容的span标签替换为了spanExt。防止被系统的span标签直接解析了。

渲染内容中,主要是需要自定义span标签和图片的展示。接下来就从这两个方面出发说明。

自定义tag优化span标签

package org.fireking.basic.textview.html;import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Log;import org.xml.sax.XMLReader;import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class SpanExtTagHandler implements Html.TagHandler {private final String TAG = "CustomTagHandler";private int startIndex = 0;private int stopIndex = 0;private final ColorStateList mOriginColors;private final Context mContext;public SpanExtTagHandler(Context context, ColorStateList originColors) {mContext = context;mOriginColors = originColors;}@Overridepublic void handleTag(boolean opening, String tag, Editable output,XMLReader xmlReader) {processAttributes(xmlReader);if (tag.equalsIgnoreCase("spanExt")) {if (opening) {startSpan(tag, output, xmlReader);} else {endSpan(tag, output, xmlReader);attributes.clear();}}}public void startSpan(String tag, Editable output, XMLReader xmlReader) {startIndex = output.length();}public void endSpan(String tag, Editable output, XMLReader xmlReader) {stopIndex = output.length();String color = attributes.get("color");String size = attributes.get("size");String style = attributes.get("style");if (!TextUtils.isEmpty(style)) {analysisStyle(startIndex, stopIndex, output, style);}if (!TextUtils.isEmpty(size)) {size = size.split("px")[0];}if (!TextUtils.isEmpty(color)) {if (color.startsWith("@")) {Resources res = Resources.getSystem();String name = color.substring(1);int colorRes = res.getIdentifier(name, "color", "android");if (colorRes != 0) {output.setSpan(new ForegroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}} else {try {output.setSpan(new ForegroundColorSpan(Color.parseColor(color)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);} catch (Exception e) {e.printStackTrace();reductionFontColor(startIndex, stopIndex, output);}}}if (!TextUtils.isEmpty(size)) {int fontSizePx = 16;if (null != mContext) {fontSizePx = DisplayUtil.sp2px(mContext, Integer.parseInt(size));}output.setSpan(new AbsoluteSizeSpan(fontSizePx), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}}final HashMap<String, String> attributes = new HashMap<String, String>();private void processAttributes(final XMLReader xmlReader) {try {Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");elementField.setAccessible(true);Object element = elementField.get(xmlReader);Field attsField = element.getClass().getDeclaredField("theAtts");attsField.setAccessible(true);Object atts = attsField.get(element);Field dataField = atts.getClass().getDeclaredField("data");dataField.setAccessible(true);String[] data = (String[]) dataField.get(atts);Field lengthField = atts.getClass().getDeclaredField("length");lengthField.setAccessible(true);int len = (Integer) lengthField.get(atts);for (int i = 0; i < len; i++)attributes.put(data[i * 5 + 1], data[i * 5 + 4]);} catch (Exception e) {e.printStackTrace();}}/*** 还原为原来的颜色*/private void reductionFontColor(int startIndex, int stopIndex, Editable editable) {if (null != mOriginColors) {editable.setSpan(new TextAppearanceSpan(null, 0, 0, mOriginColors, null),startIndex, stopIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);} else {editable.setSpan(new ForegroundColorSpan(0xff2b2b2b), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}}/*** 解析style属性*/private void analysisStyle(int startIndex, int stopIndex, Editable editable, String style) {Log.e(TAG, "style:" + style);String[] attrArray = style.split(";");Map<String, String> attrMap = new HashMap<>();if (null != attrArray) {for (String attr : attrArray) {String[] keyValueArray = attr.split(":");if (null != keyValueArray && keyValueArray.length == 2) {// 记住要去除前后空格attrMap.put(keyValueArray[0].trim(), keyValueArray[1].trim());}}}Log.e(TAG, "attrMap:" + attrMap.toString());String color = attrMap.get("color");String fontSize = attrMap.get("font-size");String fontWeight = attrMap.get("font-weight");if (!TextUtils.isEmpty(fontWeight) && "bold".equalsIgnoreCase(fontWeight)) {editable.setSpan(new StyleSpan(Typeface.BOLD), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);} else if (!TextUtils.isEmpty(fontWeight) && "italic".equalsIgnoreCase(fontWeight)) {editable.setSpan(new StyleSpan(Typeface.ITALIC), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}if (!TextUtils.isEmpty(fontSize)) {fontSize = fontSize.split("px")[0];}if (!TextUtils.isEmpty(color)) {if (color.startsWith("@")) {Resources res = Resources.getSystem();String name = color.substring(1);int colorRes = res.getIdentifier(name, "color", "android");if (colorRes != 0) {editable.setSpan(new ForegroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}} else {try {editable.setSpan(new ForegroundColorSpan(Color.parseColor(color)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);} catch (Exception e) {e.printStackTrace();reductionFontColor(startIndex, stopIndex, editable);}}}if (!TextUtils.isEmpty(fontSize)) {int fontSizePx = 16;if (null != mContext) {fontSizePx = DisplayUtil.sp2px(mContext, Integer.parseInt(fontSize));}editable.setSpan(new AbsoluteSizeSpan(fontSizePx), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}}
}  

代码整体上非常简单,首先判断需要解析的标签。根据#public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader)的isOpen判断是开标签还是关标签。

> 开标签<span 
> 关标签</span>

对于开标签,不需要过多关注,只需要知道开始的位置即可。重点关注的还是关标签。

这里需要使用xmlReader解析出来对应的属性和文本内容,然后将内容转换为对应的span设置给Editable output即可。

自定义Html.ImageGetter支持加载网络图片

  • BitmapTarget.java

BitmapTarget用户装载Glide加载的图片对象

public class BitmapTarget extends SimpleTarget<Bitmap> {  private final DrawableWrapper drawableWrapper;  private Context context;  private TextView textView;  public BitmapTarget(TextView textView, DrawableWrapper drawableWrapper, Context context) {  this.drawableWrapper = drawableWrapper;  this.context = context;  this.textView = textView;  }  @Override  public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {  Drawable drawable = new BitmapDrawable(context.getResources(), resource);  int width = drawable.getIntrinsicWidth();  int height = drawable.getIntrinsicHeight();  drawable.setBounds(0, 0, width, height);  drawableWrapper.setBounds(0, 0, width, height);  drawableWrapper.setDrawable(drawable);  textView.setText(textView.getText());  textView.invalidate();  }  
}
  • DrawableWrapper.java

DrawableWrapper用来承接渲染到ImageSpandrawable

public class DrawableWrapper extends BitmapDrawable {  private Drawable drawable;  DrawableWrapper() {  }  @Override  public void draw(Canvas canvas) {  if (drawable != null){  drawable.draw(canvas);  }  }  public Drawable getDrawable() {  return drawable;  }  public void setDrawable(Drawable drawable) {  this.drawable = drawable;  }  
}
  • MyImageGetter.java

MyImageGetter 使用Glide进行图片的下载,并且渲染到drawable中,用于ImageSpan展示使用。

public class MyImageGetter implements Html.ImageGetter {  private Context context;  private TextView textView;  public MyImageGetter(Context context, TextView textView) {  this.context = context;  this.textView = textView;  }  @Override  public Drawable getDrawable(String source) {  DrawableWrapper drawableWrapper = new DrawableWrapper();  Drawable drawable = new BitmapDrawable(Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888));  drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());  drawableWrapper.setDrawable(drawable);  Glide.with(context).asBitmap().load(source).into(new BitmapTarget(textView, drawableWrapper, context));  return drawableWrapper;  }  
}

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

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

相关文章

病情聊天机器人,利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合

项目设计目的&#xff1a; 本项目旨在开发一个病情聊天机器人&#xff0c;利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合&#xff0c;实现对病情相关数据的存储、查询和自动回答。通过与用户的交互&#xff0c;机器人可以根据用户提供的症状描述&#xff0c;给出初步的可…

字母简化(UPC练习)

题目描述 给出一串全部为小写英文字母的字符串&#xff0c;要求把这串字母简化。简化规则是&#xff1a;统计连续出现的字母数&#xff0c;输出时先输出个数&#xff0c;再输出字母。比如&#xff1a;aaabbbaa&#xff0c;则简化为3a3b2a&#xff1b;而zzzzeeeeea&#xff0c;…

帆软报表中定时调度中的最后一步如何增加新的处理方式

在定时调度中,到调度执行完之后,我们可能想做一些别的事情,当自带的处理方式不满足时,可以自定义自己的处理方式。 产品的处理方式一共有如下这些类型: 我们想在除了上面的处理方式之外增加自己的处理方式应该怎么做呢? 先看下效果: 涉及到两方面的改造,前端与后端。…

前端算法之二叉树

二叉树 二叉树用于解决什么问题 数据的组织与搜索&#xff1a;排序&#xff1a;表达式和计算&#xff1a;图形处理&#xff1a; 举例&#xff1a;二叉树的最近公共祖先 思路&#xff1a; 排序/排布方式 和 &#xff08;排序中&#xff09;当前树和节点的关系 举例2&#xff1a;…

光照贴图的参数化

正如Jon在上一篇文章中所解释的那样&#xff0c;我们在《见证者》中使用了预先计算的全局照明&#xff0c;而我的首要任务之一就是开发该系统。 我开发了一些有趣的技术来计算自动参数化&#xff0c;以一种可以轻松映射到 GPU 的方式制定照明计算&#xff0c;并通过使用辐照度…

【如何选择Mysql服务器的CPU核数及内存大小】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

Codeium在IDEA里的3个坑

转载自Codeium在IDEA里的3个坑&#xff1a;无法log in&#xff0c;downloading language server和中文乱码_downloading codeium language server...-CSDN博客文章浏览阅读1.7w次&#xff0c;点赞26次&#xff0c;收藏47次。Codeium安装IDEA插件的3个常见坑_downloading codeiu…

4-2. AOP细节

1 AOP细节 1.1 切入点表达式 1.1.1 作用 通过表达式的方式定位一个或多个具体的连接点。 1.1.2 语法细节 1&#xff09;切入点表达式的语法格式 execution([权限修饰符] [返回值类型] [简单类名/全类名] 方法名) 2) 举例说明 3&#xff09;在AspectJ中&#xff0c;切入…

通过国家网络风险管理方法提供安全的网络环境

印度尼西亚通过讨论网络安全法草案启动了其战略举措。不过&#xff0c;政府和议会尚未就该法案的多项内容达成一致。另一方面&#xff0c;制定战略性、全面的网络安全方法的紧迫性从未像今天这样重要。 其政府官方网站遭受了多起网络攻击&#xff0c;引发了人们对国家网络安全…

小肥柴慢慢手写数据结构(C篇)(5-2 AVL树)

小肥柴慢慢学习数据结构笔记&#xff08;C篇&#xff09;&#xff08;5-2 AVL树 目录5-5 AVL出现的原因5-5-1 平衡树5-5-2 平衡二叉树的具体案例 5-6 AVL平衡策略的讨论5-7 不使用平衡因子的实现&#xff08;黑皮书&#xff0c;训练思维&#xff09;5-8 使用平衡因子的实现&…

普中STM32-PZ6806L开发板(USART2 串口 + HI-LINK-V20离线语音模块控制LED灯)

简介 买了HI-LINK-V20型号的离线语音识别模块, 为了后面可以做有意思的东西, 现在先来用用, 使用USART2 串口 接收来自我在HI-LINK-V20中预设的动作, 当识别到词条时发送对应的指令到串口, HI-LINK串口接的就是STM32F03ZET6的USART2, 且往下看。 电路原理图 连线图 连线引脚表…

Linux:/proc/sys/vm/目录各文件详解

目录 前言一、/proc/sys/vm/目录各文件二、相关功能的API函数 前言 /proc/sys/vm/ 目录是 Linux 系统中的一个特殊目录&#xff0c;它包含了与虚拟内存子系统相关的系统内核参数。这些参数可以用来配置系统的虚拟内存管理策略&#xff0c;包括内存分配、页面置换、内存压缩、NU…

数据结构【线性表篇】(四)

数据结构【线性表篇】(四&#xff09; 文章目录 数据结构【线性表篇】(四&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f; 目录一、栈(一)、栈的顺序存储(二)、栈的链式存储(三)、共享栈 二、队列(一)、队列的顺序存储(二)、队列的…

Nginx 不同源Https请求Http 报strict-origin-when-cross-origin

原因&#xff1a; nginx代理配置url指向只开放了/* 而我/*/*多了一层路径 成功&#xff1a;

2024新版塔罗占卜网站源码风水起名附带搭建视频及文本教程

附带文本教学及视频教程安装方法以linux为例&#xff1a; 1、建议在服务器上面安装宝塔面板&#xff0c;以便操作&#xff0c;高逼格技术员可以忽略这步操作。 2、把安装包文件解压到根目录&#xff0c;同时建立数据库&#xff0c;把数据文件导入数据库 3、修改核心文件conf…

python多环境管理工具——pyenv-win安装与使用教程

目录 pyenv-win简介 pyenv-win安装 配置环境变量 pyenv的基本命令 pyenv安装py环境 pyenv安装遇到问题 pycharm测试 pyenv-win简介 什么是pyenv-win&#xff1a; 是一个在windows系统上管理python版本的工具。它是pyenv的windows版本&#xff0c;旨在提供类似于unix/li…

cargo设置国内源 windows+linux

cargo默认的源比pip的源好多了&#xff0c;但是有时候速度还是很慢 一、部分国内源&#xff08;排名不分先后&#xff09; 这些源的格式用在具体的配置文件中 中国科学技术大学 [source.crates-io] replace-with ustc[source.ustc] registry "git://mirrors.ustc.ed…

Redis Cluster集群模式学习

Redis Cluster集群模式 Redis哨兵模式&#xff1a;https://blog.csdn.net/liwenyang1992/article/details/133956200 Redis Cluster集群模式示意图&#xff1a; Cluster模式是Redis3.0开始推出采用无中心结构&#xff0c;每个节点保存数据和整个集群状态&#xff0c;每个节点都…

[C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计

源码地址&#xff1a; github地址&#xff1a;https://github.com/Ahmednull/L2CS-Net L2CS-Net介绍&#xff1a; 眼睛注视&#xff08;eye gaze&#xff09; 是在各种应用中使用的基本线索之一。 它表示用户在人机交互和开放对话系统中的参与程度。此外&#xff0c;它还被用…

C/C++面向对象(OOP)编程-回调函数详解(回调函数、C/C++异步回调、函数指针)

本文主要介绍回调函数的使用&#xff0c;包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;C/C精进之路 &…