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, так как они помогут вам составить расписание и избавят вас от необходимости спать