Question

I'm trying to draw a number inside of a circle and having issues with the centering on the text. The following is the xml I'm using to display the TextView

<RelativeLayout
    android:id="@+id/label"
    android:layout_width="72dip"
    android:layout_height="72dip"
    android:layout_centerVertical="true"
    android:layout_alignParentLeft="true"
    android:layout_margin="8dip" >

    <View 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/hole_background"
        android:layout_centerInParent="true"/>
    <TextView 
        android:id="@+id/hole"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textColor="@android:color/white"
        android:textSize="56sp"
        android:textStyle="bold"
        android:gravity="center"/>
</RelativeLayout>

The problem is it is just slightly more towards the bottom (by about 10 pixels on the device I tested). This is a screenshot from the view hierarchy viewer that shows what I'm talking about...

enter image description here

I've tried combining the views and using the background such as here, or the removing the font padding like suggested here but neither worked, the font padding ending up as seen below...

enter image description here

I am currently am using a negative margin to adjust it to look right but that doesn't seem like the correct way to do it. Does anyone have any ideas on how to make text centered without using negative margins that I have to manually check based upon the text size?

Thanks in advance

Was it helpful?

Solution

So based upon the comments of LairdPleng and 323go I ended up just creating a custom view. The following view will do the centering exactly based upon the height of the number being drawn...

public class Label extends View {

    private static final int TEXT_SIZE = 56;

    private String mText;

    private float mCenterX;
    private float mCenterY;
    private float mRadius;

    private Paint mCirclePaint;
    private Paint mTextPaint;
    private Rect mTextBounds;

    public HoleLabel(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupCanvasComponents(context);
    }

    public HoleLabel(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupCanvasComponents(context);
    }

    public HoleLabel(Context context) {
        super(context);
        setupCanvasComponents(context);
    }

    public void setText(String text) {
        if (!StringUtils.equals(mText, text)) {
            mText = text;
            invalidate();
        }
    }

    private void setupCanvasComponents(Context context) {
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setColor(Color.BLACK);
        mCirclePaint.setStyle(Paint.Style.FILL);

        Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/CustomFont.otf");
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
                .getDefaultDisplay().getMetrics(displayMetrics);
        float scaledDensity = displayMetrics.scaledDensity;

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(TEXT_SIZE * scaledDensity);
        mTextPaint.setTypeface(font);
        mTextPaint.setTextAlign(Align.CENTER);

        mTextBounds = new Rect();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mCenterX = w / 2.0f;
        mCenterY = h / 2.0f;
        mRadius = w / 2.0f;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Draw the background
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);

        // Draw the text
        if (mText != null) {
            mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
            canvas.drawText(mText, mCenterX, (mCenterY + mTextBounds.height() / 2), mTextPaint);
        }
    }

}

And in xml...

<com.example.widget.Label
        android:id="@+id/label"
        android:layout_width="72dip"
        android:layout_height="72dip"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:layout_margin="8dip" />

The result being...

enter image description here

I had a very specific use case so I don't know if it'll work well if with less rigid size specifications but it was the way to go for me.

OTHER TIPS

Would add this as a comment, but don't have enough reputation. Expanding on 323go's comment, you might find it easier to use images for your numbers, rather than spending a whole lot of time changing fonts and manually adjusting offsets - which may look fine on one dpi then look off again on another.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top