为什么我的可扩展文本没有显示?

10

背景

我正在尝试在 TextView 上使用一个简单的 SpannableString,基于我找到的 UnderDotSpan 类(在这里)。

原始的 UnderDotSpan 只是在文本下方(不重叠)放置一个特定大小和颜色的点。我的尝试是先正常使用它,然后使用自定义的可绘制对象代替一个点。

问题

与正常的 Span 使用相反,这个代码段什么也没有显示,即使是文本也没有。

以下是正常 Span 的用法:

val text = "1"
val timeSpannable = SpannableString(text)
timeSpannable.setSpan(ForegroundColorSpan(0xff00ff00.toInt()), 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(timeSpannable);

如果我想在TextView中显示一个绿色的数字"1",可以使用spannable。但是当我尝试下一个spannable时,整个TextView内容(包括文本和点)都不会显示:

val text = "1"
val spannable = SpannableString(text)
spannable.setSpan(UnderDotSpan(this@MainActivity, 0xFF039BE5.toInt(), textView.currentTextColor),
                0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(spannable, TextView.BufferType.SPANNABLE)
// this also didn't work:       textView.setText(spannable)

奇怪的是,在我使用的一个项目中,它在RecyclerView中正常工作,而在另一个项目中却不行。
这是UnderDotSpan的代码:
class UnderDotSpan(private val mDotSize: Float, private val mDotColor: Int, private val mTextColor: Int) : ReplacementSpan() {
    companion object {
        @JvmStatic
        private val DEFAULT_DOT_SIZE_IN_DP = 4
    }

    constructor(context: Context, dotColor: Int, textColor: Int) : this(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_DOT_SIZE_IN_DP.toFloat(), context.resources.displayMetrics), dotColor, textColor) {}

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?) = Math.round(paint.measureText(text, start, end))

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        if (TextUtils.isEmpty(text)) {
            return
        }
        val textSize = paint.measureText(text, start, end)
        paint.color = mDotColor
        canvas.drawCircle(x + textSize / 2, bottom + mDotSize, mDotSize / 2, paint)
        paint.color = mTextColor
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
    }

}

请注意,TextView 没有任何特殊属性,但我仍会展示它:
<android.support.constraint.ConstraintLayout
    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" tools:context="com.example.user.myapplication.MainActivity">

    <TextView android:id="@+id/textView"
        android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

我尝试过的方法

我尝试了从其他span类扩展,也尝试以其他方式将文本设置到TextView中。

我还尝试了基于UnderDotSpan类创建的其他span类。例如:

class UnderDrawableSpan(val drawable: Drawable, val drawableWidth: Int = drawable.intrinsicWidth, val drawableHeight: Int = drawable.intrinsicHeight, val margin: Int = 0) : ReplacementSpan() {
    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int = Math.round(paint.measureText(text, start, end))

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        if (TextUtils.isEmpty(text))
            return
        val textSize = paint.measureText(text, start, end)

        canvas.drawText(text, start, end, x, y.toFloat(), paint)
        canvas.save()
        canvas.translate(x + textSize / 2f - drawableWidth / 2f, y.toFloat() + margin)
        if (drawableWidth != 0 && drawableHeight != 0)
            drawable.setBounds(0, 0, drawableWidth, drawableHeight)
        drawable.draw(canvas)
        canvas.restore()
    }

}

在调试过程中,我发现draw函数甚至没有被调用,而getSize确实被调用了(并返回了一个>0的值)。

问题

为什么span不能显示在TextView上?

我使用的方法有什么问题吗?

如何修复它并使用这个span

为什么它在其他更复杂的情况下可能有效?


你尝试过使用 Html.toHtml 吗? - Zoe stands with Ukraine
@Zoe 不是,但现在我已经知道问题所在了:不是显示文本“1”和点,而是显示 <p dir="ltr">1</p> 。我希望能够像其他正常的span一样使用该代码。 - android developer
@pskink,你知道为什么我写的东西在这种情况下不起作用吗? - android developer
@BenP。整个TextView的内容,包括文本和点号。我会更新问题。 - android developer
@aelimill 我不希望在真正的应用程序中出现这个问题,但是即使使用“match_parent”(对于宽度和高度都设置为“match_parent”),文本和点都不会显示出来。 - android developer
显示剩余3条评论
4个回答

