多行"ReplacementSpan"绘图问题 [英] Multiline `ReplacementSpan` drawing issue

查看:708
本文介绍了多行"ReplacementSpan"绘图问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只要文本不是太长,我的自定义替换跨度就可以工作,但是一旦文本长于一行,跨度图就会完全破裂.我的理解是,在这种情况下draw()被调用两次,导致跨度绘制两次.没有办法将第二个抽奖电话与第一个抽奖电话区分开,让您控制要绘制的内容和位置. startend由于报告错误的值而变得无用.

My custom replacement span works as long as text is not too long but as soon as text is longer than one line, span drawing completely breaks apart. My understanding is that draw() gets called twice in this case causing span to draw twice. There is no way to differentiate that second draw call from first one, giving you control over what to draw and where. start and end become useless as they report wrong values.

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

Is ReplacementSpan supposed to even work for multiline text? I would appreciate any help to resolve this issue.

当我将所选文本更改为我的CustomReplacementSpan时,会发生以下情况:

This is what happens when I change selected text to my CustomReplacementSpan:

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)
    }
}

activity_main.xml

<?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无法做到这一点.这是来自文章绘制圆角背景在弗洛里纳·蒙特内斯库(Florina Muntenescu)的文字上,他写了有关跨度等的博客. (以下引用中的重点是我的.)

Flowing onto a new line is, evidently, something that ReplacementSpan cannot do. Here is a quote from an article Drawing a rounded corner background on text by Florina Muntenescu who blogs about spans and the like. (Emphasis in the following quote is mine.)

我们需要与文本一起绘制一个可绘制对象.我们可以实现自定义的ReplacementSpan来自己绘制背景和文本. 但是ReplacementSpans无法流入下一行,因此我们将无法支持多行背景.它们看起来更像Chip(材料设计组件),其中每个元素都必须放在一行上.

We need to draw a drawable together with the text. We can implement a custom ReplacementSpan to draw the background and the text ourselves. However ReplacementSpans cannot flow into the next line, therefore we will not be able to support a multi-line background. They would rather look like Chip, the Material Design component, where every element must fit on a single line.

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

This is the issue that you are having. The article goes on about a possible solution that you may want to look into. For example, it may be possible to use some of the techniques outlined in the article to define multiple ReplacementSpans dependent upon line breaks like is done with the background drawables.

还有其他跨度类型可能更适合您的目的. 此处是其中的列表.

There are other span types that may be more congenial to your purposes. Here is list of them.

这篇关于多行"ReplacementSpan"绘图问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