ReplacementSpan的draw()方法没有被调用

14

我像这样在字符串中设置背景:

spanString.setSpan(new BackgroundColorSpan(color), 0, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

但我想增加这个背景的左右填充,所以我创建了自定义标签。

public class PaddingBackgroundSpan extends ReplacementSpan {
private int mBackgroundColor;
private int mForegroundColor;

public PaddingBackgroundSpan(int backgroundColor, int foregroundColor) {
    this.mBackgroundColor = backgroundColor;
    this.mForegroundColor = foregroundColor;
}

@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
    return Math.round(measureText(paint, text, start, end));

}

@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
    RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
    paint.setColor(mBackgroundColor);
    canvas.drawRect(rect, paint);
    paint.setColor(mForegroundColor);
    canvas.drawText(text, start, end, x, y, paint);
}

private float measureText(Paint paint, CharSequence text, int start, int end) {
    return paint.measureText(text, start, end);
}

我使用我的跨度:

spanString.setSpan(new PaddingBackgroundSpan(color1, color2), 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

很遗憾,我的draw()方法没有被调用。getSize()被正确调用了。

8个回答

27

如果有人遇到这个问题,我也遇到了。我需要在setText()方法中传入第二个参数。我的调用看起来像这样:

textView.setText(spanned, TextView.BufferType.SPANNABLE);

希望这能帮到你!


11

ReplacementSpan.getSize()的文档提供了答案:

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

确保使用正确的值更新Paint.FontMetricsInt以解决问题。

在我的情况下,我只是从TextViewPaint中取出了度量值:

@Override
public int getSize(
        Paint paint,
        CharSequence text,
        int start,
        int end,
        Paint.FontMetricsInt fm) {
    final Paint.FontMetrics paintFontMetrics = paint.getFontMetrics();
    if (fm != null) {
        fm.ascent = (int) paintFontMetrics.ascent;
        fm.bottom = (int) paintFontMetrics.bottom;
        fm.descent = (int) paintFontMetrics.descent;
        fm.leading = (int) paintFontMetrics.leading;
        fm.top = (int) paintFontMetrics.top;
    }
    //...
}

8
在方法getSize()中,必须基于paint FontMetricsInt重新计算Paint.FontMetricsInt fm
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
    width = Math.round(paint.measureText(text, start, end));
    Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
    if (fm != null) {
        fm.top = metrics.top;
        fm.ascent = metrics.ascent;
        fm.descent = metrics.descent;

        fm.bottom = metrics.bottom;
    }
    return width;
}

4

看起来我找到了一个可行的(但有些hacky)解决方案。试着在字符串末尾添加另一个符号,这样它就不会包含在中 - 然后调用draw()函数!

 char EXTRA_SPACE = ' ';
 String text = originalString + EXTRA_SPACE;
 Spannable spannable = new SpannableString(text);
 spannable.setSpan(new MyReplacementSpan(), 0, text.length()-1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
 textView.setText(spannable);

看起来它忽略了整个字符串宽度的span标签。

确实,它能够工作,但我们为什么需要这样的变通方法呢?认真地说。 - sokarcreative

3
即使你返回了的宽度,但是当覆盖整个字符串时,的高度为0。因此您的实际上存在,具有指定的宽度,但是其高度为0,因此未被绘制。

您必须设置的高度,通过更新函数 getSize 的参数Paint.FontMetricsInt fm的值。可以查看DynamicDrawable.getSize函数来参考如何通过更新FontMetrics值来设置高度。


0

我认为这会对你有所帮助。将textView的宽度设置为match parent。 main_activity.xml

<TextView
   android:width="match_parent"

0
调用视图的invalidate()方法以强制调用draw()

我不想在视图层面上搞乱,我想要一个像标准BackgroundColorSpan一样自己填充的Span。这可能吗?[编辑]我刚刚检查了您的解决方案。在添加SpannableString后在TextView上调用invalidate(这是您的建议吗?),但它不起作用。 - jakub

0

该方法可以刷新异步加载的ImageSpan

        try {
            val sendWatchersMethod =
                SpannableStringBuilder::class.java.getDeclaredMethod("sendToSpanWatchers", Int::class.java, Int::class.java, Int::class.java)
            sendWatchersMethod.isAccessible = true
            val len = length()
            sendWatchersMethod.invoke(spanString, 0, len, len)
        } catch (e: Exception) {
            e.printStackTrace()
        }

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