6
基本问题在于未设置 ReplacementSpan 的高度。如 ReplacementSpan 的源代码 所述:

如果跨度覆盖整个文本,并且未设置高度,则不会为该跨度调用 draw(Canvas, CharSequence, int, int, float, int, int, int, Paint)}。

这是对 Archit Sureja 发布的内容的重复。在我的原始帖子中,我在 getSize() 中更新了 ReplacementSpan 的高度,但现在我实现了 LineHeightSpan.WithDensity 接口来完成相同的工作。(感谢 vovahost 这里 提供的信息。)
然而,您提出了其他需要解决的问题。
您提供的项目存在的问题是点号不适合必须驻留的TextView中。您看到的是截断点。如果点的大小超过文本的宽度或高度怎么办?
首先,关于高度,接口LineHeightSpan.WithDensitychooseHeight()方法通过将点的大小添加到字体的有效高度中来调整TextView字体底部的定义。为此,点的高度被添加到字体的底部:
fontMetricsInt.bottom = fm.bottom + mDotSize.toInt(); 

(这是与上一个答案不同的更改,它使用了TextView的padding。自从这个更改后,UnderDotSpan类不再需要TextView。虽然我添加了TextView,但它并不是真正所需的。)

最后一个问题是,如果圆点比文本更宽,则在开头和结尾时会被截断。在这里,clipToPadding="false"不能解决问题,因为圆点被截断不是因为它被剪裁到填充区域,而是被剪裁到我们在getSize()中设置的文本宽度。为了解决这个问题,我修改了getSize()方法,检测圆点是否比文本测量值更宽,并增加返回值以匹配圆点的宽度。一个名为mStartShim的新值是必须应用于文本和圆点绘制以使其符合要求的量。

最后一个问题是,圆点的中心点是下方文本的圆点半径而不是直径,因此draw()中绘制圆点的代码被更改为:

canvas.drawCircle(x + textSize / 2, bottom.toFloat(), mDotSize / 2, paint)

(我还改变了代码,使用Canvas翻译而不是添加偏移量。效果是相同的。)

以下是结果:

enter image description here

activity_main.xml

<android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/darker_gray">

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="24dp"
    android:background="@android:color/white"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val text = "1"
        val spannable = SpannableString(text)
        spannable.setSpan(UnderDotSpan(this@MainActivity, 0xFF039BE5.toInt(), textView.currentTextColor),
                0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        textView.setText(spannable, TextView.BufferType.SPANNABLE)
    }
}

UnderDotSpan.kt

// From the original UnderDotSpan: Also implement the LineHeightSpan.WithDensity interface to
// compute the height of our "dotted" font.

class UnderDotSpan(private val mDotSize: Float, private val mDotColor: Int, private val mTextColor: Int) : ReplacementSpan(), LineHeightSpan.WithDensity {
    companion object {
        @JvmStatic
        private val DEFAULT_DOT_SIZE_IN_DP = 16
    }

    // Additional horizontal space to the start, if needed, to fit the dot
    var mStartShim = 0;

