Search code examples
androiddrawableitemizedoverlay

Programmatically creating icon to use as ItemizedOverlay drawable - Android


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?


Solution

  • 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;
        }
    }