JProgressBar не обновляется

Я сделал очень простой код, чтобы показать его здесь, у меня есть кнопка, которая должна показывать JDialog для проверки статуса выполнения, я использую функцию invoke поздно, чтобы пройти через EDT, и мой цикл не находится в методе run, так почему мой бар не обновляется? вот код

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class JBarEx extends JFrame {
private JTextField progStatus = new JTextField("Undefined");
private JButton dialogBtn = new JButton("Show Progression dialog");
final JDialog dlg = new JDialog((JFrame) null, "prog Title", false);
final JProgressBar dpb = new JProgressBar(0, 100);

public JBarEx() {
    JPanel pan = new JPanel();
    dialogBtn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            showProgress();
        }
    });
    progStatus.setEditable(false);
    pan.add(progStatus);
    pan.add(dialogBtn);
    setContentPane(pan);
    this.setSize(200, 100);
    setVisible(true);
}

public void showProgress() {
    dlg.add(BorderLayout.CENTER, dpb);
    dlg.add(BorderLayout.NORTH, new JLabel("prog message"));
    dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    dlg.setSize(300, 75);
    dlg.setLocationRelativeTo(null);
    dlg.setVisible(true);

    for (int i = 0; i < 100; i++) {
        final int ii = i;
        try {
            Thread.sleep(25);
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    updateBar(ii);

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

}

public void updateBar(int newValue) {
    dpb.setValue(newValue);
}

public static void main(String[] args) {
    JBarEx jbx = new JBarEx();
}

}

3 ответа

Решение

Ваш showProgress Метод выполняется в контексте потока диспетчеризации событий. EDT отвечает, помимо прочего, за обработку запросов на краску. Это означает, что до тех пор, пока ваш for-loop выполняется, EDT не может обрабатывать новые запросы на рисование (или обрабатывать invokeLater события либо) как это блокирует EDT.

Несмотря на то, что существует множество возможных способов решения этой проблемы, на основе вашего примера кода самым простым будет использование SwingWorker,

Он позволяет вам выполнять долгосрочную задачу в фоновом потоке (освобождая EDT), но также позволяет вам publishing обновления (при необходимости), чтобы их можно было обрабатывать в EDT, а также предоставляет удобный progress уведомление.

Например...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class SwingWorkerProgress {

    public static void main(String[] args) {
        new SwingWorkerProgress();
    }

    public SwingWorkerProgress() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JProgressBar pbProgress;
        private JButton start;

        public TestPane() {

            setBorder(new EmptyBorder(10, 10, 10, 10));
            pbProgress = new JProgressBar();
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(4, 4, 4, 4);
            gbc.gridx = 0;
            gbc.gridy = 0;
            add(pbProgress, gbc);

            start = new JButton("Start");
            gbc.gridy++;
            add(start, gbc);

            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    start.setEnabled(false);
                    ProgressWorker pw = new ProgressWorker();
                    pw.addPropertyChangeListener(new PropertyChangeListener() {

                        @Override
                        public void propertyChange(PropertyChangeEvent evt) {
                            String name = evt.getPropertyName();
                            if (name.equals("progress")) {
                                int progress = (int) evt.getNewValue();
                                pbProgress.setValue(progress);
                                repaint();
                            } else if (name.equals("state")) {
                                SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
                                switch (state) {
                                    case DONE:
                                        start.setEnabled(true);
                                        break;
                                }
                            }
                        }

                    });
                    pw.execute();
                }
            });

        }
    }

    public class ProgressWorker extends SwingWorker<Object, Object> {

        @Override
        protected Object doInBackground() throws Exception {

            for (int i = 0; i < 100; i++) {        
                setProgress(i);
                try {
                    Thread.sleep(25);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            return null;
        }
    }
}

Проверьте параллелизм в Swing для более подробной информации

Даже если вы исправите цикл, как указали другие, вы все равно заблокируете поток отправки событий. for цикл запускается showProgress() который вызывается из прослушивателя событий. Обновления помещаются в очередь событий, но они не обрабатываются, пока цикл не завершится.

Вместо этого используйте Swing Timer. Что-то вроде этого:

Timer timer = new Timer(25, new ActionListener() {
    private int position;

    @Override
    public void actionPerformed(ActionEvent e) {
         position++;
         if (position < lastPosition) {
             updateBar(position);
         } else {
             ((Timer) e.getSource).stop();
         }
    }
});
timer.start();

где lastPosition будет состояние, в котором вы хотите остановить индикатор выполнения.

Вне зависимости от этой ошибки, но, тем не менее, вы не должны создавать компоненты Swing вне потока диспетчеризации событий. Лучше всего сделать это с самого начала:

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            JBarEx jbx = new JBarEx();
        }
    });
}
for (int i = 0; i < 0; i++) {

Вы никогда не будете вводить этот код, поэтому никогда не вызовете метод updateBar(..)

мне нужно быть больше 0 в этом случае. Если это 1, то updateBar будет вызываться один раз, если 2, то updateBar будет вызываться дважды и т. Д.

Также вместо того, чтобы делать

Thread.sleep(25);

посмотрите на исполнителей Java, так как они помогут вам составить расписание и избавят вас от необходимости спать

Другие вопросы по тегам