多行“ReplacementSpan”绘图问题

2024-01-09

只要文本不太长,我的自定义替换跨度就可以工作,但一旦文本长于一行,跨度绘图就会完全崩溃。我的理解是draw()在这种情况下被调用两次导致跨度绘制两次。无法区分第二个绘制调用和第一个绘制调用,从而使您可以控制绘制内容和绘制位置。start and end因为他们报告了错误的价值观,所以变得毫无用处。

Is ReplacementSpan甚至应该适用于多行文本?对于解决此问题的任何帮助,我将不胜感激。

这就是当我将所选文本更改为我的文本时发生的情况CustomReplacementSpan:

自定义替换跨度.kt

import android.graphics.Canvas
import android.graphics.Paint
import android.os.Build
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.text.TextUtils
import android.text.style.ReplacementSpan
import androidx.core.graphics.withTranslation

class CustomReplacementSpan(val spanText: String, val color: Int) : ReplacementSpan() {

    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        return paint.measureText(spanText).toInt()
    }

    override fun draw(
        canvas: Canvas,
        text: CharSequence?,
        start: Int,
        end: Int,
        x: Float,
        top: Int,
        y: Int,
        bottom: Int,
        paint: Paint
    ) {
        paint.color = color

        canvas.drawMultilineText(
            text = spanText,
            textPaint = paint as TextPaint,
            width = canvas.width,
            x = x,
            y = top.toFloat()
        )
    }


}

fun Canvas.drawMultilineText(
    text: CharSequence,
    textPaint: TextPaint,
    width: Int,
    x: Float,
    y: Float,
    start: Int = 0,
    end: Int = text.length,
    alignment: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL,
    spacingMult: Float = 1f,
    spacingAdd: Float = 0f,
    includePad: Boolean = true,
    ellipsizedWidth: Int = width,
    ellipsize: TextUtils.TruncateAt? = null
) {
    val staticLayout =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            StaticLayout.Builder.obtain(text, start, end, textPaint, width)
                .setAlignment(alignment)
                .setLineSpacing(spacingAdd, spacingMult)
                .setIncludePad(includePad)
                .setEllipsizedWidth(ellipsizedWidth)
                .setEllipsize(ellipsize)
                .build()
        } else {
            StaticLayout(
                text, start, end, textPaint, width, alignment,
                spacingMult, spacingAdd, includePad, ellipsize, ellipsizedWidth
            )
        }

    staticLayout.draw(this, x, y)
}

private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) {
    canvas.withTranslation(x, y) {
        draw(this)
    }
}

MainActivity.kt

import android.os.Bundle
import android.text.Spannable
import android.view.View
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun applySpan(view: View) {
        val editText = findViewById<EditText>(R.id.edit)
        if (editText.selectionStart < 0 || editText.selectionEnd < 0) {
            return
        }
        val fullText = editText.text
        val text = fullText.subSequence(editText.selectionStart, editText.selectionEnd)
        val span = CustomReplacementSpan(text.toString(), ContextCompat.getColor(this, android.R.color.holo_blue_dark))
        editText.text.setSpan(span, editText.selectionStart, editText.selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
    }
}

活动主文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edit"
        style="@style/Widget.AppCompat.EditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="applySpan"
        android:text="Make it span" />

</LinearLayout>

显然,流向新的线路是ReplacementSpan不会。这是一篇文章的引用在文本上绘制圆角背景 https://medium.com/androiddevelopers/drawing-a-rounded-corner-background-on-text-5a610a95af5作者:Florina Muntenescu,她在博客中介绍了跨度等内容。 (以下引用中的强调是我的。)

我们需要将可绘制对象与文本一起绘制。我们可以实现自定义 ReplacementSpan 来自己绘制背景和文本。但是 ReplacementSpans 不能流入下一行,因此我们将无法支持多行背景。它们宁愿看起来像 Chip(Material Design 组件),其中每个元素都必须位于一行上。

