【问题标题】:ReplacementSpan's draw() method isn't calledReplacementSpan 的 draw() 方法没有被调用
【发布时间】:2013-12-02 21:17:27
【问题描述】:

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

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() 被正确调用。

【问题讨论】:

    标签: android spannablestring


    【解决方案1】:

    万一有人遇到这个问题,我遇到了。我必须做的是将第二个参数传递给 setText() 方法。我的电话看起来像

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

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      在方法getSize() 中,Paint.FontMetricsInt fm 必须根据paint FontMetricsInt 重新计算:

      @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;
      }
      

      【讨论】:

        【解决方案3】:

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

        返回跨度的宽度。扩展类可以通过更新 Paint.FontMetricsInt 的属性来设置跨度的高度。 如果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;
            }
            //...
        }
        

        【讨论】:

          【解决方案4】:

          看起来我找到了解决这个问题的有效方法(但很老套)。尝试在字符串末尾添加另一个符号,使其不包含在跨度中 - 然后 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);
          

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

          【讨论】:

          • 确实,它有效,但我们为什么要认真对待这样的解决方法..
          【解决方案5】:

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

          您必须为函数 getSize 设置更新参数 Paint.FontMetricsInt fm 的值的跨度高度。检查DynamicDrawable.getSize函数作为参考,通过更新FontMetrics值来设置高度。

          【讨论】:

            【解决方案6】:

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

            【讨论】:

            • 我不想在视图级别上弄得一团糟,我希望 Span 像标准的 BackgroundColorSpan 一样自行制作填充。可能吗? Edit 我刚刚检查了您的解决方案。添加 SpannableString 后在 TextView 上调用 invalidate(这是您的提议吗?),它不起作用。
            【解决方案7】:

            我认为这对你有帮助。设置 textView 宽度匹配父级。 main_activity.xml

            <TextView
               android:width="match_parent"
            

            【讨论】:

              猜你喜欢
              • 2012-03-13
              • 2014-06-02
              • 2011-08-28
              • 2017-02-24
              • 2015-02-23
              • 2013-12-19
              • 1970-01-01
              • 2020-09-19
              相关资源
              最近更新 更多