如何在 Android 中使用 Canvas.drawText 绘制跨度字符串

2024-02-17

我想画一个SpannedString to a Canvas.

SpannableString spannableString = new SpannableString("Hello World!");
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.YELLOW);
spannableString.setSpan(foregroundSpan, 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(backgroundSpan, 3, spannableString.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);

上面的例子是使用TextView,这又使用Layout用跨度绘制文本。我知道using a Layout是推荐的方式 https://stackoverflow.com/a/41870464/3681880将文本绘制到画布上。但是,我正在从头开始制作自己的文本布局,因此我需要自己实现。

做这样的事情是行不通的

canvas.drawText(spannableString, 0, spannableString.length(), 0, 0, mTextPaint);

because drawText只从中获取文本spannableString,不是任何跨度。绘图颜色由单独处理TextPaint.

我该如何使用canvas.drawText (or drawTextRun)绘制跨度信息(特别是这里的前景色和背景色)?

Related

  • 如何在 Android 中循环 SpannedString 或 SpannableString 中的跨度 https://stackoverflow.com/questions/43270575/how-to-loop-through-the-spans-in-a-spannedstring-or-spannablestring-in-android
  • 是否可以通过调用 Canvas.drawText() 一次来显示多色文本? https://stackoverflow.com/questions/10410515/is-it-possible-to-display-multi-color-text-with-one-call-to-canvas-drawtext

制定解决方案

我本来打算直接做自我回答,但事实证明这比我想象的要困难。所以我会先发布,然后在我能弄清楚的时候添加答案。 (我当然欢迎任何人先回答。)

以下是我迄今为止所拥有的作品:

  • 将每个跨度范围绘制为单独的文本运行 https://stackoverflow.com/a/43270576/3681880
  • Use drawTextRun https://developer.android.com/reference/android/graphics/Canvas.html#drawTextRun(java.lang.CharSequence,%20int,%20int,%20int,%20int,%20float,%20float,%20boolean,%20android.graphics.Paint)绘制文本 () (update:直到 API 23 才添加)
  • Use getRunAdvance https://developer.android.com/reference/android/graphics/Paint.html#getRunAdvance(char%5B%5D,%20int,%20int,%20int,%20int,%20boolean,%20int)测量从哪里开始下一个文本运行(update:直到API 23才添加,使用measureText反而)
  • 背景颜色可能需要单独绘制(使用drawRect或者可能drawPath? See here https://azzits.wordpress.com/tag/android-drawtext-with-background-color/, here https://stackoverflow.com/questions/8242439/how-to-draw-text-with-background-color-using-canvas, and here https://stackoverflow.com/questions/31877417/android-draw-text-with-solid-background-onto-canvas-to-be-used-as-a-bitmap.)
  • 源代码为TextView https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java, StaticLayout https://github.com/android/platform_frameworks_base/blob/master/core/java/android/text/StaticLayout.java, and TextLine https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/TextLine.java

对于大多数提出这个问题的人来说,您可能应该使用StaticLayout绘制跨区文本。看这个答案 https://stackoverflow.com/a/10410843/3681880寻求帮助。

但是,如果您确实需要自己绘制跨区文本,那么您将需要循环遍历所有跨度范围 https://stackoverflow.com/a/43270576/3681880并分别绘制每一个。您还需要测量每个跨度中文本的长度,以便知道从哪里开始绘制下一个跨度。

下面的代码处理BackgroundColorSpan and ForegroundColorSpan.

// set up the spanned string
SpannableString spannableString = new SpannableString("Hello World!");
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.YELLOW);
spannableString.setSpan(foregroundSpan, 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(backgroundSpan, 3, spannableString.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

// draw each span one at a time
int next;
float xStart = 0;
float xEnd;
for (int i = 0; i < spannableString.length(); i = next) {

    // find the next span transition
    next = spannableString.nextSpanTransition(i, spannableString.length(), CharacterStyle.class);

    // measure the length of the span
    xEnd = xStart + mTextPaint.measureText(spannableString, i, next);

    // draw the highlight (background color) first
    BackgroundColorSpan[] bgSpans = spannableString.getSpans(i, next, BackgroundColorSpan.class);
    if (bgSpans.length > 0) {
        mHighlightPaint.setColor(bgSpans[0].getBackgroundColor());
        canvas.drawRect(xStart, mTextPaint.getFontMetrics().top, xEnd, mTextPaint.getFontMetrics().bottom, mHighlightPaint);
    }

    // draw the text with an optional foreground color
    ForegroundColorSpan[] fgSpans = spannableString.getSpans(i, next, ForegroundColorSpan.class);
    if (fgSpans.length > 0) {
        int saveColor = mTextPaint.getColor();
        mTextPaint.setColor(fgSpans[0].getForegroundColor());
        canvas.drawText(spannableString, i, next, xStart, 0, mTextPaint);
        mTextPaint.setColor(saveColor);
    } else {
        canvas.drawText(spannableString, i, next, xStart, 0, mTextPaint);
    }

    xStart = xEnd;
}

下图中最上面的字符串是用上面的代码绘制的。底部的绳子是用常规的TextView(它使用StaticLayout).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Android 中使用 Canvas.drawText 绘制跨度字符串 的相关文章

  • 如何用Android做交互动画(翻译)

    我在 Android 中有一些 png 序列 我需要将它们的 x 和 y 位置从屏幕顶部到底部的翻译动画化 当动画发生时 我需要对象来接收单击事件 我知道这在 3 0 之前的 Android 版本中效果不太好 因为display对象的位置与
  • 无法获取项目的未知属性“assembleRelease”

    将 Android Studio 更新到版本 2 2 并将 gradle 插件更新到 2 2 0 后 出现以下错误 错误 32 1 评估项目 jobdispatcher 时出现问题 无法获取 org gradle api Project 类
  • 在javascript中计算精确的字符\字符串高度

    我正在使用画布 但我想不出任何解决方案 也无法在线找到问题的答案 我有一种字体 其中包含不同尺寸 高度和宽度 的符号 字符 我想从字体中绘制一些字符 符号 并在符号的顶部 向下绘制一些字符 问题是我无法找到一种方法来获得我正在绘制的字符的精
  • 需要对某些片段禁用 CollapsingToolbarLayout 的展开

    我有一个AppCompatActivity控制替换许多片段 这是我的布局 活动 main xml
  • 在 Android 市场中以编程方式检查我的应用程序版本

    目前 我正在启动时检查应用程序版本代码 并将其与我的服务器上的最新版本代码进行匹配 并根据此匹配 我发送用户从 Android 市场获取最新更新 它运行良好 但我的问题是我必须手动更改服务器上的最新版本代码 并且我不知道新版本何时发布APK
  • 突出显示列表视图项目

    我需要在触摸列表视图项目时突出显示它并保持突出显示状态 我尝试了我发现的一切 但没有任何效果 这是我的代码 这是列表视图
  • Android 音乐播放器应用程序:如何为服务中运行的媒体播放器设置完整的侦听器?

    我正在编写一个音乐播放器应用程序 我在服务中有 MediaPlayer 对象 问题是 我不知道如何从服务更新用户界面 例如 我想更新当前歌曲的剩余时间 但是 因为 MediaPlayer 正在服务 我无法设置 MediaPlayer 对象的
  • MediaPlayer.create() 始终返回 null

    我以前用过媒体播放器 从来没有遇到过这个问题 每当我尝试使用 MediaPlayer create 时 该方法都会给我 null 并且我无法播放声音 我有什么遗漏的吗 public class Game extends Activity p
  • 使用 START_STICKY 启动时服务进程被终止后的 onStartCommand

    我一直在阅读 Android 文档 我想知道是否有人可以阐明当以 START STICKY 启动的服务的进程被终止时服务实例会发生什么情况 我假设本地状态数据 实例变量 也丢失了 Android 在重新创建服务时是否会采取任何措施来帮助重新
  • 在 Android 中使用 AES 加密的最佳实践是什么?

    我为什么问这个问题 我知道人们对 AES 加密存在很多疑问 即使对于 Android 也是如此 如果您在网络上搜索 会发现很多代码片段 但在每个页面上 在每个 Stack Overflow 问题中 我都发现了另一个具有重大差异的实现 所以我
  • 如何从画布中删除路径区域(Android)

    我需要裁剪角落ImageView 不要将它们弄圆 而是擦除每个角上的三角形 似乎唯一的方法就是覆盖onDraw方法并使用从画布上删除这些区域Path 问题是我没有纯色背景 所以我需要擦除这些区域 但不要用某种颜色填充它们 我为此使用以下代码
  • Android - 使用 SAX 解析器解析大文件

    我正在尝试使用 SAX 解析器解析来自 webservice 的 xml 数据 当我尝试使用 URL 解析数据 大小 7 4MB 时 它工作正常 但是当我从 URL 复制 xml 数据并放置 xml 文件时 size 7 4MB 在raw文
  • 本地管理的广播接收器泄漏?

    当应用程序被系统杀死时 本地 即使用 LocalBroadcastManager 管理 BroadcastReceiver 是否有可能泄漏 我需要它的具体用例是我想在活动的 onCreate onDestroy 中注册 注销 Broadca
  • 如何在Room的数据库迁移中正确添加索引?

    我在迁移 Room 数据库时遇到问题 在更新的数据库中 我必须将一个字段从整数更改为双精度值 我读到它并不像听起来那么容易 为了做到这一点 我必须使用这个更改后的属性创建新的临时表 复制前一个表中的所有值 删除旧的值 最后重命名临时表 我的
  • 使用 twitter API 1.1 在 Android 应用程序中显示 twitter feed

    基本上我想展示这个提要 https en twitter com epl live https en twitter com epl live在我的应用程序中 由于 twitter 更改了其 api 1 1 每个调用都必须经过授权 我发现的
  • Android:从 PhoneGap 应用打开 Play 商店链接

    我想从我的phonegap 3 4 应用程序打开一个指向Google Play 商店的链接 呼唤market details id com google android apps maps导致 ActivityNotFoundExcepti
  • Android - 检测视图上的双击和三次点击

    我一直在尝试构建一个可以检测双敲击和三敲击的敲击检测器 在我的努力失败后 我在网上搜索了很长时间以找到可以使用的东西 但没有运气 奇怪的是 像这样的图书馆如此稀缺 有什么帮助吗 你可以尝试这样的事情 尽管我通常建议不要使用三次点击作为一种模
  • Android:选择 EditField 上焦点上的所有文本

    我试图让 Android 在获得焦点时选择 EditText 字段中的所有文本 我在布局中使用此属性 在两个字段上 android selectAllOnFocus true 我不确定这是否相关 但为了将光标移动到第一个可编辑字段 前面 还
  • Android SDK WebView调用Activity

    我试图在单击 WebView 组件内的链接时启动活动 我的Webview已加载到里面Main java我想启动SubActivity java当点击网站内的链接时Main java 另外 如何将参数传递给此活动 Example inspec
  • Android:我的应用程序太大并给出“无法执行 dex:方法 ID 不在 [0, 0xffff]: 65536”?

    我正在尝试将我的应用程序与 Box Dropbox 和 Google Drive 集成 所有这 3 项服务都需要许多第 3 方 jar 此外 我的应用程序已经需要一些第三方 jar 现在 当我尝试从 Eclipse 运行我的应用程序时 出现

随机推荐

  • 在 Win32 C++ 中创建 GUI

    我正在开发我的第一个 Windows 桌面应用程序 并且正在尝试找出创建该程序的 GUI 的最佳方法 我知道 我知道 考虑到有关该主题的数据量 我觉得问这个问题很愚蠢 然而 大多数答案似乎已经过时 我不确定它们是否适合我的特定项目 另外 W
  • 什么是测试中的软件故障? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我正在上软件测试的研究生课程 我们花了一整堂课来讨论故障 错误和故障之间的区别 我对测试中软件故障的定义不满意 你的定义是什么 您可能对此感兴趣
  • 何时使用 Vanilla JavaScript 与 jQuery?

    我注意到 在监视 尝试回答常见 jQuery 问题时 有一些使用 javascript 而不是 jQuery 的做法 实际上使您能够少写多做 同样的数量 并且还可能产生性能优势 一个具体的例子 this vs this 在引用被单击对象 i
  • Visual C++ 似乎对类的 POD 成员进行了零初始化,但它不应该这样做

    我有一堂这样的课 class TestClass public TestClass Note I wish not to initialize rawMemory for whatever reason int rawMemory 32 i
  • x86 上 Java 侵入性最小的编译屏障

    如果我有一个 Java 进程通过共享 ByteBuffer 或类似的方式与其他进程交互 那么 C C 中的编译器屏障的侵入性最小的等效项是什么 不需要可移植性 我对 x86 特别感兴趣 例如 我有 2 个进程根据伪代码读取和写入内存区域 p
  • 在 C# 中替换字符串中的所有特殊字符

    我想找到字符串中的所有特殊字符并用连字符 我正在使用下面的代码 string content foo bar regular expression replace 123 string pattern a zA Z regex patter
  • 将 PHP 字符串剪短

    你会如何把字符串剪短 这样它就不会进入 div 标签中的下一行例如我的message字符串包含以下内容 我们更喜欢可以回答的问题 而不仅仅是讨论 提供详细信息 写得清楚 简单 如果您的问题与此网站有关 请在元上提问 我想最好将其显示为 我们
  • 安装 hunspell 包

    我期待使用 pip 安装 hunspell 包 但它会抛出以下错误 Collecting hunspell Using cached hunspell 0 4 1 tar gz Building wheels for collected p
  • Ubuntu 18.04 服务器(Rails 6.0)上的“生产”环境缺少“secret_key_base”,尝试了多个主题

    这个话题有一个SOLUTION嵌入在最后 PROBLEM 我第一次在 Ubuntu 18 04 上的 VPS 上部署 Rails 应用程序 与 Nginx 一起 我遵循了很好的教程Gorails https www youtube com
  • 更改 HttpWebRequest 实例的 Uri?

    我有一个在事件中拦截的 HttpWebRequest 实例 我想在发送请求之前编辑 url 但我找不到执行此操作的方法 属性 RequestUri 是只读的 我想了几种方法 但似乎找不到可行的解决方案 使用反射来设置值 创建一个新请求 然后
  • Python - 在迭代字典列表时如何处理丢失的键? [复制]

    这个问题在这里已经有答案了 在下面的示例中 我迭代字典列表 并将 年龄 保存在列表中 然而 第二个字典没有键 年龄 在这种情况下 我希望将 null 值保存在列表中 关于如何实现这一目标有什么建议吗 my list age 0 name A
  • CHANGEM 的优雅矢量化版本(替代值) - MATLAB

    在Matlab 2012b中 有一个changem函数允许您用一组键指定的其他值替换矩阵的元素 替换数据数组中的值 http www mathworks com help map ref changem html jsessionid 7b
  • 来自另一个 bigDecimal.toString() 的 new BigDecimal 是否始终等于?

    在Java中 来自另一个bigDecimal toString 的new BigDecimal总是等于吗 例如 BigDecimal a new BigDecimal 1 23 BigDecimal b new BigDecimal a t
  • UIViewController 动画时方向错误

    为了使用 UISplitViewController 我在从一个视图控制器导航到另一个视图控制器时替换了窗口根控制器 为了在执行此操作时获得一些不错的过渡 我使用了如下的缩放效果 MyOtherViewController controll
  • SocketServer绑定多个服务器

    我正在尝试使用 python 的 SocketServer 模块绑定多个服务器 import SocketServer from SocketServer import BaseRequestHandler class HTTPSERVER
  • MYSQL子集操作

    有没有办法实现类似的目标 SELECT FROM tb values WHERE value1 value2 value3 SUBSET OF SELECT value FROM tb value WHERE isgoodvalue tru
  • 在 Android Studio 上运行的 HTC One M8

    有谁使用HTC One M8设备开启安卓工作室 我的问题是 IDE 无法识别它 我杀死了 AVD 重新启动了 IDE 几次 拔插了设备 但仍然无法识别 HTC One M8 与 Android Studio 配合使用的要求是 安装 HTC
  • 是否可以获得鼠标按钮 4、5 等?

    简而言之 有没有办法在 JavaScript 中检测额外的鼠标按钮按下情况 它没有与其余的鼠标输入一起记录 所以我猜它不在标准实现中 有没有什么方法 例如库 可以启用额外的鼠标按钮 是的 你可以这样做 检查一下鼠标事件 button htt
  • 使用属性文件的配置创建未知数量的 Bean

    我的情况是 我有一个属性文件来配置未知数量的 bean rssfeed source 0 http feed com rss news xml rssfeed title 0 Sample feed 1 rssfeed source 1 h
  • 如何在 Android 中使用 Canvas.drawText 绘制跨度字符串

    我想画一个SpannedString to a Canvas SpannableString spannableString new SpannableString Hello World ForegroundColorSpan foreg