这就是您遇到的问题。本文继续讨论您可能想要研究的可能解决方案。例如,可以使用本文中概述的一些技术来定义多个ReplacementSpans取决于换行符,就像对背景可绘制对象所做的那样。

还有其他跨度类型可能更适合您的目的。Here https://developer.android.com/reference/android/text/style/package-summary是其中的列表。

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

多行“ReplacementSpan”绘图问题 的相关文章

  • 检测到设备正在振动?

    我使用下面的代码来振动设备 public void vibrator try Vibrator vibrator Vibrator getSystemService Context VIBRATOR SERVICE vibrator vib
  • Android第一次动画不流畅

    我正在尝试一个动画将 imageView 从屏幕底部滑动到屏幕中心 但是当我第一次执行此动画时 它不平滑 但当第二次执行动画时 它是正常且平滑的 我几乎尝试了所有方法 但无法解决我的问题 这是我的动画文件
  • 为什么将函数参数声明为最终的?

    我目前正在阅读 Sams 出版的 24 小时自学 Android 应用程序开发 一书 我对 Java Android 或其他方面还比较陌生 我对 ActionScript 3 有非常扎实的背景 它与 Java 有足够的相似之处 因此该语言本
  • 在自定义对象中创建时粘性服务不会重新启动

    我有一个具有绑定服务的单例对象 我希望它重新启动 当我从启动器启动应用程序时 单例对象将初始化并绑定到这个现有的服务实例 以下是在单例中创建和绑定服务的代码 public class MyState private static MySta
  • 共同的偏好不断消失

    我正在使用共享首选项来存储我的应用程序的登录凭据 除了一个用户之外 一切正常 一段时间后 共享偏好似乎会以某种方式重置或清除 我已针对该用户调整了我的应用程序 使其不再清除他的共享偏好设置 这样我就可以确定这不是我的应用程序的错 但即使在这
  • Android libgdx 首选项丢失

    我在 Libgdx 引擎中创建了 Android 游戏 一段时间后 我注意到在某些应用程序杀手中杀死该应用程序后 或者如果我在 Android 设置中执行 强制关闭 操作 我保存到首选项中的游戏选项就会丢失 有办法防止这种情况吗 我从来没有
  • (Ionic 2)尝试回退到 Cordova-lib 执行时发生错误:TypeError:无法读取未定义的属性“then”

    Edit 使用 ionic 2 时会发生这种情况 我知道它还不稳定 但我认为可能有一些解决方案 因为其他人似乎没有遇到这个问题 Edit end 由于某种原因 我在尝试使用 ionic build android 和 ionic build
  • Android Studio 在编译时未检测到支持库

    由于 Android Studio 将成为 Android 开发的默认 IDE 因此我决定将现有项目迁移到 Android studio 中 项目结构似乎不同 我的项目中的文件夹层次结构如下 Complete Project gt idea
  • 在意图过滤器中使用多个操作时的默认值

    尝试理解 Android 中的意图和操作并查看文档 http developer android com guide topics intents intents filters html 但我一直看到的一件事是定义了多个操作的意图过滤器
  • Android 原理图内容提供程序库配置?

    Jake Wharton 在最近的一次演讲中提到了这个库 它看起来是避免大量样板文件的好方法 所以我尝试了一下 但没有任何成功 https github com SimonVT schematic https github com Simo
  • 获取 AlarmManager 中活动的 PendingIntents 列表

    我有办法获取活动列表PendingIntent在设备中 我开始工作AlarmManager我想看看我的PendingIntents 已正确创建和删除 也很高兴看到其他什么PendingIntent在那里 只是为了看看某些应用程序是否正在做一
  • TextView 之间有分隔线

    我正在尝试在 android studio 中创建以下布局 因为我对 android 东西还很陌生 所以我第一次尝试使用 LinearLayout 并认为这可能无法实现 现在我正在尝试使用RelativeLayout 我已经用颜色创建了这个
  • 更新到材质 1.2.0 后,材质按钮上缺少圆角半径属性

    这是我的材质按钮代码
  • CookieManager.getInstance().removeAllCookie();不删除所有cookie

    我在应用程序的 onCreate 中调用 CookieManager getInstance removeAllCookie 我遇到了一个奇怪的问题 我看到 GET 请求中传递了意外的 cookie 值 事实上 cookie 值是一个非常非
  • Flutter 深度链接

    据Flutter官方介绍深层链接页面 https flutter dev docs development ui navigation deep linking 我们不需要任何插件或本机 Android iOS 代码来处理深层链接 但它并没
  • Android相机意图:如何获取全尺寸照片?

    我正在使用意图来启动相机 Intent cameraIntent new Intent android provider MediaStore ACTION IMAGE CAPTURE getParent startActivityForR
  • 如何在不更改手机语言的情况下更改Android应用程序语言?

    我希望用户在应用程序内选择一种语言 选择语言后 我希望字符串使用特定语言 如果我更改手机语言 那么我的应用程序将以设置的语言运行 我无法找到任何在不更改手机语言的情况下设置语言的方法 此外 一旦设置了语言 更改就应该反映出来 有人可以建议一
  • 调节麦克风录音音量

    我们正在尝试调整录音时的音量级别 麦克风似乎非常敏感 会接收到很多静电 我们查看了 setVolumeControlStream 但找不到传入其中来控制麦克风的流 将您的音频源设置为 MIC using MediaRecorder Audi
  • 应用程序关闭时的倒计时问题

    我制作了一个 CountDownTimer 代码 我希望 CountDownTimer 在完成时重新启动 即使应用程序已关闭 但它仅在应用程序正在运行或重新启动应用程序时重新启动 因此 如果我在倒计时为 00 10 分钟 秒 时关闭应用程序
  • Android:有没有办法以毫安为单位获取设备的电池容量?

    我想获取设备的电池容量来进行一些电池消耗计算 是否可以以某种方式获取它 例如 三星 Galaxy Note 2 的电池容量为 3100mAh 谢谢你的帮助 知道了 在 SDK 中无法直接找到任何内容 但可以使用反射来完成 这是工作代码 pu

