Robert Smith
Robert Smith

Reputation: 457

Draw a text middle of an image android

I have to place marker on map view and write a number on marker. I have done that, but text alignment varies in different resolution. bellow is the reference code

        float xVal = (float) curScreenCoords.x;  // Point curScreenCoords
        float yVal = (float) curScreenCoords.y-20; // Point curScreenCoords
        Bitmap bitmap = BitmapFactory.decodeResource ( context.getResources() , ( R.drawable.pin_number ) ) ;
        canvas.drawBitmap(bitmap, xVal, yVal, getInnerPaint()); 

        public Paint getInnerPaint() {
         if (innerPaint == null) {
             innerPaint = new Paint();
         }
        innerPaint.setARGB(255, 117, 161, 220); // blue
        innerPaint.setAntiAlias(true);  
        innerPaint.setStyle(Style.FILL);
        return innerPaint;
        }
        canvas.drawText(String.valueOf(10), xVal+20, yVal+22, getCountPaint()); // 10 is just for example, it can vary to one digit to two to three 
        public Paint getCountPaint() {
        if (innerPaint == null) {
        innerPaint = new Paint();
        }
        innerPaint.setARGB(255, 255, 255, 255); 
        innerPaint.setAntiAlias(true);  
        innerPaint.setStyle(Style.FILL);
        innerPaint.setTextSize(12f);
        innerPaint.setTextAlign(Align.CENTER);
        return innerPaint;
       }

everything works fine, excepting text alignment, this code works fine for 480*800 resolution. Text is perfectly center aligned in canvas. x, y position is perfect on image, but this does not look perfect on 320*480. x and y position of text looks different on this resolution. Can anyone please suggest me what exactly happens wrong? is there any fundamentals for doing the same on different sized devices? Thanks in advance.

Upvotes: 3

Views: 5763

Answers (6)

Piotr
Piotr

Reputation: 3970

I had the same issue. I wanted to draw text inside bitmap - for clustering purpose on Google Maps. Actually it draws text on the CENTER of the bitmap.

Code written in Kotlin

fun createCircleBitmapWithTextInside(context: Context, @DrawableRes drawableId: Int, text: String = "POI"): Bitmap{
            val scale = context.resources.displayMetrics.density

            var bitmap = getBitmap(context, drawableId)//BitmapFactory.decodeResource(res, drawableId)
            var bitmapConfig = bitmap.config ?: Bitmap.Config.ARGB_8888
            // resource bitmaps are imutable,
            // so we need to convert it to mutable one
            bitmap = bitmap.copy(bitmapConfig, true)

            val canvas = Canvas(bitmap)
            val paint = Paint(ANTI_ALIAS_FLAG)
            paint.color = ResourceUtils.getColor(context, R.color.white)
            paint.textSize = (18 * scale)
            paint.setShadowLayer(1f, 0f, 1f, Color.WHITE)
            paint.style = Paint.Style.FILL
            paint.textAlign = Paint.Align.CENTER

            val bound = Rect()
            paint.getTextBounds(text, 0, text.length, bound)
            val x = (canvas.width.toFloat() /2)
            // x - point to the center of width
            val y = canvas.height / 2 - (paint.descent() + paint.ascent()) / 2

/*            Timber.d("bitmap.width:${canvas.width} \tbound.width:${bound.width()} \tx:$x")
            Timber.d("bitmap.height:${canvas.height} \tbound.height:${bound.height()} \ty:$y")*/

            canvas.drawText(text, x, y, paint)
            return bitmap
        }

Upvotes: 0

user1530779
user1530779

Reputation: 509

Hi I guess None of the answers given above are good enough so i post my answer try it out guys will work on all devices and is not complex at all

Canvas canvas = new Canvas(bitmap);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//paint.setTextAlign(Align.CENTER);
paint.setColor(activity.getResources().getColor(R.color.white));
paint.setTextSize(30);

// draw text to the Canvas center
Rect boundsText = new Rect();
paint.getTextBounds(String.valueOf(cluster.getMarkerList().size()), 0, 
    String.valueOf(cluster.getMarkerList().size()).length(), boundsText);
int x = (bitmap.getWidth() - boundsText.width()) / 2;
int y = (bitmap.getHeight() + boundsText.height()) / 2;

canvas.drawText(String.valueOf(cluster.getMarkerList().size()), x, y, paint);

Upvotes: 5

jlhuertas
jlhuertas

Reputation: 1477

I think you can measure the width and height that the text would have once written in the canvas and then use it to center it. Something like:

String text = "whatever";
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
canvas.drawText(text, (canvas.getWidth() - bounds.width()) / 2, (canvas.getHeight() - bounds.height()) / 2, paint);

Upvotes: 5

Blair
Blair

Reputation: 11

Make sure you check hardware acceleration is turned off. On 4.1.2 and other devices (samsung Galaxy Tag 2.0) you get a Fatal Signal 11 error.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

This corrects the problem I was having with this code. The canvas.drawText was causing the error.

Upvotes: 1

vasart
vasart

Reputation: 6702

Your values curScreenCoords.y-20 and xVal+20, yVal+22 have constant pixel offset for all resolutions, but they should depend on device's pixel density like this:

xOffset = (int) (13.33f * context.getResources().getDisplayMetrics().density + 0.5f);
yOffset = (int) (14.67f * context.getResources().getDisplayMetrics().density + 0.5f);
canvas.drawText(String.valueOf(10), xVal + xOffset, yVal + yOffset, getCountPaint());

Upvotes: 2

Amit
Amit

Reputation: 13364

You can extend the Image class and override onDraw of it. As below

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.widget.ImageView;

/**
 * @author amit
 * 
 */
public class CustomImageView extends ImageView {
    private int notificationCount;
    private Paint paint;

    /**
     * @param context
     */
    public CustomImageView(Context context) {
        super(context);
        notificationCount = 0;
        paint = new Paint();
        paint.setColor(Color.RED);
        ColorFilter cf = new ColorFilter();
        paint.setColorFilter(cf);
        paint.setStyle(Paint.Style.FILL);
        paint.setFakeBoldText(true);
        paint.setTextSize(15);
    }

    public synchronized void incrementNotification() {
        notificationCount--;
        this.invalidate();
    }

    public synchronized void decrementNotification() {
        notificationCount++;
        this.invalidate();
    }

    /**
     * @return the notificationCount
     */
    public synchronized int getNotificationCount() {
        return notificationCount;
    }

    /**
     * @param notificationCount
     *            the notificationCount to set
     */
    public synchronized void setNotificationCount(int notificationCount) {
        this.notificationCount = notificationCount;
        this.invalidate();
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.widget.ImageView#onDraw(android.graphics.Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas) {
        // System.out.println("OnDraw is called");
        super.onDraw(canvas);
        if (notificationCount == 0) {
            return;
        }
        canvas.drawText(String.valueOf(notificationCount), 0, 0, paint);
    }

}

Than You can call any of the following method

incrementNotification(); 
decrementNotification();
setNotification(int number);

Choose the color of your choice inside the constructor.. Good Luck!!

Upvotes: 0

Related Questions