    constructor(context: Context, dotColor: Int, textColor: Int)
            : this(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_DOT_SIZE_IN_DP.toFloat(),
            context.resources.displayMetrics), dotColor, textColor)

    // ReplacementSpan override to determine the size (length) of the text.
    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        val baseTextWidth = paint.measureText(text, start, end)

        // If the width of the text is less than the width of our dot, increase the text width
        // to match the dot's width; otherwise, just return the width of the text.
        mStartShim = if (baseTextWidth < mDotSize) ((mDotSize - baseTextWidth) / 2).toInt() else 0
        return Math.round(baseTextWidth + mStartShim * 2)
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int,
                      y: Int, bottom: Int, paint: Paint) {
        if (TextUtils.isEmpty(text)) {
            return
        }
        val textSize = paint.measureText(text, start, end)
        paint.color = mDotColor
        canvas.save()

        // Draw the circle in the horizontal center and under the text. Add in the
        // offset (mStartShim) if we had to increase the length of the text to accommodate our dot.
        canvas.translate(mStartShim.toFloat(), -mDotSize / 2)

        // Draw a circle, but this could be any other shape or drawable. It just has
        // to fit into the allotted space which is the size of the dot.
        canvas.drawCircle(x + textSize / 2, bottom.toFloat(), mDotSize / 2, paint)
        paint.color = mTextColor

        // Keep the starting shim, but reset the y-translation to write the text.
        canvas.translate(0f, mDotSize / 2)
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
        canvas.restore()
    }

    // LineHeightSpan.WithDensity override to determine the height of the font with the dot.
    override fun chooseHeight(charSequence: CharSequence, i: Int, i1: Int, i2: Int, i3: Int,
                              fontMetricsInt: Paint.FontMetricsInt, textPaint: TextPaint) {
        val fm = textPaint.fontMetricsInt

        fontMetricsInt.top = fm.top
        fontMetricsInt.ascent = fm.ascent
        fontMetricsInt.descent = fm.descent

        // Our "dotted" font now must accommodate the size of the dot, so change the bottom of the
        // font to accommodate the dot.
        fontMetricsInt.bottom = fm.bottom + mDotSize.toInt();
        fontMetricsInt.leading = fm.leading
    }

    // LineHeightSpan.WithDensity override that is needed to satisfy the interface but not called.
    override fun chooseHeight(charSequence: CharSequence, i: Int, i1: Int, i2: Int, i3: Int,
                              fontMetricsInt: Paint.FontMetricsInt) {
    }
}

如果需要在文本下方放置一个小的可绘制对象,可以使用以下基于UnderDotSpan的类:

UnderDrawableSpan.java

public class UnderDrawableSpan extends ReplacementSpan implements LineHeightSpan.WithDensity {
    final private Drawable mDrawable;
    final private int mDrawableWidth;
    final private int mDrawableHeight;
    final private int mMargin;

    // How much we need to jog the text to line up with a larger-than-text-width drawable.
    private int mStartShim = 0;

    UnderDrawableSpan(Context context, Drawable drawable, int drawableWidth, int drawableHeight,
                      int margin) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();

        mDrawable = drawable;
        mDrawableWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                                                         (float) drawableWidth, metrics);
        mDrawableHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                                                          (float) drawableHeight, metrics);
        mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                                                  (float) margin, metrics);
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
                     int bottom, @NonNull Paint paint) {
        if (TextUtils.isEmpty(text)) {
            return;
        }

        float textWidth = paint.measureText(text, start, end);
        float offset = mStartShim + x + (textWidth - mDrawableWidth) / 2;

        mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
        canvas.save();
        canvas.translate(offset, bottom - mDrawableHeight);
        mDrawable.draw(canvas);
        canvas.restore();

        canvas.save();
        canvas.translate(mStartShim, 0);
        canvas.drawText(text, start, end, x, y, paint);
        canvas.restore();
    }

    // ReplacementSpan override to determine the size (length) of the text.
    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        float baseTextWidth = paint.measureText(text, start, end);

        // If the width of the text is less than the width of our drawable, increase the text width
        // to match the drawable's width; otherwise, just return the width of the text.
        mStartShim = (baseTextWidth < mDrawableWidth) ? (int) (mDrawableWidth - baseTextWidth) / 2 : 0;
        return Math.round(baseTextWidth + mStartShim * 2);
    }

    // LineHeightSpan.WithDensity override to determine the height of the font with the dot.
    @Override
    public void chooseHeight(CharSequence charSequence, int i, int i1, int i2, int i3,
                             Paint.FontMetricsInt fontMetricsInt, TextPaint textPaint) {
        Paint.FontMetricsInt fm = textPaint.getFontMetricsInt();

        fontMetricsInt.top = fm.top;
        fontMetricsInt.ascent = fm.ascent;
        fontMetricsInt.descent = fm.descent;

        // Our font now must accommodate the size of the drawable, so change the bottom of the
        // font to accommodate the drawable.
        fontMetricsInt.bottom = fm.bottom + mDrawableHeight + mMargin;
        fontMetricsInt.leading = fm.leading;
    }

    // Required but not used.
    @Override
    public void chooseHeight(CharSequence charSequence, int i, int i1, int i2, int i3,
                             Paint.FontMetricsInt fontMetricsInt) {
    }
}

