37

I'm trying to draw some text onto an MapView on Android. The drawing of the text goes fine, but it's very hard to read the text because it's white with no black border (like the rest of the text that appears naturally on MapViews to denote cities, states, and countries). I can't seem to figure how to draw the text with a black border. Anyone know how to do this?

This is the sort of code I'm using right now (this is just example code, found in one of my overlays):

@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    Paint textPaint = new Paint();
    textPaint.setARGB(255, 255, 255, 255);
    textPaint.setTextAlign(Paint.Align.CENTER);
    textPaint.setTextSize(16);
    textPaint.setTypeface(Typeface.DEFAULT_BOLD);

    canvas.drawText("Some Text", 100, 100, textPaint);

    super.draw(canvas, mapView, shadow);
}
Dan Lew
  • 85,990
  • 32
  • 182
  • 176

4 Answers4

68

The easiest way to do this is with a Stroke... something like this:

@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    Paint strokePaint = new Paint();
    strokePaint.setARGB(255, 0, 0, 0);
    strokePaint.setTextAlign(Paint.Align.CENTER);
    strokePaint.setTextSize(16);
    strokePaint.setTypeface(Typeface.DEFAULT_BOLD);
    strokePaint.setStyle(Paint.Style.STROKE);
    strokePaint.setStrokeWidth(2);

    Paint textPaint = new Paint();
    textPaint.setARGB(255, 255, 255, 255);
    textPaint.setTextAlign(Paint.Align.CENTER);
    textPaint.setTextSize(16);
    textPaint.setTypeface(Typeface.DEFAULT_BOLD);

    canvas.drawText("Some Text", 100, 100, strokePaint);
    canvas.drawText("Some Text", 100, 100, textPaint);

    super.draw(canvas, mapView, shadow);
}

This will draw a border of 2 pixels around the outside of the text then draw the text over the top of it, giving you the illusion of an outline.

Also, it may be worth setting the Paints up in the constructor then just reusing them.

Jeremy Logan
  • 47,151
  • 38
  • 123
  • 143
  • 1
    Ah, wonderful! A good solution. Also, I already do setup the Paints in the constructor and reuse; I just simplified my example above for explanatory purposes. – Dan Lew Jan 28 '10 at 15:26
  • 1
    I figured (you don't seem dumb :) ). Mostly just adding that for posterity. – Jeremy Logan Jan 28 '10 at 17:33
  • @fiXedd But how am I suppose to override a View that is declared in the xml file? – The Student Jul 29 '10 at 21:01
  • 2
    @Tom you can include your own Views in xml. Instead of you can do – Jeremy Logan Aug 02 '10 at 17:54
  • 3
    I would throw in a .setAntiAlias(true); as well for each Paint object. – Someone Somewhere Feb 23 '11 at 21:05
  • With the correct colors this effect is really awesome, thanks! – Simon Forsberg Jul 16 '13 at 22:42
  • Please note there is a [**bug with strokes in Android 4.4**](https://code.google.com/p/android/issues/detail?id=62800). If the text size if above 256 pixels it results in very weird stroke rendering. A workaround is to draw the outline/stroke with the alternative method [**presented in this answer**](http://stackoverflow.com/a/5817510/708906). – Tony Chan Jun 12 '14 at 01:48
19

Instead of this code (from the first answer)

canvas.drawText("Some Text", 100, 100, strokePaint);
canvas.drawText("Some Text", 100, 100, textPaint);

try to use the same with Path:

Path path = new Path();
String text = "Some Text";
textPaint.getTextPath(text, 0, text.length(), 0, 100, path);
canvas.drawPath(path, strokePaint);
canvas.drawPath(path, textPaint);

looks better?

biddulph.r
  • 5,226
  • 3
  • 32
  • 47
Oleg
  • 191
  • 1
  • 2
  • I didn't know about getTextPath, that's pretty cool! (Though I don't think I'd use it for this particular case.) – Josh Jul 07 '11 at 13:07
  • 1
    +1 because this alternative method to drawing a text outline is the only good workaround to a **[bug that was introduced in Android 4.4](https://code.google.com/p/android/issues/detail?id=62800)** (KitKat) and is still present in 4.4.3. @Oleg your answer predates the introduction of the bug, but you have inadvertently saved my sanity. Thank you. – Tony Chan Jun 12 '14 at 01:43
  • 3
    @Oleg you should tell something about "tp" data type... it's a Paint object. Sounds weird to me... What does Paint object has to do with text and Path? – j.c Apr 04 '16 at 16:24
  • Definitely this solution looks better! – Damir Aug 18 '16 at 08:58
  • What is variable `tp` when you call `tp.getTextPath()`? – Seth Jun 22 '18 at 02:33
11

The half-answer, which may or may not be good enough (it was in my case), is to set a shadow:

textPaint.setShadowLayer(3, 0, 0, Color.BLACK);

The shadow helps the text stand out a lot, but isn't quite as good as a black border would be. I'm still curious how to solve the original question.

Dan Lew
  • 85,990
  • 32
  • 182
  • 176
2

This is a complete shot in the dark and there might be a better way, but if you create 4 copies of the text, set their color to black, then shift each layer by 1 pixel diagonally, it would create an illusion of a border. So if your text is positioned at [100,100], the 4 shadows would need to be positioned at [99,99], [99,101], [101,99] and [101,101], like this:

canvas.drawText("Some Text", 99, 99, borderPaint);
canvas.drawText("Some Text", 99, 101, borderPaint);
canvas.drawText("Some Text", 101, 99, borderPaint);
canvas.drawText("Some Text", 101, 101, borderPaint);

canvas.drawText("Some Text", 100, 100, textPaint);
Aleksander Kmetec
  • 3,287
  • 2
  • 24
  • 19