随机推荐

  • 方案和浅绑定

    define make lambda x lambda y cons x list y let x 7 p make 4 cons x p 0 我是计划和函数式程序的新手 所以我对遍历程序有点笨拙 但我知道如果我使用深度绑定 该程序将返回
  • 如何使用函数进行模板模板参数推导?

    考虑一组函数 例如 template lt class Fun gt void A const Fun template lt class Fun gt void B const Fun template lt class Fun gt v
  • GWT 1.6 项目战争布局 - 混合源代码和编译器生成的工件?

    刚刚完成了一个基于 GWT 1 5 的项目 我正在考虑如何迁移到 1 6 我很惊讶地发现 GWT 似乎想将其编译输出写入 war 目录 通常在该目录中将项目置于源代码控制之下 这背后的原因是什么 谷歌真的认为这是个好主意吗 是否有解决方法可
  • 如何从HDFS文件系统执行hadoop jar?

    我总是从本地文件系统执行作业 如下所示 hadoop jar home usr jar myjar jar com test TestMain 如何执行相同的命令 但 myjar jar 将位于 hdfs 中 就像是 hadoop jar
  • 如何转义字段名称方括号内的方括号

    我有一些生成列名的动态 SQL 一个简单的例子可能是这样的 SELECT dbo getSomething 123 Eggs scrambled or Bacon fried 最终的列名称应该是这样的 鸡蛋 炒 或培根 煎 如果我尝试运行它
  • 使用 LESS 递归获取当前索引

    我正在尝试做到这一点 我有一个像这样的数组 levels level one level two level three level four level five level six level seven level eight 每个级
  • iPhone超声波检测(超过22kHz)

    iphone 3GS及以上型号能检测到的最大频率是多少 我一直在探索iPhone音频 我需要在没有任何外部设备的情况下检测 22 kHz 的声音频率 是否可以 如果麦克风设计得好 它将有一个抗混叠滤波器 其滚降始于略低于奈奎斯特频率 以确保
  • ffmpeg水印处理速度很慢

    我正在开发一个视频处理项目并使用 ffmpeg 进行水印 我完全实现了我想要的 但问题是这个过程非常非常慢 我在 32 位操作系统 Microsoft Windows 8 64 位 CPU 上使用具有 4GB RAM 的英特尔智能第二代系列
  • iOS8中用户默认的plist文件存储在哪里?

    我找不到首选项 plist 文件 因为首选项文件夹为空 要获取库文件夹 我使用 println NSSearchPathForDirectoriesInDomains LibraryDirectory UserDomainMask true
  • 在 MongoDB 中的 Angular.js 中显示图像

    我最近打开了另一个关于如何使用 node js mongoose 在 mongodb 数据库中存储图像的线程 使用猫鼬保存图像 https stackoverflow com questions 27353346 saving image
  • Django:通过“field__isnull=True”或“field=None”过滤查询集?

    我必须通过动态值 可以是 无 过滤查询集 我可以简单地写 filtered queryset queryset filter field value 或者我应该检查 无 if value is None filtered queryset
  • 设置 Cygwin + Android NDK + cocos2Dx 以与 Eclipse 配合使用

    我正在关注该网站的教程 通过游戏应用货币化 作者 Todd Perkins http www lynda com Android tutorials Understanding downloading Cocos2d x 107169 12
  • BASH 数组索引减去最后一个数组

    这是一个困扰我的问题 我需要从用户输入中读取版本号 并且我想使用存储版本号的数组的长度创建一个 菜单 然而 BASH 的神秘语法在这里对我没有帮助 echo VERSIONS 2 0 10 1 2 0 7 1 2 0 7 1 2 0 7 1
  • 数据表:尝试以 csv 格式获取每个表行的选定单选按钮值

    我正在使用 jquery 数据表插件 我想做的是以 CSV 逗号分隔值 格式获取所有选定的单选按钮值 由于记录太多 所以使用分页 当我选择第一页上的单选按钮时 我可以获取 CSV 中所有选定的单选按钮值 运行良好 当我转到第二页并选择单选按
  • 使用 nginx 记录已解析的请求

    如何为 nginx 设置自定义日志格式 以便解析请求并单独记录其各部分 我们提供图片文件来统计邮件的打开次数 图片的 URL 有所不同 但遵循以下规则 http www server com counter XXXXX YYYYY dail
  • GitHub 操作:致命:无法读取“https://github.com”的用户名:设备未配置

    我试图让git clone在私人存储库上使用 Github Action 但我不确定应该如何配置它以使用 SSH 连接到 GitHub 顺便说一句 它是 macOS 运行程序 此时此刻 actions checkout工作正常 但是当我打电
  • 通过互联网传输数据的最简单方法,Python

    我有两台电脑 都连接到互联网 我想在它们之间传输一些基本数据 字符串 整数 浮点数 我是网络新手 所以我正在寻找最简单的方法来做到这一点 我需要哪些模块来做到这一点 两个系统都将运行 Windows 7 只要它不是异步的 同时发送和接收 您
  • VBscript 的递归问题

    我正在尝试在 vbscript 中实现一些递归 Function largest prime factor ByVal num For i 2 to num 2 If num mod i 0 Then this number is not
  • 如何获取特定用户的 Windows“特殊文件夹”路径?

    在服务内部 确定特定用户的特殊文件夹路径 例如 我的文档 的最佳方法是什么 SHGetFolderPath允许您传递令牌 因此我假设有某种方法可以模拟您感兴趣的文件夹的用户 有没有办法仅根据用户名来执行此操作 如果没有 用户帐户所需的最少信
  • 多行“ReplacementSpan”绘图问题

    只要文本不太长 我的自定义替换跨度就可以工作 但一旦文本长于一行 跨度绘图就会完全崩溃 我的理解是draw 在这种情况下被调用两次导致跨度绘制两次 无法区分第二个绘制调用和第一个绘制调用 从而使您可以控制绘制内容和绘制位置 start an