使用以下的可绘制 XML 和 UnderDrawableSpan 结合使用会产生如下结果: (可绘制对象的宽度和高度设置为12dp。文本的字体大小为24sp。)

enter image description here

gradient_drawable.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="4dp"
        android:height="4dp" />
    <gradient
        android:type="radial"
        android:gradientRadius="60%p"
        android:endColor="#e96507"
        android:startColor="#ece6e1" />
</shape>


最近我有机会重新审视这个问题和答案。我发布了一个更加灵活的UnderDrawableSpan代码版本。在GitHub上有一个演示项目

UnderDrawableSpan.kt(更新版)

/**
 * Place a drawable at the bottom center of text within a span. Because this class is extended
 * from [ReplacementSpan], the span must reside on a single line and cannot span lines.
 */
class UnderDrawableSpan(
    context: Context, drawable: Drawable, drawableWidth: Int, drawableHeight: Int, margin: Int
) : ReplacementSpan(), LineHeightSpan.WithDensity {
    // The image to draw under the spanned text. The image and text will be horizontally centered.
    private val mDrawable: Drawable

    // The width if the drawable in dip
    private var mDrawableWidth: Int

    // The width if the drawable in dip
    private var mDrawableHeight: Int

    // Margin in dip to place around the drawable
    private var mMargin: Int

    // Amount to offset the text from the start.
    private var mTextOffset = 0f

    // Amount to offset the drawable from the start.
    private var mDrawableOffset = 0f

    // Descent specified in font metrics of the TextPaint.
    private var mBaseDescent = 0f

    init {
        val metrics: DisplayMetrics = context.resources.displayMetrics

        mDrawable = drawable
        mDrawableWidth = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, drawableWidth.toFloat(), metrics
        ).toInt()
        mDrawableHeight = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, drawableHeight.toFloat(), metrics
        ).toInt()
        mMargin = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, margin.toFloat(), metrics
        ).toInt()
    }

    override fun draw(
        canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int,
        bottom: Int, paint: Paint
    ) {
        canvas.drawText(text, start, end, x + mTextOffset, y.toFloat(), paint)

        mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight)
        canvas.save()
        canvas.translate(x + mDrawableOffset + mMargin, y + mBaseDescent + mMargin)
        mDrawable.draw(canvas)
        canvas.restore()
    }

    // ReplacementSpan override to determine the width that the text and drawable should occupy.
    // The computed width is determined by the greater of the text width and the drawable width
    // plus the requested margins.
    override fun getSize(
        paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?
    ): Int {
        val textWidth = paint.measureText(text, start, end)
        val additionalWidthNeeded = mDrawableWidth + mMargin * 2 - textWidth

        // If the width of the text is less than the width of our drawable, increase the text width
        // to match the drawable's width; otherwise, just return the width of the text.
        return if (additionalWidthNeeded >= 0) {
            // Drawable is wider than text, so we need to offset the text to center it.
            mTextOffset = additionalWidthNeeded / 2
            textWidth + additionalWidthNeeded
        } else {
            // Text is wider than the drawable, so we need to offset the drawable to center it.
            // We do not need to expand the width.
            mDrawableOffset = -additionalWidthNeeded / 2
            textWidth
        }.toInt()
    }

    // Determine the height for the ReplacementSpan.
    override fun chooseHeight(
        text: CharSequence?, start: Int, end: Int, spanstartv: Int, lineHeight: Int,
        fm: Paint.FontMetricsInt, paint: TextPaint
    ) {
        // The text height must accommodate the size of the drawable. To make the accommodation,
        // change the bottom of the font so there is enough room to fit the drawable between the
        // font bottom and the font's descent.
        val tpMetric = paint.fontMetrics

        mBaseDescent = tpMetric.descent
        val spaceAvailable = fm.descent - mBaseDescent
        val spaceNeeded = mDrawableHeight + mMargin * 2

        if (spaceAvailable < spaceNeeded) {
            fm.descent += (spaceNeeded - spaceAvailable).toInt()
            fm.bottom = fm.descent + (tpMetric.bottom - tpMetric.descent).toInt()
        }
    }

    // StaticLayout prefers LineHeightSpan.WithDensity over this function.
    override fun chooseHeight(
        charSequence: CharSequence?, i: Int, i1: Int, i2: Int, i3: Int, fm: Paint.FontMetricsInt
    ) = throw IllegalStateException("LineHeightSpan.chooseHeight() called but is not supported.")
}

