Android Spannablecontent With Rounded Corners

前端 未结 8 790
无人及你
无人及你 2020-12-13 15:06

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 BackGrou

相关标签:
8条回答
  • 2020-12-13 15:36

    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 :)

    0 讨论(0)
  • 2020-12-13 15:38

    After reading getting a little help with a converter for C#, I came up with this. I still have some tweaking to do, but if anyone is also looking for a similar answer.

    public class RoundedBackgroundSpan extends ReplacementSpan
    {
    
        @Override
        public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            return 0;
        }
    
        @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 + text.length(), bottom);
            paint.setColor(Color.CYAN);
            canvas.drawRoundRect(rect, 20, 20, paint);
            paint.setColor(Color.WHITE);
            canvas.drawText(text, start, end, x, y, paint);
        }
    }
    
    0 讨论(0)
  • 2020-12-13 15:41

    Hopefully this answer simplifies it for those still looking...

    You can simply use a "chip" drawable. It does all the calculations correctly and is of much more minimal code.

    See Standalone ChipDrawable

    For completeness copied here:

    res/xml/standalone_chip.xml:

    <chip xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:text="@string/item_browse_database_sample"
        app:chipBackgroundColor="@color/blueBase"
        app:closeIconVisible="false" />
    

    in java:

    // Inflate from resources.
    ChipDrawable chip = ChipDrawable.createFromResource(getContext(), R.xml.standalone_chip);
    
    // Use it as a Drawable however you want.
    chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight());
    ImageSpan span = new ImageSpan(chip);
    
    Editable text = editText.getText();
    text.setSpan(span, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    

    End result:

    0 讨论(0)
  • 2020-12-13 15:43

    Here's an improved version based on @ericlokness answer, with custom background and text colors. It also works with multiple spans on the same TextView.

    public class RoundedBackgroundSpan extends ReplacementSpan
    {
      private final int _padding = 20;
      private int _backgroundColor;
      private int _textColor;
    
      public RoundedBackgroundSpan(int backgroundColor, int textColor) {
        super();
        _backgroundColor = backgroundColor;
        _textColor = textColor;
      }
    
      @Override
      public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        return (int) (_padding + paint.measureText(text.subSequence(start, end).toString()) + _padding);
      }
    
      @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 - _padding, top, x + width + _padding, bottom);
        paint.setColor(_backgroundColor);
        canvas.drawRoundRect(rect, 20, 20, paint);
        paint.setColor(_textColor);
        canvas.drawText(text, start, end, x, y, paint);
      }
    }
    
    0 讨论(0)
  • 2020-12-13 15:46

    Watching Google's video, they offer this solution:

    Sadly I see here various things missing, and I can't find the full code, so I can't try it out.

    0 讨论(0)
  • 2020-12-13 15:51

    I further improved mvandillen class.

    This seems to work very fine:

    public class RoundedBackgroundSpan extends ReplacementSpan
        {
            private final int mPadding = 10;
            private int mBackgroundColor;
            private int mTextColor;
    
            public RoundedBackgroundSpan(int backgroundColor, int textColor) {
                super();
                mBackgroundColor = backgroundColor;
                mTextColor = textColor;
            }
    
            @Override
            public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
                return (int) (mPadding + paint.measureText(text.subSequence(start, end).toString()) + mPadding);
            }
    
            @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, top+mPadding, x + width + 2*mPadding, bottom);
                paint.setColor(mBackgroundColor);
                canvas.drawRoundRect(rect, mPadding, mPadding, paint);
                paint.setColor(mTextColor);
                canvas.drawText(text, start, end, x+mPadding, y, paint);
            }
        }
    
    0 讨论(0)
提交回复
热议问题