【发布时间】:2021-03-20 14:31:19
【问题描述】:
我正在开发一个国际象棋引擎,但我遇到了以下问题:除非调用repaint(),否则我的棋子不会被绘制。但是,正方形(= 国际象棋场)看起来很好。
我读到我应该避免在paintComponent 中使用repaint(),因为这会使函数被连续调用。我怎样才能避免打电话给repaint(),但仍然绘制我的图像?
这是代码:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class ChessBoard extends JPanel {
ArrayList<Square> squares = new ArrayList<Square>();
Piece piece = new Piece("B", 0);
public ChessBoard() {
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
squares.add(new Square(i, j));
}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Square square : squares) {
square.draw(g);
}
piece.draw(g);
//repaint(); //Image only appears if this is called
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ChessBoard chessboard = new ChessBoard();
chessboard.createFrame();
}
public void createFrame() {
JFrame f = new JFrame("ChessBoard");
f.getContentPane().setPreferredSize(new Dimension(squareSize()*8, squareSize()*8));
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Chessboard");
f.setLocation((int)(screenSize().width-squareSize()*8)/2, (int)(screenSize().height-squareSize()*8)/2);
f.add(this);
f.pack();
Frame.getFrames();
f.setVisible(true);
}
public static Dimension screenSize() {
return Toolkit.getDefaultToolkit().getScreenSize();
}
public static int squareSize() {
return (int)screenSize().height/12;
}
}
class Square {
int start_x, start_y;
int square_size;
Color color;
public Square(int x, int y) {
// constructor
start_x = x*ChessBoard.squareSize();
start_y = y*ChessBoard.squareSize();
square_size = ChessBoard.squareSize();
if ((x+y)%2 == 0) {
// Color of the white squares
color = new Color(209, 192, 148);
} else {
// Color of the black squares
color = new Color(110, 83, 43);
}
}
public void draw(Graphics g) {
g.setColor(this.color);
g.fillRect(this.start_x, this.start_y, square_size, square_size);
}
}
class Piece {
String type;
int coordinate, square_size, piece_size;
int[] draw_coordinates = {7, 6, 5, 4, 3, 2, 1, 0};
Image image;
public Piece(String type, int coordinate) {
this.type = type;
this.coordinate = coordinate;
this.square_size = ChessBoard.squareSize();
this.piece_size = (int)ChessBoard.squareSize()*2/3;
this.image = Toolkit.getDefaultToolkit().getImage("src/pieces/black_bishop.png");
}
public int co_x_board() {
return coordinate - ((int)coordinate/8)*8;
}
public int co_y_board() {
return (int)coordinate/8;
}
public int co_x_draw() {
return co_x_board()*square_size+((int)square_size/6);
}
public int co_y_draw() {
return draw_coordinates[co_y_board()]*square_size+((int)square_size/6);
}
public void draw(Graphics g) {
g.drawString(type, co_x_draw(), co_y_draw()); // does work
g.drawImage(image, co_x_draw(), co_y_draw(), piece_size, piece_size, null); // does not work
}
}
提前谢谢你!
【问题讨论】:
-
我不明白你的问题。当框架显示时,棋盘的原始状态被绘制。如果您对任何 ArrayList 进行更改,那么是的,您需要调用 repaint() 来绘制新状态。发布适当的minimal reproducible example 来演示问题。另请注意,您不应该设置框架的首选尺寸。您的计算没有考虑标题栏和边框的大小。相反,您应该设置正方形的首选大小,然后 pack() 框架。
-
当我运行我的程序时,只显示正方形。但是,我希望在启动程序时显示这些片段。但是,如上所述,这些片段仅在调用 f.repaint() 后才显示出来(在这种情况下,当按下鼠标时)。我希望这可以解决问题。
-
关于首选尺寸,我是故意这样做的,因为我事先不知道标题栏的尺寸(或者还有其他更简洁的方法可以让它工作吗?)。
-
不,这并不能解决问题。您的绘画逻辑同时绘制正方形和碎片。如果这些片段没有出现,那么您有一个逻辑问题,最初没有正确设置这些片段。问题与绘画逻辑无关。
-
这正是我不明白为什么它不起作用的原因。当我在绘图组件中调用 white_pieces.size() 时,我可以清楚地看到我的作品已创建。然而,在调用 f.repaint 之前,它们不会自己绘制。我还注意到 paintComponent 在任何交互之前执行了 2 次。
标签: java swing paintcomponent