@androiddeveloper 你能否提供一些不按预期工作的代码并在此发布吗? - Cheticamp
@androiddeveloper 那个项目根本不显示文本 - 这是我能够重现的一个情况。我正在寻找一个可以显示截断点的项目。 - Cheticamp
@androiddeveloper 。UnderDrawableSpan 的工作方式与您记录的一样。缺少的是在您找到 UnderDrawableSpan 的较大日历项目中设置跨度高度的代码。我没有追踪到那个,但是在绘制之前有时需要设置高度。只要点符合 TextView 定义的边界,剪辑问题就不存在。问题出现是因为该点为 16dp,而不是在同一日历项目中使用的 4dp - Cheticamp
只是一个简单的可绘制对象。例如,可以选择GradientDrawable并在运行时设置其大小。 - android developer
如果图形比文本宽,文本必须向左移动以保持一切对齐。mStartShim就是这个量。边距的使用未定义,但我在代码中保留了它,这样您就可以根据需要应用它。 - Cheticamp
显示剩余25条评论

4

你的span没有显示是因为由于高度未设置,draw方法未被调用。

请参考此链接

https://developer.android.com/reference/android/text/style/ReplacementSpan.html

GetSize() - 返回span的宽度。扩展类可以通过更新Paint.FontMetricsInt的属性来设置span的高度。如果span覆盖整个文本,并且未设置高度,则不会为该span调用draw(Canvas, CharSequence, int, int, float, int, int, int, Paint)方法。

Paint.FontMetricsInt对象的所有变量都是0,所以没有高度,因此没有调用draw方法。

关于Paint.FontMatricsInt的工作原理,您可以参考此链接。

Meaning of top, ascent, baseline, descent, bottom, and leading in Android's FontMetrics

因此,我们使用在getSize的参数中获取的paint对象来设置Paint.FontMetricsInt。

这是我的代码,我改了一些关于设置高度的东西。

class UnderDotSpan(private val mDotSize: Float, private val mDotColor: Int, private val mTextColor: Int) : ReplacementSpan() {
    companion object {
        @JvmStatic
        private val DEFAULT_DOT_SIZE_IN_DP = 16
    }

    constructor(context: Context, dotColor: Int, textColor: Int) : this(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_DOT_SIZE_IN_DP.toFloat(), context.resources.displayMetrics), dotColor, textColor) {}

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        val asd = paint.getFontMetricsInt()
        fm?.leading = asd.leading
        fm?.top = asd.top
        fm?.bottom = asd.bottom
        fm?.ascent = asd.ascent
        fm?.descent = asd.descent
        return Math.round(measureText(paint, text, start, end))
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        if (TextUtils.isEmpty(text)) {
            return
        }
        val textSize = paint.measureText(text, start, end)
        paint.color = mDotColor
        canvas.drawCircle(x + textSize / 2, (bottom /2).toFloat(), mDotSize / 2, paint)
        paint.color = mTextColor
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
    }

    private fun measureText(paint: Paint, text: CharSequence, start: Int, end: Int): Float {
        return paint.measureText(text, start, end)
    }
}

Final output which I getting is like below

enter image description here

