Java ждет, пока компонент будет нарисован - PullRequest
5 голосов
/ 09 декабря 2010

Я пытаюсь создать программу на 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 закончит рисовать и начнет ждать после этого?Или есть другой способ решить проблему?

Спасибо!Леонтий

Ответы [ 3 ]

5 голосов
/ 09 декабря 2010

Весь ваш код выполняется в потоке рассылки событий. Это фактически приводит к тому, что все пользовательское взаимодействие переходит в спящий режим, поскольку нить диспетчеризации событий отвечает за все пользовательское взаимодействие - как ввод (события), так и вывод (рисование).

Вам нужно, чтобы ожидание происходило за пределами EDT. Вы должны знать, как выполнять события, включенные и выключенные в EDT. Вы делаете это, создавая новый Runnable, а затем либо вызываете new Thread(runnable), чтобы выполнить его из EDT, либо SwingUtilities.invokeLater(runnable), чтобы он выполнялся в EDT. Все взаимодействие с компонентами Swing должно происходить в EDT, поскольку объекты Swing не являются потокобезопасными. Все спящие, ожидающие, доступ к файлам, доступ к сети, доступ к базе данных или что-либо еще, что может блокировать в течение неопределенных периодов времени, должно происходить вне EDT.

Есть много вопросов о переполнении стека, связанных с потоком рассылки событий. Я рекомендую вам ознакомиться с ними, чтобы найти больше информации и примеров кода, чтобы делать то, что вы хотите делать по-разному.

1 голос
/ 09 декабря 2010

Звучит так, будто вы хотите, чтобы изображения появлялись одно за другим по мере их загрузки (т.е. как на веб-странице), это правильно? Если это так, вы не получите этот эффект с вашим текущим кодом, потому что ваш пользовательский интерфейс не будет обновляться до тех пор, пока не будут загружены все изображения, это потому, что вы загружаете изображения в EDT (Event Dispatch Thread), который является тем же потоком, который ' Я сделаю картину. Рисование происходит «когда есть время», что в данном случае означает, что после загрузки всех изображений.

Чтобы решить вашу проблему, я предлагаю вам создать подкласс 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 голос
/ 09 декабря 2010

Вместо того чтобы усыплять поток, используйте таймер и запускайте следующее изображение как событие.

...