概述
本文旨在记录Java代码初学者的学习历程并表达本人对代码的理解。
本文的内容包括:以设计简易五子棋程序为具体需求,解决代码的延时执行,网格吸附,实心圆的绘制等问题。
1.代码的延时执行
在设计五子棋的UI窗口时,我们常这样写:
import javax.swing.*;
import java.awt.*;
public class Window {
public int x0=40;
public int y0=40;
public int wid=40;
public int line=15;
public void Win() {
JFrame jframe=new JFrame();
jframe.setSize(1000,700);
jframe.setTitle("五子棋");
jframe.setDefaultCloseOperation(3);
jframe.setLocationRelativeTo(null);
rePaint repaint=new rePaint();
jframe.add(repaint, BorderLayout.CENTER);
repaint.setBackground(Color.blue);
Listener listener=new Listener();
repaint.addMouseListener(listener);
jframe.setVisible(true);
Graphics g=repaint.getGraphics();
listener.g=g;
for (int i=0;i<line;i++){
g.drawLine(x0,y0+wid*i,x0+wid*(line-1),y0+wid*i);
g.drawLine(x0+wid*i,y0,x0+wid*i,y0+wid*(line-1));
}
}
public static void main(String[] args) {
Window window=new Window();
window.Win();
}
}
我们希望这段代码能实现五子棋窗口和棋盘。但运行后往往得不到棋盘,它受Java代码的执行机制影响。当代码执行到jframe.setVisible(true);时,代码会分为两条支线同时执行,一支接着往下,即绘制棋盘;另一支跳转到组件的paint方法,将组件本身绘制出来。当棋盘绘制成功的时间早于组件绘制的时间时,即程序先绘制棋盘再绘制组件时,棋盘就会消失。因此我们需要棋盘后于组件绘制,延时执行就是一种办法。具体方法如下:
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
其中的100代表延迟100ms再往下执行。由于现代计算机的运行速度偏快,这个数字通常较小。
2.实心圆的绘制与网格吸附
解决上面的问题后,我们会得到这样的窗口:

下一步,我们会完成棋子的绘制和让棋子仅能放在棋盘线条交汇处的需求。
棋子是个实心圆,Java的Graphics类自带实心圆的直接绘制方法,如下:
Graphics g=jframe.getGraphics();
g.fillOval( , , , );
fillOval()中,前两个位置是圆外接正方形的左上角的坐标,后两个分别是宽和高,因为我们要绘制圆,它们现在就是圆的直径。
随后,我们要让棋子吸附在最近的交点上。其核心思想是判断鼠标点击的位置与周围交点的距离,取最近的那个。
我们会采用取余数的方法分别判断x和y。取出鼠标点击点的x与y,然后用x-棋盘与窗口左边界的距离,再用得到的数对棋盘线条间隔取余,若余数大于间隔的1/2,则可说明鼠标点击点偏右。y方向上用同样的方法。
随后,我们用鼠标点击点的x-棋盘与窗口左边界的距离,再除以线条间距,可得到一个整数。这其中用到了前一篇笔记提到的Java中整型计算只会保留整数部分的特性。再根据前面提到的判断偏左偏右的方法,对这个整数进行+1或不变的操作,最后就能得到棋子的实际落点与棋盘最左边间所差的点的数量。
最后,利用这个数量,换算出棋子期望落点的坐标。
最后的最后,因为fillOval()中,前两个位置是圆外接正方形的左上角的坐标,而我们需要圆的圆心在交点上,因此需要让这两个坐标减去一个半径。
3.完善与补充
完成涉及到新知识的部分后,利用前面学过的知识实现paint重写,黑白棋子交替后,即可完成五子棋程序的初步设计。
import javax.swing.*;
import java.awt.*;
public class Window {
public int x0=40;
public int y0=40;
public int wid=40;
public int line=15;
public void Win() {
JFrame jframe=new JFrame();
jframe.setSize(1000,700);
jframe.setTitle("五子棋");
jframe.setDefaultCloseOperation(3);
jframe.setLocationRelativeTo(null);
rePaint repaint=new rePaint();
jframe.add(repaint, BorderLayout.CENTER);
repaint.setBackground(Color.blue);
Listener listener=new Listener();
repaint.addMouseListener(listener);
jframe.setVisible(true);
Graphics g=repaint.getGraphics();
listener.g=g;
try {
Thread.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i=0;i<line;i++){
g.drawLine(x0,y0+wid*i,x0+wid*(line-1),y0+wid*i);
g.drawLine(x0+wid*i,y0,x0+wid*i,y0+wid*(line-1));
}
}
public static void main(String[] args) {
Window window=new Window();
window.Win();
}
}
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.ImageObserver;
import java.text.AttributedCharacterIterator;
public class Listener extends MouseAdapter {
public static int x0=40,y0=40,wid=40,line=15;
public int ClickX,ClickY,ActuX,ActuY;
public Graphics g;
Boolean p=true;
Store[] SArray=new Store[9999];
int index=0;
public void mouseClicked(MouseEvent e) {
Store St=new Store();
ClickX=e.getX();
ClickY=e.getY();
int a,b;
if((ClickX-x0)%wid>wid/2){
a=(ClickX-x0)/wid+1;
System.out.println(a);
}
else{
a=(ClickX-x0)/wid;
System.out.println(a);
}
if((ClickY-y0)%wid>wid/2){
b=(ClickY-y0)/wid+1;
}
else{
b=(ClickY-y0)/wid;
}
ActuX=x0+a*wid;
ActuY=y0+b*wid;
St.ActuX=ActuX;
St.ActuY=ActuY;
if (p){
g.setColor(Color.black);
}
if(p==false){
g.setColor(Color.white);
}
St.p=p;
g.fillOval(ActuX-wid/4,ActuY-wid/4,wid/2,wid/2);
if(p==true){
p=false;
}
else {
p=true;
}
rePaint.SArray2[index]=St;
index++;
}
}
import javax.swing.*;
import java.awt.*;
public class rePaint extends JPanel {
public static int x0=40,y0=40,wid=40,line=15;
public static Store[] SArray2=new Store[9999];
public void paint(Graphics g) {
super.paint(g);
for (int i=0;i<line;i++){
g.drawLine(x0,y0+wid*i,x0+wid*(line-1),y0+wid*i);
g.drawLine(x0+wid*i,y0,x0+wid*i,y0+wid*(line-1));
}
for (int i=0;i<9999;i++){
if(SArray2[i]==null){
break;
}
Store s=SArray2[i];
s.way(g);
}
}
}
import java.awt.*;
public class Store {
public static int x0=40,y0=40,wid=40,line=15;
public int ActuX,ActuY;
public Graphics g;
Boolean p;
public void way(Graphics g) {
if (p){
g.setColor(Color.black);
}
if(p==false){
g.setColor(Color.white);
}
g.fillOval(ActuX-wid/4,ActuY-wid/4,wid/2,wid/2);
}
}
在写这次代码的时候,我对用数组实现paint重绘的操作有了更深的理解。这种操作实际用一个类/对象保留了大量作为整体的数据和方法,然后在paint中一一调用数组中所有对象里的方法。


