I'm currently working on extending a TextView, adding an outline around the text. Thus far, the only problem I've been having is my inability to position the "outline" correctly behind a text. If I code the extended class like the one portrayed below, I get a label 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);
}
}
Bah, that was stupid of me. I just needed to change-up that commented-out line:
super.getPaint().getTextBounds(text, 0, text.length(), mTextBounds);
In addition, for actually rendering the text, I need to average this view's height and the text's height:
// draw everything
canvas.drawText(text,
super.getWidth() * 0.5f, (super.getHeight() + mTextBounds.height()) * 0.5f,
mStrokePaint);
The entire code now reads 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);
}
}