Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to change my string to make a badge with a number in the middle by using Spannable String. I can highlight the appropriate letter/number by setting the BackGroundColorSpan, but need help making it a little prettier. I was hoping to have rounded corners with a little bit of padding around the entire shape.

This article is really close to what I'm trying to do: Android SpannableString set background behind part of text

I really need to keep the resource as a TextView due to the way it interacts with my application.

Any ideas how to utilize ReplacementSpan for my particular situation?

Here is my code snippet:

            if (menuItem.getMenuItemType() == SlidingMenuItem.MenuItemType.NOTIFICATIONS) {
                myMenuRow.setTypeface(null, Typeface.NORMAL);
                myMenuRow.setTextColor(getContext().getResources().getColor(R.color.BLACK));
                myMenuRow.setActivated(false);
                SpannableString spannablecontent = new SpannableString(myMenuRow.getText());
                spannablecontent.setSpan(new BackgroundColorSpan(Color.argb(150,0,0,0)), 18, myMenuRow.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                myMenuRow.setText(spannablecontent);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
105 views
Welcome To Ask or Share your Answers For Others

1 Answer

Actually i found big issues with all of those answers when displaying multiple lines of badges. After lots of testing and tweaking. I Finally got the best version of the above.

The basic idea is to trick the TextView by setting a much bigger text size and setting the wanted size inside the span. Also, you can see i'm drawing the badge background and text differently.

So, this is my RoundedBackgroundSpan:

public class RoundedBackgroundSpan extends ReplacementSpan {

    private static final int CORNER_RADIUS = 12;

    private static final float PADDING_X = GeneralUtils.convertDpToPx(12);
    private static final float PADDING_Y = GeneralUtils.convertDpToPx(2);

    private static final float MAGIC_NUMBER = GeneralUtils.convertDpToPx(2);

    private int mBackgroundColor;
    private int mTextColor;
    private float mTextSize;

    /**
     * @param backgroundColor color value, not res id
     * @param textSize        in pixels
     */
    public RoundedBackgroundSpan(int backgroundColor, int textColor, float textSize) {
        mBackgroundColor = backgroundColor;
        mTextColor = textColor;
        mTextSize = textSize;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        paint = new Paint(paint); // make a copy for not editing the referenced paint

        paint.setTextSize(mTextSize);

        // Draw the rounded background
        paint.setColor(mBackgroundColor);
        float textHeightWrapping = GeneralUtils.convertDpToPx(4);
        float tagBottom = top + textHeightWrapping + PADDING_Y + mTextSize + PADDING_Y + textHeightWrapping;
        float tagRight = x + getTagWidth(text, start, end, paint);
        RectF rect = new RectF(x, top, tagRight, tagBottom);
        canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, paint);

        // Draw the text
        paint.setColor(mTextColor);
        canvas.drawText(text, start, end, x + PADDING_X, tagBottom - PADDING_Y - textHeightWrapping - MAGIC_NUMBER, paint);
    }

    private int getTagWidth(CharSequence text, int start, int end, Paint paint) {
        return Math.round(PADDING_X + paint.measureText(text.subSequence(start, end).toString()) + PADDING_X);
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        paint = new Paint(paint); // make a copy for not editing the referenced paint
        paint.setTextSize(mTextSize);
        return getTagWidth(text, start, end, paint);
    }
}

And here is how i'm using it:

public void setTags(ArrayList<String> tags) {
    if (tags == null) {
        return;
    }

    mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 26); // Tricking the text view for getting a bigger line height

    SpannableStringBuilder stringBuilder = new SpannableStringBuilder();

    String between = " ";
    int tagStart = 0;

    float textSize = 13 * getResources().getDisplayMetrics().scaledDensity; // sp to px

    for (String tag : tags) {
        // Append tag and space after
        stringBuilder.append(tag);
        stringBuilder.append(between);

        // Set span for tag
        RoundedBackgroundSpan tagSpan = new RoundedBackgroundSpan(bgColor, textColor, textSize);
        stringBuilder.setSpan(tagSpan, tagStart, tagStart + tag.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        // Update to next tag start
        tagStart += tag.length() + between.length();
    }

    mTextView.setText(stringBuilder);
}


Note:

  • You can play with all sizes and constants to fit to your wanted style
  • If you use an external font be sure to set android:includeFontPadding="false" otherwise it can mess up the line's height

Enjoy :)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...