使用以下代码在文本下绘制圆形:
class UnderDotSpan(private val mDotSize: Float, private val mDotColor: Int, private val mTextColor: Int) : ReplacementSpan() {
    companion object {
        @JvmStatic
        private val DEFAULT_DOT_SIZE_IN_DP = 4
    }

    constructor(context: Context, dotColor: Int, textColor: Int) : this(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_DOT_SIZE_IN_DP.toFloat(), context.resources.displayMetrics), dotColor, textColor) {}

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        val asd = paint.getFontMetricsInt()
        fm?.leading = asd.leading + mDotSize.toInt()
        fm?.top = asd.top
        fm?.bottom = asd.bottom
        fm?.ascent = asd.ascent
        fm?.descent = asd.descent
        return Math.round(paint.measureText(text, start, end))
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        if (TextUtils.isEmpty(text)) {
            return
        }
        val textSize = paint.measureText(text, start, end)
        paint.color = mDotColor
        canvas.drawCircle(x + textSize / 2, bottom + mDotSize, mDotSize / 2, paint)
        paint.color = mTextColor
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
    }
}

而最后一个是IMP

val text = "1\n" 代替 val text = "1"


1
我在文本下方看到了点。请看我最近添加的屏幕截图。其次,如我在答案中所说,我们在Paint.FontMatricsInt的所有变量中获取0,这会设置您的TextView的高度。您可以参考我最近在答案中添加的链接。因此,文本视图的高度不会自动设置,这在其他可跨度类中发生。 - Archit Sureja
我在你的示例中没有看到文本下面的点。我看到的是一个叠在另一个上面的点。我的意思是在Y轴上,而不是Z轴上。换句话说,这意味着您需要向下看一点才能看到该点。关于这个问题,我不明白为什么在某些情况下它会起作用。你只是改变了getSize的实现吗? - android developer
1
我已经更改了canvas.drawCircle路径,所以它在y轴上面另一个图形,请检查更新的答案。关于这个问题,我也不太理解,为什么有些情况下它能正常工作,可能是在某些情况下高度由Android设置,没有深入研究。 - Archit Sureja
让我们在聊天中继续这个讨论。点击此处进入聊天室 - Archit Sureja
好的,但是我仍然需要它是wrap_content。文本可以很短,也可以很长。即使从存储库的原始代码中使用“match_parent”似乎也无法工作,所以我仍然不明白为什么它在他们的情况下有效,而在这里却无效。正如你所写的那样,如果我在他们的代码中使用“wrap_content”,它也不能正常工作。 - android developer
显示剩余10条评论

-2

一旦在textview上设置了文本,就可以使用:

textview.setMovementMethod(LinkMovementMethod.getInstance());

例子:

tvDescription.setText(hashText);
tvDescription.setMovementMethod(LinkMovementMethod.getInstance());

无法运行,仍然看不到文本,下面也没有点。 - android developer

-3
/*
 * Set text with hashtag and mentions on TextView
 * */
public void setTextOnTextView(String description, TextView tvDescription)
{
    SpannableString hashText = new SpannableString(description);
    Pattern pattern = Pattern.compile("@([A-Za-z0-9_-]+)");
    Matcher matcher = pattern.matcher(hashText);
    while (matcher.find()) {
        final StyleSpan bold = new StyleSpan(android.graphics.Typeface.BOLD); // Span to make text bold
        hashText.setSpan(bold, matcher.start(), matcher.end(), 0);
    }
    Pattern patternHash = Pattern.compile("#([A-Za-z0-9_-]+)");
    Matcher matcherHash = patternHash.matcher(hashText);
    while (matcherHash.find()) {
        final StyleSpan bold = new StyleSpan(android.graphics.Typeface.BOLD); // Span to make text bold
        hashText.setSpan(bold, matcherHash.start(), matcherHash.end(), 0);
    }
    tvDescription.setText(hashText);
    tvDescription.setMovementMethod(LinkMovementMethod.getInstance());
}

我没有看到这里使用UnderDotSpan,而你添加了其他样式... - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接