-->

Adding a padding/margin to a Spannable

2019-01-23 05:05发布

问题:

I'm using BackgroundColorSpan to customize parts of a TextView. Here's the code I have:

String s = "9.5 Excellent!";
s.setSpan(new BackgroundColorSpan(darkBlue, 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new BackgroundColorSpan(darkBlue, 3, 14, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Which gives me the following result:

And this is what I'm trying to achieve:

As you can see, I'm trying to add a padding to the "9.5" as well as the "excellent!" Strings, but I've been unable to find a solution so far.

Is there a way to add this padding/margin to these Spannables?

回答1:

You can use ReplacementSpan. In your Activity:

TextView tagsTextView = (TextView) mView.findViewById(R.id.tagsTextView);
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();

SpannableString tag1 = new SpannableString("9.5");
stringBuilder.append(tag1);
stringBuilder.setSpan(new TagSpan(getResources().getColor(R.color.blue), getResources().getColor(R.color.white)), stringBuilder.length() - tag1.length(), stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

SpannableString tag2 = new SpannableString("excellent!");
stringBuilder.append(tag2);
stringBuilder.setSpan(new TagSpan(getResources().getColor(R.color.blueLight), getResources().getColor(R.color.blue)), stringBuilder.length() - tag2.length(), stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tagsTextView.setText(stringBuilder, TextView.BufferType.SPANNABLE);

TagSpan.java

public class TagSpan extends ReplacementSpan {
    private static final float PADDING = 50.0f;
    private RectF mRect;
    private int mBackgroundColor;
    private int mForegroundColor;

    public TagSpan(int backgroundColor, int foregroundColor) {
        this.mRect = new RectF();
        this.mBackgroundColor = backgroundColor;
        this.mForegroundColor = foregroundColor;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        // Background
        mRect.set(x, top, x + paint.measureText(text, start, end) + PADDING, bottom);
        paint.setColor(mBackgroundColor);
        canvas.drawRect(mRect, paint);

        // Text
        paint.setColor(mForegroundColor);
        int xPos = Math.round(x + (PADDING / 2));
        int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)) ;
        canvas.drawText(text, start, end, xPos, yPos, paint);
    }

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


回答2:

You can use RoundedBackgroundSpan.

  1. It supports both padding and margin
  2. It avoids the situation where the text is not centered in the TextView.
  3. It can also set BackgroundColor for the text.

    public class RoundedBackgroundSpan extends ReplacementSpan {
    
    
    private final int mBackgroundColor;
    private final int mTextColor;
    private final int mPaddingLeft;
    private final int mPaddingRight;
    private final int mMarginLeft;
    private final int mMarginRight;
    
    /**
     * Add rounded background for text in TextView.
     * @param backgroundColor background color
     * @param textColor       text color
     * @param paddingLeft     padding left(including background)
     * @param paddingRight    padding right(including background)
     * @param marginLeft      margin left(not including background)
     * @param marginRight     margin right(not including background)
     */
    public RoundedBackgroundSpan(int backgroundColor, int textColor,
                                 int paddingLeft,
                                 int paddingRight,
                                 int marginLeft,
                                 int marginRight) {
        mBackgroundColor = backgroundColor;
        mTextColor = textColor;
        mPaddingLeft = paddingLeft;
        mPaddingRight = paddingRight;
        mMarginLeft = marginLeft;
        mMarginRight = marginRight;
    }
    
    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end,
                       Paint.FontMetricsInt fm) {
        return (int) (mMarginLeft + mPaddingLeft +
                paint.measureText(text.subSequence(start, end).toString()) +
                mPaddingRight  + mMarginRight);
    }
    
    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
                     int bottom, Paint paint) {
        float width = paint.measureText(text.subSequence(start, end).toString());
        RectF rect = new RectF(x + mMarginLeft, top
                - paint.getFontMetricsInt().top + paint.getFontMetricsInt().ascent
                , x + width + mMarginLeft + mPaddingLeft + mPaddingRight, bottom);
        paint.setColor(mBackgroundColor);
        canvas.drawRoundRect(rect, rect.height() / 2, rect.height() / 2, paint);
        paint.setColor(mTextColor);
        canvas.drawText(text, start, end, x + mMarginLeft + mPaddingLeft,
                y - paint.getFontMetricsInt().descent / 2, paint);
    }
    }