Android中的ReplacementSpan替代方案

我有一个应用程序,可以分页大文本,并为每个单词或句子设置多个跨度.我正在使用ReplacementSpan为每个单词绘制背景.我不能使用BackgroundSpan,因为它太简单了,不能让我控制画布.由于ReplacementSpan扩展了MetricAffectingSpan,它影响了文本的布局,完全打破了我的分页.我正在使用StaticLayout来计算每个页面的文本,而StaticLayout不允许跨越,因此它可以计算跨越大小影响的先验.

ReplacementSpan有替代品吗?如何在不影响文本大小和布局的情况下绘制我想要的背景?

这是我的replacementspan的代码

public class BackgroundColorWithoutLineHeightSpan extends ReplacementSpan {

  private static final float DP_ACTIVE = ViewsUtils.dpToPx(4);
  private static final int DP_OUTSIDE_PADDING = (int) ViewsUtils.dpToPx(6);
  private static final float DP_PHRASE = ViewsUtils.dpToPx(4);
  private static final float DP_ROUNDED = ViewsUtils.dpToPx(3);

  private final int mColor;
  private final int mTextHeight;
  private int mBorderColor;
  private boolean mIsSelected;
  private boolean mIsPhrase;

  public BackgroundColorWithoutLineHeightSpan(int color,int textHeight,boolean isPhrase) {
    mColor = color;
    mTextHeight = textHeight;
    mIsPhrase = isPhrase;
  }

  public BackgroundColorWithoutLineHeightSpan(int color,boolean isSelected,int borderColor,boolean isPhrase) {
    mColor = color;
    mTextHeight = textHeight;
    mIsSelected = isSelected;
    mBorderColor = borderColor;
    mIsPhrase = isPhrase;
  }

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

  @Override
  public void draw(@NonNull Canvas canvas,float x,int top,int y,int bottom,Paint paint) {

    canvas.save();

    Rect newRect = canvas.getClipBounds();
    newRect.inset(-DP_OUTSIDE_PADDING,-DP_OUTSIDE_PADDING);

    canvas.clipRect(newRect,Region.Op.REPLACE);

    float measuredText = measureText(paint,end);

    int paintColor = paint.getColor();

    if (!mIsSelected) {
      RectF rect;
      rect = new RectF(x,top,x + measuredText,top + mTextHeight);

      paint.setstrokeWidth(0.0f);
      paint.setColor(mColor);
      paint.setStyle(Paint.Style.FILL);

      canvas.drawRoundRect(rect,DP_ROUNDED,paint);

    } else {

      RectF rect;
      if (mIsPhrase) {
        rect = new RectF(x - DP_PHRASE,top - DP_PHRASE,x + measuredText + DP_PHRASE,top + mTextHeight + DP_PHRASE);
      } else {
        rect = new RectF(x - DP_ACTIVE,top - DP_ACTIVE,x + measuredText + DP_ACTIVE,top + mTextHeight + DP_ACTIVE);
      }
      paint.setstrokeWidth(0.0f);
      paint.setColor(mColor);
      paint.setStyle(Paint.Style.FILL);

      canvas.drawRoundRect(rect,paint);

      RectF border;
      if (mIsPhrase) {
        border = new RectF(x - DP_PHRASE,top + mTextHeight + DP_PHRASE);
      } else {
        border = new RectF(x - DP_ACTIVE,top + mTextHeight + DP_ACTIVE);
      }

      paint.setColor(mBorderColor);
      paint.setstrokeWidth(4.0f);
      paint.setStyle(Paint.Style.stroke);

      canvas.drawRoundRect(border,paint);
    }

    paint.setStyle(Paint.Style.FILL);
    paint.setColor(paintColor);
    canvas.drawText(text,end,x,y,paint);

    canvas.restore();
  }

  private float measureText(Paint paint,int end) {
    return paint.measureText(text,end);
  }
}
最佳答案
尝试这个简单的跨度,它在所有跨度上绘制纯红色背景(即使它是多行跨度)但你可以绘制任何你喜欢的:

class LBS implements LineBackgroundSpan {
    private final TextView tv;
    private int start;
    private int end;

    public LBS(TextView tv,int end) {
        this.tv = tv;
        this.start = start;
        this.end = end;
    }

    @Override
    public void drawBackground(Canvas c,Paint p,int left,int right,int baseline,int lnum) {
        Layout layout = tv.getLayout();
        int startLine = layout.getLineForOffset(this.start);
        int endLine = layout.getLineForOffset(this.end);
        if (startLine <= lnum && lnum <= endLine) {
            if (startLine == lnum) {
                left = (int) layout.getPrimaryHorizontal(this.start);
            }
            if (endLine == lnum) {
                right = (int) layout.getPrimaryHorizontal(this.end);
            }
            int origColor = p.getColor();
            p.setColor(Color.RED);
            c.drawRect(left,right,bottom,p);
            p.setColor(origColor);
        }
    }
}

测试代码(设置0和ssb.length()作为开始和结束不是非常有效,所以你可以优化它):

TextView tv = new TextView(this);
setContentView(tv);
tv.setTextSize(32);
SpannableStringBuilder ssb = new SpannableStringBuilder("Chop a handfull spinach,pork shoulder,and dill in a large cooker over medium heat,cook for six minutes and varnish with some bok choy.");
LBS span = new LBS(tv,30,100);
ssb.setSpan(span,ssb.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(ssb);

Log.d(TAG,"onCreate text [" + ssb.subSequence(30,100) + "]");

编辑

如果您有多个单词来标记/突出显示,您可以使用它的修改版本:

class LBS implements LineBackgroundSpan {
    TextView tv;
    Listirst);
            int endLine = layout.getLineForOffset(range.second);
            if (startLine <= lnum && lnum <= endLine) {
                if (startLine == lnum) {
                    left = (int) layout.getPrimaryHorizontal(range.first);
                }
                if (endLine == lnum) {
                    right = (int) layout.getPrimaryHorizontal(range.second);
                }
                int origColor = p.getColor();
                p.setColor(Color.RED);
                c.drawRect(left,p);
                p.setColor(origColor);
            }
        }
    }
}

测试代码

    TextView tv = new TextView(this);
    setContentView(tv);
    tv.setTextSize(32);
    String text = "Chop a handfull spinach,cook for six minutes and varnish with some bok choy.";
    SpannableStringBuilder ssb = new SpannableStringBuilder(text);
    LBS span = new LBS(tv);

    String[] words = {
            "spinach,pork shoulder","cooker","with some bok choy",};
    for (String word : words) {
        int idx = text.indexOf(word);
        span.add(idx,idx + word.length());
    }

    ssb.setSpan(span,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    tv.setText(ssb);

相关文章

Android性能优化——之控件的优化 前面讲了图像的优化,接下...
前言 上一篇已经讲了如何实现textView中粗字体效果,里面主要...
最近项目重构,涉及到了数据库和文件下载,发现GreenDao这个...
WebView加载页面的两种方式 一、加载网络页面 加载网络页面,...
给APP全局设置字体主要分为两个方面来介绍 一、给原生界面设...
前言 最近UI大牛出了一版新的效果图,按照IOS的效果做的,页...