简体   繁体   English

在调用 repaint() 之前,paintComponent 不会绘制所有内容

[英]paintComponent not drawing everything until repaint() is called

I am working on a chess engine, and I have the following problem: my chess pieces are not drawn unless repaint() is called.我正在研究国际象棋引擎,但遇到以下问题:除非调用repaint() ,否则不会绘制我的棋子。 However, the squares (= the chess field) appear just fine.但是,正方形(= 国际象棋场)看起来很好。 I read I should avoid using repaint() inside the paintComponent since this will make the function be called continuously.我读到我应该避免在paintComponent中使用repaint() ,因为这会使 function 被连续调用。 How can I avoid calling repaint() , but still have my image drawn?如何避免调用repaint() ,但仍然绘制我的图像? This is the code:这是代码:

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
    }
}

Thank you in advance!先感谢您!

The problem lies in the fact that that you didn't supply the ImageObserver argument of the drawImage method call.问题在于您没有提供drawImage方法调用的ImageObserver参数。

I can reproduce the error you are seeing in your MRE.我可以重现您在 MRE 中看到的错误。

I have worked with reading images via Toolkit.getDefaultToolkit().getImage("...");我曾通过Toolkit.getDefaultToolkit().getImage("...");阅读图像。 (or Toolkit.getDefaultToolkit().createImage("..."); ) and sometimes it does not work if you don't supply the ImageObserver , or don't call for example getWidth(null) on the image before painting it, as well as other symptoms, for which I don't know the cause. (或Toolkit.getDefaultToolkit().createImage("..."); ),有时如果您不提供ImageObserver ,或者在绘制图像之前不调用例如getWidth(null) ,以及其他我不知道原因的症状。 But what I do know is that if you supply the ImageObserver argument then it will work.但我所知道的是,如果您提供ImageObserver参数,那么它将起作用。

Remember, Component is an ImageObserver ... So you need to actually supply the Component (to which the Graphics object belongs at) to the drawImage last argument.请记住, Component是一个ImageObserver ...因此您需要将ComponentGraphics object 所属的)实际提供给drawImage最后一个参数。

So, you can change your Piece#draw method to accept the Component on which it is drawn, like so:因此,您可以更改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);
}

Then remember to call it properly, by changing the ChessBoard#paintComponent like so:然后记得通过像这样更改ChessBoard#paintComponent来正确调用它:

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    for (Square square : squares) {
        square.draw(g);
    }
    piece.draw(g, this);
}

Another way that usually corrects the error, even with null as the ImageObserver is to use an ImageIO#read method to read in the image as a BufferedImage .即使将null作为ImageObserver ,通常更正错误的另一种方法是使用ImageIO#read方法将图像作为BufferedImage读取。 Again, I don't know why this works, but it does.同样,我不知道为什么会这样,但确实如此。 I also did not test your case with ImageIO#read , but I still tested it with Toolkit.getDefaultTooklit().getImage("...");我也没有使用ImageIO#read测试您的案例,但我仍然使用Toolkit.getDefaultTooklit().getImage("...");进行了测试。 and it works (but you need to supply the ImageObserver argument).它可以工作(但您需要提供ImageObserver参数)。

Some suggestions:一些建议:

In my opinion, it will be far less complicated to lay out some JLabel s in a JPanel with GridLayout (for example new GridLayout(0, 8); ) and then set the images as Icon s (via myLabel.setIcon(new ImageIcon(myImage)); ) to the JLabel s...在我看来,使用GridLayoutJPanel中布置一些JLabel (例如new GridLayout(0, 8); )然后将图像设置为Icon (通过myLabel.setIcon(new ImageIcon(myImage)); ) 到JLabel s...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM