在调用 repaint() 之前,paintComponent 不会绘制所有内容
paintComponent not drawing everything until repaint() is called
我正在研究国际象棋引擎,但遇到以下问题:除非调用 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
}
}
提前致谢!
问题在于您没有提供 drawImage
方法调用的 ImageObserver
参数。
我可以重现您在 MRE 中看到的错误。
我曾通过 Toolkit.getDefaultToolkit().getImage("...");
(或 Toolkit.getDefaultToolkit().createImage("...");
)阅读图像,但有时如果您不提供 ImageObserver
或不要求example getWidth(null)
在绘画之前在图像上,以及其他我不知道原因的症状。但我所知道的是,如果您提供 ImageObserver
参数,那么它将起作用。
请记住,Component
是一个 ImageObserver
... 所以您需要实际提供 Component
(Graphics
对象所属的对象)到 drawImage
最后一个参数。
因此,您可以更改 Piece#draw
方法以接受绘制它的 Component
,如下所示:
public void draw(Graphics g, final Component observer) {
g.drawString(type, co_x_draw(), co_y_draw());
g.drawImage(image, co_x_draw(), co_y_draw(), piece_size, piece_size, observer);
}
然后记得正确调用它,像这样更改 ChessBoard#paintComponent
:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Square square : squares) {
square.draw(g);
}
piece.draw(g, this);
}
另一种通常更正错误的方法,即使使用 null
作为 ImageObserver
也是使用 ImageIO#read
方法将图像读入为 BufferedImage
。同样,我不知道为什么会这样,但确实如此。我也没有用 ImageIO#read
测试你的情况,但我仍然用 Toolkit.getDefaultTooklit().getImage("...");
测试它并且它有效(但你需要提供 ImageObserver
参数)。
一些建议:
在我看来,在 JPanel
中放置一些 JLabel
和 GridLayout
(例如 new GridLayout(0, 8);
)然后设置会简单得多图像 Icon
s(通过 myLabel.setIcon(new ImageIcon(myImage));
)到 JLabel
s...
我正在研究国际象棋引擎,但遇到以下问题:除非调用 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
}
}
提前致谢!
问题在于您没有提供 drawImage
方法调用的 ImageObserver
参数。
我可以重现您在 MRE 中看到的错误。
我曾通过 Toolkit.getDefaultToolkit().getImage("...");
(或 Toolkit.getDefaultToolkit().createImage("...");
)阅读图像,但有时如果您不提供 ImageObserver
或不要求example getWidth(null)
在绘画之前在图像上,以及其他我不知道原因的症状。但我所知道的是,如果您提供 ImageObserver
参数,那么它将起作用。
请记住,Component
是一个 ImageObserver
... 所以您需要实际提供 Component
(Graphics
对象所属的对象)到 drawImage
最后一个参数。
因此,您可以更改 Piece#draw
方法以接受绘制它的 Component
,如下所示:
public void draw(Graphics g, final Component observer) {
g.drawString(type, co_x_draw(), co_y_draw());
g.drawImage(image, co_x_draw(), co_y_draw(), piece_size, piece_size, observer);
}
然后记得正确调用它,像这样更改 ChessBoard#paintComponent
:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Square square : squares) {
square.draw(g);
}
piece.draw(g, this);
}
另一种通常更正错误的方法,即使使用 null
作为 ImageObserver
也是使用 ImageIO#read
方法将图像读入为 BufferedImage
。同样,我不知道为什么会这样,但确实如此。我也没有用 ImageIO#read
测试你的情况,但我仍然用 Toolkit.getDefaultTooklit().getImage("...");
测试它并且它有效(但你需要提供 ImageObserver
参数)。
一些建议:
在我看来,在 JPanel
中放置一些 JLabel
和 GridLayout
(例如 new GridLayout(0, 8);
)然后设置会简单得多图像 Icon
s(通过 myLabel.setIcon(new ImageIcon(myImage));
)到 JLabel
s...