Java图形界面的重新绘制行为

5
我已经寻找了相当长时间来查找我代码中这种行为的原因。我不想深入研究Swing API以弄清楚为什么会发生这种情况。我希望能得到有关导致此问题的原因的任何信息。
这是我正在编写的应用程序的简化版本,问题似乎出现在我第一次单击绘制时,图像不会绘制到面板上,但是当我第二次单击它时,它会完美地绘制图像。之后进行的任何绘画都将正常工作,但最初的绘画问题使我感到非常恼火。谢谢任何帮助! :)
public class ImageTester {

public static void main(String[] args) {
    final JFrame window = new JFrame("Image Tester");
    final JPanel pane = new JPanel();
    JButton draw = new JButton("Draw");
    JButton paint = new JButton("Paint");

    Toolkit k = Toolkit.getDefaultToolkit();
    final Image i = k.createImage("tester.jpg");
    //pane.getGraphics().drawImage(i, 22, 15, null);

    draw.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent arg0) {
            System.out.println(pane.getGraphics());
            pane.getGraphics().drawRect(5, 5, 15, 15);
            pane.getGraphics().drawImage(i, 15, 15, null);
            System.out.println("Performance");
        }
    });

    paint.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {

        }
    });

    pane.add(draw);
    pane.add(paint);
    window.add(pane);
    window.setVisible(true);
    window.setSize(new Dimension(400, 400));
    window.setLocationRelativeTo(null);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
3个回答

5
除了camickr的建议外...
使用Toolkit.createImage()可异步加载图像。 可以使用ImageIO.read(URL /文件/ InputStream)或添加MediaTracker。
例如:
第一次运行时我看到。 enter image description here
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;

public class ImageTester {

public static void main(String[] args) throws Exception {
    final JFrame window = new JFrame("Image Tester");
    JButton draw = new JButton("Draw");
    JButton paint = new JButton("Paint");

    final Image i = ImageIO.read(new URL(
        "http://pscode.org/media/citymorn2.jpg"));

    ImagePanel gui = new ImagePanel();
    gui.setImage(i);
    draw.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent arg0) {
        }
    });

    paint.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
        }
    });

    gui.add(draw);
    gui.add(paint);
    window.add(gui);
    window.setVisible(true);
    window.setSize(new Dimension(400, 400));
    window.setLocationRelativeTo(null);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

class ImagePanel extends JPanel {

    Image i;

    public void setImage(Image image) {
        i = image;
    }

    public void paintComponent(Graphics g) {
        //System.out.println(pane.getGraphics());
        super.paintComponent(g);
        g.drawRect(5, 5, 15, 15);
        g.drawImage(i, 15, 15, null);
        System.out.println("Performance");
    }
}

我认为在性能方面,ImageIO 更慢、更昂贵?我正在尝试提高一些性能。 - JavaMeditator
@user1125661,我不知道你上面的陈述是否正确,但在担心性能之前最好学习正确的编程技术。使用你以前的方法,你尝试在点击按钮后调整框架大小看看会发生什么吗? - camickr
这个程序只是一个样例测试程序,用于缩小我在应用程序中遇到的问题范围。我知道这里实现了许多问题和可怕的编程技术。不过还是谢谢你的建议! - JavaMeditator

1
不要使用getGraphics()方法。当Swing确定组件需要重新绘制时,该绘画将会丢失。
自定义绘画是通过覆盖JPanel(或JComponent)的paintComponent()方法来完成的,然后将面板添加到框架中。
有关更多信息和示例,请参见自定义绘画

这是正确的,但实际上与他遇到的问题无关。 - Ernest Friedman-Hill
看看安德鲁的建议,以更好的方式读取图像,这样你就不会遇到你现在遇到的问题了。 - camickr

1
当您使用createImage()时,图像数据被加载,但在它知道将要绘制的组件之前,它不会被转换为可渲染的像素。 Toolkit.prepareImage()方法可以做到这一点。将此行添加到程序的末尾,绘画问题将消失:

k.prepareImage(i, -1, -1, pane);

prepareImage()(返回)“如果图像已经完全准备好,则为true;否则为false。” 鉴于这意味着它不是一个阻塞方法,在这段代码中它的作用是什么? - Andrew Thompson
谢谢您的快速回复。它确实解决了问题。解释非常有帮助。我也会更深入地研究这个。谢谢。 - JavaMeditator
@user1125661 这可能会解决问题,但这只是因为您的代码使用了错误的自定义绘制方法,而且不应该是您追求的解决方案。结合Andrew和我自己的建议,寻找正确的解决方案。如果您使用正确的绘制技术,就不需要使用此处建议的方法。 - camickr
+1 表示解释了实际问题,-1 表示没有提出更好的自定义绘画解决方案。 - camickr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接