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();
}
}
运行效果如下图所示: