简体   繁体   English

在鼠标单击之前,JPanel不会绘制图像

[英]JPanel doesn't draw image until mouse click

I have a small, simple program I've started and have a strange bug I haven't been able to figure out or find the cause for online. 我已经启动了一个简单的小型程序,并且遇到了一个奇怪的错误,我一直无法弄清或找到导致在线的原因。

All my program does right now is build a JFrame, build a JPanel, and draw an image onto the JPanel from a thread. 我的程序现在要做的就是构建一个JFrame,构建一个JPanel并从线程将图像绘制到JPanel上。 The issue is, the image does not display unless I click in the Frame, left click, right click, or even click with my mouse wheel makes the image display, but resizing the frame doesn't make the image display. 问题是,除非我单击框架,单击鼠标左键,单击鼠标右键,甚至用鼠标滚轮单击,否则图像不会显示,但是调整框架的大小不会显示图像。 I've tried validate() on the panel and frames both, and I've tried putting repaint() in the code called by the thread but the image flickers when repaint() is in the code. 我在面板上尝试了validate()并同时对它们进行了框架化,并且尝试将repaint()放入线程调用的代码中,但是当repaint()在代码中时,图像会闪烁。

I put a print statement in the code that actually draws the image, but the statement never shows up until I click in the frame. 我在实际绘制图像的代码中添加了一条打印语句,但是直到单击框架后该语句才显示。 I've walked through with the debugger and the code seems to be getting called correctly though. 我已经遍历了调试器,但是代码似乎被正确调用了。 If I put a print statement anywhere in code being called from the main thread's update() function, the image displays correctly without having to click. 如果我在主线程的update()函数调用的代码中的任何地方放置了一条打印语句,则无需单击即可正确显示图像。

Here is my code, any suggestions would be appreciated: 这是我的代码,任何建议将不胜感激:

The Frame 框架

public class GameFrame extends JFrame{

  int w = 1300;
  int h = 740;

  public GameFrame() {

    this.setSize(w, h);
    setTitle("Frame");

    GamePanel panel = new GamePanel(w, h);
    this.add(panel);
    this.setVisible(true);
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
  }

  public static void main(String[] args) {
    GameFrame gameFrame = new GameFrame();
  }
}

The Panel 专家小组

public class GamePanel extends JPanel implements Runnable{

//core classes
GameRenderer renderer;
GameManager manager;
GameLoader loader;

//graphics stuff
private Graphics dbg;
private Image dbImage = null;
private int panelWidth;
private int panelHeight;

//game updating stuff
Thread animator;
boolean running;

public GamePanel(int w, int h) {
    this.setSize(w, h);
    this.setVisible(true);
    this.setFocusable(true);
    this.panelWidth = w;
    this.panelHeight = h;

    renderer = new GameRenderer();
    manager = new GameManager();
    loader = new GameLoader();

    startGame();
}

private void startGame() {
    animator = new Thread(this, "Animator");
    animator.start();
    running = true;
}

public void run() {
    while(running) {
        gameAction();
    }
}

//everything that needs to be updated continuously
private void gameAction() {
    //updateGame(); // Need code here to update everything
    gameRender(); // Draw to the double buffer.
    paintScreen(); // Draw double buffer to screen.
}

/**
 * Draws the game image to the buffer.
 * 
 */
private void gameRender() {
    if(dbImage == null) {
        dbImage = createImage(this.panelWidth, this.panelHeight);
        return;
    }
    dbg = dbImage.getGraphics();
    renderer.draw((Graphics2D) dbg, panelWidth, panelHeight);
}

/**
 * Draws the game image to the screen by drawing the buffer.
 */
private void paintScreen() {
    Graphics g;
    try {
        g = this.getGraphics();
        if ((g != null) && (dbImage != null))  {
            g.drawImage(dbImage, 0, 0, null);
            g.dispose();
        }
    } catch (Exception e) { System.out.println("Graphics context error: " + e); }
}

}

Image Renderer 影像渲染器

public class GameRenderer {

ImageManipulator im = new ImageManipulator();

/**
 * Actually draw everything that needs to be drawn
 * Layering also happens here 
 */
public void draw(Graphics2D g,int screenWidth, int screenHeight) {
    BufferedImage tileImg = im.loadImage("Images/tile.png");
    Tile tile = new Tile(tileImg, 30, 30);
    tile.draw(g);
}

}

Class with image I'm trying to draw 我正在尝试绘制的图像类

public class Tile {

BufferedImage tileImg;
//x and y values based on tile grid - z based on height up
int tileX;
int tileY;
int tileZ;
//actual coordinate of top left corner of tile *image*
int pixelX;
int pixelY;

public Tile(BufferedImage img, int pixelX, int pixelY) {
    this.pixelX = pixelX;
    this.pixelY = pixelY;
    this.tileImg = img;
}

public void draw(Graphics g) {
    g.drawImage(tileImg, pixelX, pixelY, null);
}

}

Again, any suggestions or ideas would help. 同样,任何建议或想法都会有所帮助。 I'm out of ideas of what might be causing this. 我不知道是什么原因造成的。 Also, does anybody know why this might be working with a print statement included but not without one? 另外,有人知道为什么这可能适用于包含但不没有打印语句的打印语句吗?

You have misunderstood how to do custom painting in a Swing component. 您误解了如何在Swing组件中进行自定义绘制。 It is not correct to call this.getGraphics() . 调用this.getGraphics()是不正确的。 The correct way to paint a Swing component is to override its paintComponent method. 绘制Swing组件的正确方法是重写其paintComponent方法。 Note that the first line in that method must call super.paintComponent : 请注意,该方法的第一行必须调用super.paintComponent

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (dbImage != null) {
        g.drawImage(dbImage, 0, 0, this);
    }
}

Also note that you must not attempt to dispose the Graphics object, since you didn't create it and it is under Swing's control, not yours. 还要注意,您不能尝试处置Graphics对象,因为您没有创建它,并且它在Swing的控制之下,而不是在您的控制之下。

For full details, see the Painting in AWT and Swing tutorial. 有关完整的详细信息,请参见AWT和Swing中绘画教程。

When you want your game to repaint, simply call the panel's repaint method. 当您希望游戏重新绘制时,只需调用面板的重新绘制方法即可。 Thus, your gameAction method should replace paintScreen() with repaint(), which will automatically call your panel's paintComponent method (and some other methods; as that tutorial explains, painting isn't a simple thing). 因此,您的gameAction方法应该用repaint()替换paintScreen(),这将自动调用面板的paintComponent方法(以及其他方法;正如该教程所解释的那样,绘画不是一件容易的事)。

You will need to put a short sleep call inside the loop in your run() method. 您将需要在run()方法的循环内进行短暂的睡眠调用。 If you try to update and repaint the game constantly, the program won't have time to actually draw anything. 如果您尝试不断更新和重绘游戏,则该程序将没有时间实际绘制任何内容。 For instance, if you want the game to update five times per second (that is, every 200 milliseconds): 例如,如果您希望游戏每秒更新五次(即每200毫秒):

try {
    while (running) {
        gameAction();
        Thread.sleep(200);
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

I have two guesses. 我有两个猜测。 The first one is the startGame method in GamePanel. 第一个是GamePanel中的startGame方法。 Put the line "running = true;" 将行“ running = true;” before you call "start()" on your thread. 在线程上调用“ start()”之前。

If that doesn't work, try this in your run() method in GamePanel: 如果这不起作用,请在GamePanel的run()方法中尝试以下操作:

public void run() { gameAction(); }

If it renders, then the problem is the while loop. 如果呈现,则问题是while循环。

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

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