1

I am trying to programmatically draw a parking icon to place as the drawable for an itemized overlay on a map.

The icon consists of a blue square with a white 'P' in its centre of which I would like to programmatically change the colour of the square to denote different parking types.

I've tried creating it via the canvas using drawRect & drawText but I cannot find a simple way of centering the text in the square and I cannot find a way to center the canvas on the coordinates - it keeps wanting to anchor from the top left hand corner.

I've alternatively tried creating an XML layout to convert to a drawable but cannot achieve this either.

Is there an elegant solution for what I am trying to achieve?

kaiz.net
  • 1,984
  • 3
  • 23
  • 31
Azza
  • 41
  • 1
  • 6

2 Answers2

3
public class TextDrawable extends Drawable {

    private final static int    TEXT_PADDING           = 3;
    private final static int    ROUNDED_RECT_RADIUS    = 5;

    private final String    text;
    private final Paint     textPaint;
    private final Rect      textBounds;
    private final Paint     bgPaint;
    private final RectF     bgBounds;

    public TextDrawable(String text, String backgroundColor, int textHeight) {

        this.text = text;

        // Text
        this.textPaint = new Paint();
        this.textBounds = new Rect();
        textPaint.setColor(Color.WHITE);
        textPaint.setARGB(255, 255, 255, 255);
        textPaint.setAntiAlias(true);
        textPaint.setSubpixelText(true);
        textPaint.setTextAlign(Paint.Align.CENTER); // Important to centre horizontally in the background RectF
        textPaint.setTextSize(textHeight);
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);
        // Map textPaint to a Rect in order to get its true height
        // ... a bit long-winded I know but unfortunately getTextSize does not seem to give a true height!
        textPaint.getTextBounds(text, 0, text.length(), textBounds);

        // Background
        this.bgPaint = new Paint();
        bgPaint.setAntiAlias(true);
        bgPaint.setColor(Color.parseColor(backgroundColor));
        float rectHeight  = TEXT_PADDING * 2 + textHeight;
        float rectWidth   = TEXT_PADDING * 2 + textPaint.measureText(text);
        //float rectWidth   = TEXT_PADDING * 2 + textHeight;  // Square (alternative)
        // Create the background - use negative start x/y coordinates to centre align the icon
        this.bgBounds = new RectF(rectWidth / -2, rectHeight / -2, rectWidth / 2, rectHeight / 2);
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawRoundRect(bgBounds, ROUNDED_RECT_RADIUS, ROUNDED_RECT_RADIUS, bgPaint);
        // Position the text in the horizontal/vertical centre of the background RectF
        canvas.drawText(text, 0, (textBounds.bottom - textBounds.top)/2, textPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        bgPaint.setAlpha(alpha);
        textPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        bgPaint.setColorFilter(cf);
        textPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}
Azza
  • 41
  • 1
  • 6
0

Make several png images & put them in res\drawable, if you have more than like 5 colors then think about using less. It's confusing for the user.

zapl
  • 63,179
  • 10
  • 123
  • 154
  • Yes I agree with more then 5 colors being to confusing for the user - I was planning to have each parking type able to be turned on/off individually in the app's settings. It will also be used with other letters to denote other map features (eg. toilets, drinking fountains, etc). I was really trying to avoid duplicating icons with different colours - particularly since it is just text on a coloured background. – Azza Mar 23 '12 at 01:22
  • You can certainly create your own icons in code but it is much easier to do it with a real image editor and chances are that you have a better looking result in the end. Also, replacing images is quite easy while code is not. And creating the image in code will even take longer than just loading an icon so your app may be slower. I'd suggest you use pngs, unless you have huge icons or want like icons for the whole Chinese alphabet. – zapl Mar 23 '12 at 08:09