Java等待组件被绘制

5

我正在尝试创建一个Java程序,它可以依次显示一组图像,并为每个图像调整帧的大小。 我正在扩展JPanel来显示这样的图像:

public class ImagePanel extends JPanel{

String filename;
Image image;
boolean loaded = false;

ImagePanel(){}

ImagePanel(String filename){
    loadImage(filename);
}

public void paintComponent(Graphics g){
    super.paintComponent(g);
    if(image != null && loaded){
        g.drawImage(image, 0, 0, this);
    }else{
        g.drawString("Image read error", 10, getHeight() - 10);
    }
}

public void loadImage(String filename){
    loaded = false;         
    ImageIcon icon = new ImageIcon(filename);
    image = icon.getImage();
    int w = image.getWidth(this);
    int h = image.getHeight(this);
    if(w != -1 && w != 0 && h != -1 && h != 0){
        setPreferredSize(new Dimension(w, h));
        loaded = true;
    }else{
        setPreferredSize(new Dimension(300, 300));
    }
}

接着,在事件线程中我会处理主要的工作:

        SwingUtilities.invokeLater(new Runnable(){

        @Override
        public void run(){
            createGUI();
        }
    });

在 createGUI() 方法中,我正在遍历图像集合:
        ImagePanel imgPan = new ImagePanel();
    add(imgPan);

    for(File file : files){
        if(file.isFile()){
            System.out.println(file.getAbsolutePath());

            imgPan.loadImage(file.getAbsolutePath());
            pack();

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
        }
    }

问题在于我的程序可以正确地调整大小,因此图像可以正确加载,但是它没有显示任何内容。 如果我只显示1个图像,它也可以工作,最后一个图像也可以工作。我认为问题在于在图像绘制完成之前调用了Thread.sleep()。
如何等待我的ImagePanel完成绘制并在此之后开始等待? 还是有其他方法来解决这个问题吗?
谢谢! Leonty
3个回答

6
你所有的代码都在事件分派线程上执行。这实际上会导致所有用户交互都处于休眠状态,因为事件分派线程负责处理所有用户交互,包括输入(事件)和输出(绘制)。
你需要让等待发生在EDT之外。你需要知道如何在EDT上或离开EDT执行事件。你可以创建一个新的“Runnable”,然后调用“new Thread(runnable)”来在EDT之外执行它,或者使用“SwingUtilities.invokeLater(runnable)”在EDT上执行它。所有与Swing组件的交互都必须在EDT上进行,因为Swing对象不是线程安全的。所有睡眠、等待、文件访问、网络访问、数据库访问或任何可能会阻塞不确定时间的操作都必须发生在EDT之外。
在Stack Overflow上有许多与事件分派线程有关的问题。我建议你查看这些问题以找到更多信息和代码示例,以不同的方式完成你想要做的事情。

谢谢!它让我走上了正确的轨道! 现在我在主线程中执行代码,所有操作都是同步的,所以等待只会发生在绘制完成后。 - Leonti

1

听起来你想让图片逐个出现,就像在网页中一样,是这样吗?如果是这样的话,你现在的代码无法实现这个效果,因为直到所有图片都加载完成,你的UI才会更新。这是因为你正在EDT(事件分发线程)上加载图片,而EDT也是执行绘制操作的线程。绘制操作发生在“有时间”的情况下,这意味着在所有图片加载完成之后。

为了解决你的问题,我建议你创建SwingWorker的子类,例如:

public class MyImageLoader extends SwingWorker<Void, String>
{
    // Is executed in background thread, EDT can meanwhile load and paint images
    @Override
    protected Void doInBackground() throws Exception
    {
        for(File file : files)
        {
             if(file.isFile()) // Maybe some more check to see if it's an image
             {
                 publish(file.getAbsolutePath());
                 Thread.sleep(500);
             }
        }
    }

    // Executed on EDT
    @Override
    protected void process(List<String> filePaths)
    {
        for(String path : filePaths)
        {
            // Load ImageIcon to UI
        }
    }
}

1

不要让线程睡眠,而是使用定时器并将下一个图像作为事件启动。


我试过那样做,它可行,但是我需要使用pack()来为每个图像调整窗口大小,而且我遇到了同样的问题——我需要知道绘画何时完成才能调用'pack()'。 - Leonti

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