So you want a stroke around the textview? Unfortunately there is no simple way to do it with the styling. You'll have to create another view and place your textview over-top, making the parent view (the one it's on top of) just a few pixels bigger - this should create an outline.
You can put a shadow behind the text, which can often help readability. Try experimenting with 50% translucent black shadows on your green text. Details on how to do this are over here: Android - shadow on text?
I've just been trying to figure out how to do this and couldn't find a good guide online but eventually figured it out. As Steve Pomeroy suggested, you do have to do something more involved. In order to get the outlined text effect, you draw the text twice: once with a thick outline and then the second time we draw the main text over the outline. But, the task is made easier because you can very easily adapt one of the code samples provided with the SDK, namely the one under this name in your SDK directory: "/samples/android-/ApiDemos/src/com/example/android/apis/view/LabelView.java". Which can also found on the Android developer website here.
Depending on what you're doing, it's very easy to see you will only need to make minor modifications to that code, such as changing it to extend from TextView, etc. Before I discovered this sample I forgot to override onMeasure() (which you must do in addition to overriding onDraw() as is mentioned in the "Building Custom Components" guide on the Android Developer website), which is part of why I was having trouble.
Once you've done that, you can do what I did:
public class TextViewOutline extends TextView {
private Paint mTextPaint;
private Paint mTextPaintOutline; //add another paint attribute for your outline
...
//modify initTextViewOutline to setup the outline style
private void initTextViewOutline() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaintOutline = new Paint();
mTextPaintOutline.setAntiAlias(true);
mTextPaintOutline.setTextSize(16);
mTextPaintOutline.setColor(0xFF000000);
mTextPaintOutline.setStyle(Paint.Style.STROKE);
mTextPaintOutline.setStrokeWidth(4);
setPadding(3, 3, 3, 3);
}
...
//make sure to update other methods you've overridden to handle your new paint object
...
//and finally draw the text, mAscent refers to a member attribute which had
//a value assigned to it in the measureHeight and Width methods
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
mTextPaintOutline);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
}
So, in order to get the outlined text effect, you draw the text twice: once with a thick outline and then the second time we draw the main text over the outline.
The framework supports text-shadow but does not support text-outline. But there is a trick: shadow is something that is translucent and fades. Redraw the shadow a couple of times and all the alpha gets summed up and the result is an outline.
A very simple implementation extends TextView and overrides the draw(..) method. Every time a draw is requested our subclass does 5-10 drawings.
public class OutlineTextView extends TextView {
// Constructors
@Override
public void draw(Canvas canvas) {
for (int i = 0; i < 5; i++) {
super.draw(canvas);
}
}
}
<OutlineTextView
android:shadowColor="#000"
android:shadowRadius="3.0" />
It is quite an old question but still I don't see any complete answers. So I am posting this solution, hoping that someone struggling with this problem might find it useful. The simplest and most effective solution is to override TextView class' onDraw method. Most implementations I have seen use drawText method to draw the stroke but that approach doesn't account for all the formatting alignment and text wrapping that goes in. And as a result often the stroke and text end up at different places. Following approach uses super.onDraw to draw both the stroke and fill parts of the text so you don't have to bother about rest of the stuff. Here are the steps
Extend TextView class
Override onDraw method
Set paint style to FILL
call parent class on Draw to render text in fill
mode.
save current text color.
Set current text color to your stroke color
Set paint style to Stroke
Set stroke width
And call parent class onDraw again to draw the stroke over the
previously rendered text.
package com.example.widgets;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.Button;
public class StrokedTextView extends Button {
private static final int DEFAULT_STROKE_WIDTH = 0;
// fields
private int _strokeColor;
private float _strokeWidth;
// constructors
public StrokedTextView(Context context) {
this(context, null, 0);
}
public StrokedTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if(attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.StrokedTextAttrs);
_strokeColor = a.getColor(R.styleable.StrokedTextAttrs_textStrokeColor,
getCurrentTextColor());
_strokeWidth = a.getFloat(R.styleable.StrokedTextAttrs_textStrokeWidth,
DEFAULT_STROKE_WIDTH);
a.recycle();
}
else {
_strokeColor = getCurrentTextColor();
_strokeWidth = DEFAULT_STROKE_WIDTH;
}
//convert values specified in dp in XML layout to
//px, otherwise stroke width would appear different
//on different screens
_strokeWidth = dpToPx(context, _strokeWidth);
}
// getters + setters
public void setStrokeColor(int color) {
_strokeColor = color;
}
public void setStrokeWidth(int width) {
_strokeWidth = width;
}
// overridden methods
@Override
protected void onDraw(Canvas canvas) {
if(_strokeWidth > 0) {
//set paint to fill mode
Paint p = getPaint();
p.setStyle(Paint.Style.FILL);
//draw the fill part of text
super.onDraw(canvas);
//save the text color
int currentTextColor = getCurrentTextColor();
//set paint to stroke mode and specify
//stroke color and width
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(_strokeWidth);
setTextColor(_strokeColor);
//draw text stroke
super.onDraw(canvas);
//revert the color back to the one
//initially specified
setTextColor(currentTextColor);
} else {
super.onDraw(canvas);
}
}
/**
* Convenience method to convert density independent pixel(dp) value
* into device display specific pixel value.
* @param context Context to access device specific display metrics
* @param dp density independent pixel value
* @return device specific pixel value.
*/
public static int dpToPx(Context context, float dp)
{
final float scale= context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
That is all. This class uses custom XML attributes to enable specifying stroke color and width from the XML layout files. Therefore, you need to add these attributes in your attr.xml file in subfolder 'values' under folder 'res'. Copy and paste the following in your attr.xml file.
Once you are done with that, you can use the custom StrokedTextView class in your XML layout files and specify stroke color and width as well. Here is an example
Remember to replace package name with your project's package name. Also add the xmlns namespace in the layout file in order to use custom XML attributes. You can add the following line in your layout file's root node.
MagicTextView is very useful to make stroke font, but in my case, it cause error like
this
this error caused by duplication background attributes which set by MagicTextView
so you need to edit attrs.xml and MagicTextView.java
I have created a library based on Nouman Hanif's answer with some additions. For example, fixing a bug that caused an indirect infinite loop on View.invalidate() calls.
OTOH, the library also supports outlined text in EditText widgets, as it was my real goal and it needed a bit more work than TextView.
I found simple way to outline view without inheritance from TextView.
I had wrote simple library that use Android's Spannable for outlining text.
This solution gives possibility to outline only part of text.
I want to add a solution in order to solve the performance issue. For example, the answer of @YGHM and a few others does the job, but it causes infinite call of onDraw because setTextColor calls invalidate(). So in order to solve it, you also need to override invalidate() and add a variable isDrawing that you will set to true, when onDraw() is in progress and drawing with a stroke. invalidate will return if the variable is true.
override fun invalidate() {
if (isDrawing) return
super.invalidate()
}
and now you will be able to set the stroke widht by app:stroke while retaining all other desirable properties of TextView. my solution only draws the stroke w/o a fill. this makes it a bit simpler than the others. bellow a screencapture with the result while setting a custom font to my customtextview.