JProgressBar не будет обновлять
Я пытаюсь добавить JProgressBar в мою программу, но она не будет обновляться! Значение изменяется только тогда, когда оно вызывает 100%. Вот мой метод.
public void downloadImages(List<String> images) {
if (errorCode == 0) {
for (int i = 0; i < images.size(); i++) {
if (errorCode == 0) {
main.progressLabel.setText("Downloading image " + Integer.toString(i + 1) + " of " + Integer.toString(images.size()));
String imageStr = images.get(i);
String imageName = imageStr.substring(imageStr.lastIndexOf("/") + 1);
try {
URL url = new URL(imageStr);
InputStream in = url.openStream();
OutputStream out = new FileOutputStream(saveDirectory + imageName);
byte[] b = new byte[2048];
int length;
while ((length = in.read(b)) != -1) {
out.write(b, 0, length);
}
in.close();
out.close();
} catch (MalformedURLException e) {
errorCode = BAD_URL;
} catch (IOException e) {
errorCode = INVALID_PATH;
}
main.progressBar.setValue(((i+1)/images.size())*100);
}
}
}
}
Изменение значения индикатора выполнения находится внизу метода выше.
И вот как я называю этот метод.
public void download() {
final Downloader downloader = new Downloader(this, albumUrl.getText(), downloadPath.getText());
progressBar.setValue(0);
downloadButton.setEnabled(false);
new Thread(new Runnable() {
public void run() {
List<String> images = downloader.getImages(downloader.getPageSource());
downloader.downloadImages(images);
if (downloader.getErrorCode() == 0) {
progressLabel.setText("All images have been downloaded!");
} else {
String error = "";
switch (downloader.getErrorCode()) {
case Downloader.BAD_URL:
case Downloader.NOT_IMGUR_ALBUM:
error = "The URL entered is either invalid or does not link to an Imgur album.";
break;
case Downloader.BLANK_URL:
error = "The album URL field cannot be blank.";
break;
case Downloader.INVALID_PATH:
error = "The system cannot find the download directory path specified.";
break;
case Downloader.BLANK_PATH:
error = "The download directory cannot be blank.";
break;
case Downloader.CANNOT_READ_URL:
error = "An error occoured while reading the URL.";
break;
case Downloader.PARSING_ERROR:
error = "An error occoured while parsing the URL.";
break;
}
JOptionPane.showMessageDialog(Main.this, error, "Error", 0);
}
downloadButton.setEnabled(true);
}
}).start();
}
РЕДАКТИРОВАТЬ: Выше не было проблемой вообще, проблема одна программа использовала целочисленное деление, а не десятичное.
3 ответа
images.size()
является целым числом, и поэтому i+1
, так что происходит то, что десятичное число усекается. Что вы должны делать, что-то вроде
main.progressBar.setValue((int)((i+1)/(double)images.size())/100))
Что это будет делать, это будет гарантировать, что i+1
делится на тип данных с поддержкой десятичных чисел, который будет возвращать более инклюзивный тип данных (в данном случае double), а затем он делит double на int, что не будет проблемой, потому что он все еще вернет двойной, потому что это более инклюзивно. Затем мы приводим это к int, потому что это тип данных setValue()
хочет.
Основная проблема заключается в том, что вы блокируете поток диспетчеризации событий, выполняя долгосрочную задачу в графическом интерфейсе пользователя.
Скорее используйте SwingWorker.
Вот небольшой пример:
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
public class ProgressBarDemo extends JPanel {
private JButton startButton;
private JTextArea taskOutput;
public ProgressBarDemo() {
super(new BorderLayout());
final JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
taskOutput = new JTextArea(5, 20);
taskOutput.setMargin(new Insets(5, 5, 5, 5));
taskOutput.setEditable(false);
// Create the demo's UI.
startButton = new JButton("Start");
startButton.setActionCommand("start");
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
startButton.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Instances of javax.swing.SwingWorker are not reusuable, so
// we create new instances as needed.
final Task task = new Task();
task.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pce) {
if ("progress".equals(pce.getPropertyName())) {
int progress = (Integer) pce.getNewValue();
progressBar.setValue(progress);
taskOutput.append(String.format("Completed %d%% of task.\n", task.getProgress()));
}
}
});
task.execute();
}
});
JPanel panel = new JPanel();
panel.add(startButton);
panel.add(progressBar);
add(panel, BorderLayout.PAGE_START);
add(new JScrollPane(taskOutput), BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
/**
* Create the GUI and show it. As with all GUI code, this must run on the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
JFrame frame = new JFrame("ProgressBarDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel progressBarPanel = new ProgressBarDemo();
frame.add(progressBarPanel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
private class Task extends SwingWorker<Void, Void> {
/*
* Main task. Executed in background thread.
*/
@Override
public Void doInBackground() {
Random random = new Random();
int progress = 0;
// Initialize progress property.
setProgress(0);
while (progress < 100) {
// Sleep for up to one second.
try {
Thread.sleep(random.nextInt(1000)-15);
} catch (InterruptedException ignore) {
}
// Make random progress.
progress += random.nextInt(10);
setProgress(Math.min(progress, 100));
}
return null;
}
/*
* Executed in event dispatching thread
*/
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
setCursor(null); // turn off the wait cursor
taskOutput.append("Done!\n");
}
}
}
Два правила свинга...
- Не блокируйте поток рассылки событий
- Не обновляйте какой-либо компонент пользовательского интерфейса вне потока диспетчеризации событий.
Я уверен, что есть и другие, но сломайте один или оба из них и ожидайте, что случится много плохих вещей.
Swing - это однопоточный API. Существует один поток, отвечающий за отправку событий, включая запросы на перерисовку. Если вы заблокируете этот поток (выполняя трудоемкие задачи, ввод-вывод и т. Д.), Вы остановите его от обработки ввода от пользователя и отправки запросов на перерисовку компонентам - поэтому ничего не будет обновлено, и ваше приложение будет выглядеть как зависшее...
Вы должны получить код загрузки вашего изображения вне EDT...
Посмотрите на Concurrency in Swing для более подробной информации, в частности, посмотрите на Worker Threads и SwingWorker