How to draw text in extended class for TextView?

I am currently working on a TextView extension by adding an outline around the text. So far, the only problem that I have encountered is my inability to correctly position the "outline" behind the text. If I code the extended class as shown below, I get a shortcut that looks like this:

Note: in the above screenshot, I set the fill color to white and the stroke color to black.

What am I doing wrong?

public class OutlinedTextView extends TextView { /* =========================================================== * Constants * =========================================================== */ private static final float OUTLINE_PROPORTION = 0.1f; /* =========================================================== * Members * =========================================================== */ private final Paint mStrokePaint = new Paint(); private int mOutlineColor = Color.TRANSPARENT; /* =========================================================== * Constructors * =========================================================== */ public OutlinedTextView(Context context) { super(context); this.setupPaint(); } public OutlinedTextView(Context context, AttributeSet attrs) { super(context, attrs); this.setupPaint(); this.setupAttributes(context, attrs); } public OutlinedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.setupPaint(); this.setupAttributes(context, attrs); } /* =========================================================== * Overrides * =========================================================== */ @Override protected void onDraw(Canvas canvas) { // Get the text to print final float textSize = super.getTextSize(); final String text = super.getText().toString(); // setup stroke mStrokePaint.setColor(mOutlineColor); mStrokePaint.setStrokeWidth(textSize * OUTLINE_PROPORTION); mStrokePaint.setTextSize(textSize); mStrokePaint.setFlags(super.getPaintFlags()); mStrokePaint.setTypeface(super.getTypeface()); // Figure out the drawing coordinates //mStrokePaint.getTextBounds(text, 0, text.length(), mTextBounds); // draw everything canvas.drawText(text, super.getWidth() * 0.5f, super.getBottom() * 0.5f, mStrokePaint); super.onDraw(canvas); } /* =========================================================== * Private/Protected Methods * =========================================================== */ private final void setupPaint() { mStrokePaint.setAntiAlias(true); mStrokePaint.setStyle(Paint.Style.STROKE); mStrokePaint.setTextAlign(Paint.Align.CENTER); } private final void setupAttributes(Context context, AttributeSet attrs) { final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OutlinedTextView); mOutlineColor = array.getColor( R.styleable.OutlinedTextView_outlineColor, 0x00000000); array.recycle(); // Force this text label to be centered super.setGravity(Gravity.CENTER_HORIZONTAL); } } 
+4
source share
4 answers
Bach, that was stupid from me. I just needed to change this commented line:
 super.getPaint().getTextBounds(text, 0, text.length(), mTextBounds); 

Also, for the actual rendering of the text, I need to average this height and the height of the text:

 // draw everything canvas.drawText(text, super.getWidth() * 0.5f, (super.getHeight() + mTextBounds.height()) * 0.5f, mStrokePaint); 

All code is now read as follows:

 public class OutlinedTextView extends TextView { /* =========================================================== * Constants * =========================================================== */ private static final float OUTLINE_PROPORTION = 0.1f; /* =========================================================== * Members * =========================================================== */ private final Paint mStrokePaint = new Paint(); private final Rect mTextBounds = new Rect(); private int mOutlineColor = Color.TRANSPARENT; /* =========================================================== * Constructors * =========================================================== */ public OutlinedTextView(Context context) { super(context); this.setupPaint(); } public OutlinedTextView(Context context, AttributeSet attrs) { super(context, attrs); this.setupPaint(); this.setupAttributes(context, attrs); } public OutlinedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.setupPaint(); this.setupAttributes(context, attrs); } /* =========================================================== * Overrides * =========================================================== */ @Override protected void onDraw(Canvas canvas) { // Get the text to print final float textSize = super.getTextSize(); final String text = super.getText().toString(); // setup stroke mStrokePaint.setColor(mOutlineColor); mStrokePaint.setStrokeWidth(textSize * OUTLINE_PROPORTION); mStrokePaint.setTextSize(textSize); mStrokePaint.setFlags(super.getPaintFlags()); mStrokePaint.setTypeface(super.getTypeface()); // Figure out the drawing coordinates super.getPaint().getTextBounds(text, 0, text.length(), mTextBounds); // draw everything canvas.drawText(text, super.getWidth() * 0.5f, (super.getHeight() + mTextBounds.height()) * 0.5f, mStrokePaint); super.onDraw(canvas); } /* =========================================================== * Private/Protected Methods * =========================================================== */ private final void setupPaint() { mStrokePaint.setAntiAlias(true); mStrokePaint.setStyle(Paint.Style.STROKE); mStrokePaint.setTextAlign(Paint.Align.CENTER); } private final void setupAttributes(Context context, AttributeSet attrs) { final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OutlinedTextView); mOutlineColor = array.getColor( R.styleable.OutlinedTextView_outlineColor, 0x00000000); array.recycle(); // Force this text label to be centered super.setGravity(Gravity.CENTER_HORIZONTAL); } } 
+4
source

I turned off some of these examples for a while, because no one seemed to line up completely correctly, and as soon as I finally understood what was happening with the text and put my math hat on, I changed my onDraw for this to be the following, and it’s fine sits regardless of the size of the text or the size and shape that contain the view ...

 @Override protected void onDraw(Canvas canvas) { if (!isInEditMode()){ // Get the text to print final float textSize = super.getTextSize(); final String text = super.getText().toString(); // setup stroke mStrokePaint.setColor(mOutlineColor); mStrokePaint.setStrokeWidth(textSize * mOutlineSize); mStrokePaint.setTextSize(textSize); mStrokePaint.setFlags(super.getPaintFlags()); mStrokePaint.setTypeface(super.getTypeface()); // draw everything canvas.drawText(text, (this.getWidth()-mStrokePaint.measureText(text))/2, this.getBaseline(), mStrokePaint); } super.onDraw(canvas); } 

It turned out that this is much less math and rect computing than the many solutions that are used too.

Edit: Forgot to mention that I copy textalign super in initialization and DO NOT force it to center. The drawText value calculated here will always be a correctly centered position for vowel text.

+3
source

I tried to get it to work for a while, and I have a solution, but this is only for a special case! It is possible to get a Layout object that is used inside the TextView to draw text. You can create a copy of this object and use it inside the onDraw(Canvas) method.

  final Layout originalLayout = super.getLayout(); final Layout layout = new StaticLayout(text, mStrokePaint, originalLayout.getWidth(), originalLayout.getAlignment(), originalLayout.getSpacingMultiplier(), originalLayout.getSpacingAdd(), true); canvas.save(); canvas.translate( layout.getLineWidth(0) * 0.5f, 0.0f ); layout.draw(canvas); canvas.restore(); 

But I'm sure this is not the best way to draw outlines. I do not know how to track changes in the TextView.getLayout() object. Also it does not work for multi-line TextView and different gravitations. And ultimately, this code has very poor performance because it allocates a Layout object for every draw. I do not understand how this works, so I would prefer not to use it.

+1
source

There are several attributes in the TextView class, for example android:shadowColor , android:shadowDx , android:shadowDy and android:shadowRadius . It seems to me that they are doing the same thing that you want to implement. So maybe try a simple TextView .

0
source

All Articles