解决Java GUI绘制文字在圆心居中的问题

1、问题描述

需求:使用Graphics类提供的实例方法drawString将文字绘制在圆心并居中显示。

问题:调用drawString实例方法所传入的x,y坐标是圆心的坐标,但绘制出的文字并不会在圆心居中显示,而是在圆心右上方显示。

代码如下所示:

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class DrawStringDemo extends Frame
{
    public DrawStringDemo()
    {
        setSize(300, 300);
        setVisible(true);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }

    @Override
    public void paint(Graphics g) {
        int startX=50,startY=50,width=60,height=60,fontSize=30;
        Font font = new Font("宋体", Font.BOLD, fontSize);
        String text="馬";

        // 画圆
        g.fillOval(startX,startY,width,height);

        // 为方便观察圆心所在的位置,此处过圆心点画一个十字线
        g.setColor(Color.RED);
        g.setFont(font);
        g.drawLine(startX+width/2,startY,startX+width/2,height*2);
        g.drawLine(startX-20,startY+height/2,startX+width+20,startY+height/2);

        // 在圆心绘制文字
        g.drawString(text,startX+width/2,startY+height/2);
    }

    public static void main(String[] args) {
        new DrawStringDemo();
    }
}

运行结果如下图所示:

2、解决方法

通过分析,发现drawString在画字时的轨迹是在(x,y)点右侧从上往下绘制,因此若要实现让字体在圆心居中显示需计算出字的高度和宽度,并分别让圆心的x轴减去字的宽度的一半,圆心的y轴加上字的高度的大概三分之一,这样做的目的是为了让文字从圆心点右上角往中心点靠拢,从而实现文字在圆心居中

计算字的高度和宽度,需要使用到FontMetrics类提供的实例方法stringWidth()和getHeight(),调用Graphics提供的实例方法getFontMetrics()可以获得FontMetrics类的实例。因此,改造后的代码如下:

@Override
public void paint(Graphics g) {
    int startX=50,startY=50,width=60,height=60,fontSize=30;
    Font font = new Font("宋体", Font.BOLD, fontSize);
    String text="馬";

    // 画圆
    g.fillOval(startX,startY,width,height);

    // 为方便观察圆心所在的位置,此处过圆心点画一个十字线
    g.setColor(Color.RED);
    g.setFont(font);
    g.drawLine(startX+width/2,startY,startX+width/2,height*2);
    g.drawLine(startX-20,startY+height/2,startX+width+20,startY+height/2);

    // 在圆心绘制文字前先计算字的高度和宽度
    FontMetrics metrics = g.getFontMetrics();
    int textWidth=metrics.stringWidth(text);
    int textHeight=metrics.getHeight();
    System.out.println("textWidth="+textWidth);
    System.out.println("textHeight="+textHeight);
    //g.drawString(text,startX+width/2,startY+height/2);
    g.drawString(text,startX+width/2-textWidth/2,startY+height/2+textHeight/3);
}

改造后运行结果如下图所示:

提示:

1、圆心的平面坐标计算方法:X坐标是startX+width/2,Y坐标是startY+height/2

2、width与height相等时为正圆,反之为椭圆
3、g.drawOval画空心圆
4、g.fillOval画实心圆

5、C#和Java的GUI画圆时都是方中圆,也就是当确定矩阵的左上角XY坐标和圆的直径时就可以确定圆在平面上的大小和位置。

2、应用场景

2.1 使用GUI绘制中国象棋

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;

public class Xiangqi extends Frame {
    // 窗体宽度
    private final int FORM_WIDTH=600;
    // 窗体高度
    private final int FORM_HEIGHT=700;
    // 棋子直径
    private final int DIAMETER=60;

    // 棋盘棋子位置表
    private int[][] _positionTable={
        // 黑方
        {2,3,6,5,1,5,6,3,2},
        {0,0,0,0,0,0,0,0,0},
        {0,4,0,0,0,0,0,4,0},
        {7,0,7,0,7,0,7,0,7},
        {0,0,0,0,0,0,0,0,0},
        // 红方
        {0,0,0,0,0,0,0,0,0},
        {14,0,14,0,14,0,14,0,14},
        {0,11,0,0,0,0,0,11,0},
        {0,0,0,0,0,0,0,0,0},
        {9,10,13,12,8,12,13,10,9}
    };

