JPanel 不重绘

JPanel does not repaint

我正在使用多个 BufferedImages。我正在使用的 JPanel 的 paint 函数在 JPanel 上绘制 currentImg:

@Override
public void paint(Graphics g) {
    g.drawImage(currentImg, 0, 0, null);
}

使用currentImg方便切换图片,一开始就等于normalImg。 redImg 是一个 BufferedImage,看起来与 normalImg 不同。 现在我想画 redImg 半秒,然后再画 normalImg。

currentImg = redImg;
repaint();
Thread.sleep(1000);
currentImg = normalImg;
repaint();

但是这段代码什么都不做,JPanel 没有被重新绘制。这段代码虽然有效:

currentImg = redImg;
repaint();
JOptionPane.showMessageDialog(this,"test");
Thread.sleep(1000);
JOptionPane.showMessageDialog(this,"test");
currentImg = normalImg;
repaint();

但我不想显示消息对话框只是为了正确地重新绘制它。 感谢您的帮助:)

您通过在绘画方法和 Swing 事件线程中调用 Thread.sleep(...) 来冻结整个应用程序。不要这样做。而是使用 Swing 计时器并从计时器中交换 JLabel 的图标。

例如:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.*;

public class SwapImages extends JPanel {
   private static final int TIMER_DELAY = 200;
   private static final String SPRITE_PATH = "http://th02.deviantart.net/"
         + "fs70/PRE/i/2011/169/0/8/blue_player_sprite_sheet_by_resetado-d3j7zba.png";
   public static final int SPRITE_ROWS = 6;
   public static final int SPRITE_COLS = 6;
   public static final int SPRITE_CELLS = 35;

   private JLabel label = new JLabel();
   private List<ImageIcon> iconList = new ArrayList<ImageIcon>();
   private int iconIndex = 0;

   public SwapImages() throws IOException {
      URL imgUrl = new URL(SPRITE_PATH);
      BufferedImage mainImage = ImageIO.read(imgUrl);

      for (int i = 0; i < SPRITE_CELLS; i++) {
         int row = i / SPRITE_COLS;
         int col = i % SPRITE_COLS;
         int x = (int) (((double) mainImage.getWidth() * col) / SPRITE_COLS);
         int y = (int) ((double) (mainImage.getHeight() * row) / SPRITE_ROWS);
         int w = (int) ((double) mainImage.getWidth() / SPRITE_COLS);
         int h = (int) ((double) mainImage.getHeight() / SPRITE_ROWS);
         BufferedImage img = mainImage.getSubimage(x, y, w, h);
         ImageIcon icon = new ImageIcon(img);
         iconList.add(icon);
      }
      add(label);
      label.setIcon(iconList.get(iconIndex));
      new Timer(TIMER_DELAY, new TimerListener()).start();
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent arg0) {
         iconIndex++;
         iconIndex %= iconList.size();
         label.setIcon(iconList.get(iconIndex));
      }
   }

   private static void createAndShowGui() {
      SwapImages mainPanel = null;
      try {
         mainPanel = new SwapImages();
      } catch (IOException e) {
         e.printStackTrace();
         System.exit(-1);
      }

      JFrame frame = new JFrame("SwapImages");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
  1. 避免覆盖 paint,尤其是顶级容器,使用基于 JComponent 的组件并覆盖其 paintComponent 方法。查看 Painting in AWT and Swing and Performing Custom Painting 以了解有关 Swing 中喘息的更多详细信息
  2. 始终调用 super.paintXxx 您覆盖的任何绘制方法
  3. 不要在事件调度线程的上下文中使用 Thread.sleep,这将阻止它处理来自 EventQueue 的新事件,包括重绘事件。
  4. 不要从事件调度线程的上下文之外修改 UI。

查看 Concurrency in Swing for more details and How to use Swing Timers 以获得可能的解决方案

还有一个