3

I'v added Text Stroke for TextView using ReplacementSpan. It's working perfectly in Android Sdk below Marshmallow. But, In marshmallow it showing blank text. When I'v added ForegroundSpan to Top TextView and then set my custom span to below views it work's. I don't understand is there anything I'm missing.
Below is my TextSpannable Class code -

public class TextSpannable extends ReplacementSpan {

    private final Paint paintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint paintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Path path = new Path();
    private int width;

    public TextSpannable(int strokeWidth) {
       paintFill.setColor(Color.WHITE);
       paintStroke.setStyle(Paint.Style.STROKE);
       paintStroke.setStrokeWidth(6);
    }

    public void setPathEffect(PathEffect effect) {
       paintStroke.setPathEffect(effect);
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, 
       int end,   Paint.FontMetricsInt fm) {
       this.paintFill.setColor(Color.WHITE);
       this.paintStroke.setColor(Color.parseColor("#402002"));
       width = (int) (paint.measureText(text, start, end) +  
       this.paintStroke.getStrokeWidth() + paintFill.getTextSize());
       return width;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, 
         float x, int top, int y,int bottom, Paint paint) {
        path.reset();
        paint.getTextPath(text.toString(), start, end, x, y, path);
        path.close();

        canvas.translate(this.paintStroke.getStrokeWidth() / 2, 0);
        canvas.drawPath(path,this.paintStroke);
        canvas.drawPath(path, this.paintFill);
        canvas.translate(-this.paintStroke.getStrokeWidth() / 2, 0);
        //canvas.drawText(this.text,start,end,x,y,paintFill);
        //canvas.drawText(this.text,start,end,x,y,paintStroke);
     }
}

This is how I set it to TextView -

    spannableString7 = 
    new SpannableStringBuilder(getString(R.string.string1));
    ForegroundColorSpan fcs  =new ForegroundColorSpan(Color.TRANSPARENT);
    spannableString7.setSpan(fcs,0, getString(R.string.string1).length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
    textSpam.setText(spannableString7, TextView.BufferType.SPANNABLE);    

   //when I comment above block, text becomes blank in Textview

    spannableString1 = 
    new SpannableStringBuilder(getString(R.string.string1));
    spannableString1.setSpan(textSpannable,0,getString(R.string.string1).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setText(spannableString1, TextView.BufferType.SPANNABLE);
Oussema Aroua
  • 5,225
  • 1
  • 24
  • 44
akki101
  • 69
  • 7

2 Answers2

2

I figured how to make it work. You must implement LineHeightSpan because if you use the ReplacementSpan by itself then I suppose the TextView doesn't know what height your text has ? Just implementing LineHeightSpan will fix the problem.

public class CoolBackgroundColorSpan extends ReplacementSpan implements LineHeightSpan {

    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        // Your code here
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
        // Your code here
    }

    @Override
    public void chooseHeight(CharSequence charSequence, int i, int i1, int i2, int i3, Paint.FontMetricsInt fontMetricsInt) {
        // This method is needed because the class implements LineHeightSpan.
        // We don't modify the fontMetricsInt because we don't want to modify the line height.
        // The line height will have the height of the text
    }

}

If you'd like also to change the line height then see this answer https://stackoverflow.com/a/39044426/1502079.

vovahost
  • 34,185
  • 17
  • 113
  • 116
1

Implementing LineHeightSpan (as per vovahost's answer) doesn't always work correctly. The height is sometimes calculated incorrectly, resulting in text being cut on top.

A possible workaround is for the SpannableStringBuilder to contain something else than just a ReplacementSpan. It is possible to append a zero-width space to the text, so it looks unchanged, but is properly measured and displayed by a TextView, e.g.:

spannableString1 = 
    new SpannableStringBuilder(getString(R.string.string1));
spannableString1.setSpan(textSpannable,0,getString(R.string.string1).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

spannableString1.append('\u200B'); // <-- append a zero-width space

textView.setText(spannableString1, TextView.BufferType.SPANNABLE);
Slav
  • 786
  • 1
  • 13
  • 25