    // 棋子名称
    private String[] _stoneNameArray={"帅","車","馬","炮","士","象","兵","将","車","马","砲","仕","相","卒"};

    public Xiangqi()
    {
        setSize(FORM_WIDTH, FORM_HEIGHT);
        setVisible(true);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }

    public void paint(Graphics g)
    {
        // 画棋盘
        drawBoard(g);
        // 画棋子
        drawStone(g);
    }

   
    private void drawBoard(Graphics g){
        // 画棋盘的行
        for (int i = 1; i <=10; i++) {
            g.drawLine(DIAMETER, i*DIAMETER, 9*DIAMETER, i*DIAMETER);
        }

        // 画棋盘的列
        for (int i = 1; i <= 9; i++)
        {
            if (i == 1 || i == 9)
            {
                g.drawLine(i * DIAMETER, DIAMETER, i *DIAMETER, 10 * DIAMETER);
            }
            else
            {
                g.drawLine(i * DIAMETER, DIAMETER,i * DIAMETER, 5*DIAMETER);
                g.drawLine( i * DIAMETER, 6 * DIAMETER, i * DIAMETER, 10 * DIAMETER);
            }
        }

        // 画黑方九宫格
        g.drawLine(4 * DIAMETER, DIAMETER, 6 * DIAMETER, 3 * DIAMETER);
        g.drawLine(6 * DIAMETER, DIAMETER, 4 * DIAMETER, 3 * DIAMETER);

        // 画红方九宫格
        g.drawLine(4 * DIAMETER, 8 * DIAMETER, 6 * DIAMETER, 10 * DIAMETER);
        g.drawLine( 6 * DIAMETER, 8 * DIAMETER, 4 * DIAMETER, 10 * DIAMETER);

        // 画楚河汉界
        Graphics2D g2 = (Graphics2D)g;
        AffineTransform affineTransform = new AffineTransform();
        // 逆时针旋转90度
        affineTransform.rotate(Math.toRadians(-90), 0, 0);
        Font font = new Font("宋体", Font.BOLD, 40);
        Font rotatedFont = font.deriveFont(affineTransform);
        g2.setFont(rotatedFont);
        g2.drawString("楚",3*DIAMETER,5*DIAMETER+DIAMETER/2+20);
        g2.drawString("河",3*DIAMETER+40,5*DIAMETER+DIAMETER/2+20);

        // 顺时针旋转90度
        affineTransform = new AffineTransform();
        affineTransform.rotate(Math.toRadians(90), 0, 0);
        rotatedFont = font.deriveFont(affineTransform);
        g2.setFont(rotatedFont);
        g2.drawString("界",6*DIAMETER,5*DIAMETER+10);
        g2.drawString("漢",6*DIAMETER+40,5*DIAMETER+10);
    }

    private void drawStone(Graphics g)
    {
        for (int i = 0; i < 10; i++)
        {
            for (int j = 0; j < 9; j++)
            {
                int stoneId=_positionTable[i][j];
                if (stoneId==0) {
                    continue;
                }
                String text=_stoneNameArray[stoneId-1];
                int x=(j + 1)*DIAMETER-DIAMETER/2;
                int y=(i + 1)*DIAMETER-DIAMETER/2;

                g.drawOval(x,y, DIAMETER,DIAMETER);
                if(isBack(stoneId))
                    g.setColor(new Color(255,248,172));
                else
                    g.setColor(new Color(251,219,166));
                g.fillOval(x,y, DIAMETER, DIAMETER);
                //Graphics2D g2d = (Graphics2D)g;
                Font font = new Font("宋体", Font.BOLD, 45);
                g.setFont(font);
                if(isBack(stoneId))
                    g.setColor(new Color(30,30,26));
                else
                    g.setColor(new Color(174,30,4));
                // 获取FontMetrics对象
                FontMetrics metrics = g.getFontMetrics();
                // 计算文本的中心位置
                int textWidth = metrics.stringWidth(text);
                int textHeight = metrics.getHeight();
                g.drawString(text,(j + 1)*DIAMETER-textWidth/2,(i + 1)*DIAMETER+textHeight/3);
            }
        }
    }

    // 是否黑方
    private static boolean isBack(int stoneId)
    {
        return stoneId>=1 && stoneId<=7;
    }

    public static void main(String[] args)
    {
        new Xiangqi();
    }
}

运行效果如下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值