【发布时间】:2017-07-26 22:16:38
【问题描述】:
我正在练习 Swing,当用户按下“开始下载”按钮时,我编写了一个下载进度条来下载图像。下载有效。问题是在我的终端中,我可以看到同一个事件 (propertyChange) 被多次触发,每次后续下载的次数都会增加。我已经使用检查点调试了我的代码,但我仍然不确定为什么会发生这种情况。
更具体地说,在我的终端中,我看到了类似
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
当我希望只看到一次“...100% 完成”时。每次下载都会累积显示的“...100% 完成”的数量。我不确定这是否会影响我的下载性能,但我想知道为什么会这样。
ProgressBar.java:
package download_progress_bar;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ProgressBar {
private JFrame frame;
private JPanel gui;
private JButton button;
private JProgressBar progressBar;
public ProgressBar() {
customizeFrame();
createMainPanel();
createProgressBar();
createButton();
addComponentsToFrame();
frame.setVisible(true);
}
private void customizeFrame() {
// Set the look and feel to the cross-platform look and feel
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) {
System.err.println("Unsupported look and feel.");
e.printStackTrace();
}
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
private void createMainPanel() {
gui = new JPanel();
gui.setLayout(new BorderLayout());
}
private void createProgressBar() {
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true); // renders a progress string
}
private void createButton() {
button = new JButton("Start download");
}
private void addComponentsToFrame() {
gui.add(progressBar, BorderLayout.CENTER);
gui.add(button, BorderLayout.SOUTH);
frame.add(gui);
frame.pack();
}
// Add passed ActionListener to the button
void addButtonListener(ActionListener listener) {
button.addActionListener(listener);
}
// Get progress bar
public JProgressBar getProgressBar() {
return progressBar;
}
// Enable or disable button
public void turnOnButton(boolean flip) {
button.setEnabled(flip);
}
}
下载器.java:
package download_progress_bar;
import java.net.*;
import java.io.*;
import java.beans.*;
public class Downloader {
private URL url;
private int percentCompleted;
private PropertyChangeSupport pcs;
public Downloader() {
pcs = new PropertyChangeSupport(this);
}
// Set URL object
public void setURL(String src) throws MalformedURLException {
url = new URL(src);
}
// Add passed PropertyChangeListener to pcs
public void addListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void download() throws IOException {
// Open connection on URL object
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Check response code (always do this first)
int responseCode = connection.getResponseCode();
System.out.println("response code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
// Open input stream from connection
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
// Open output stream for file writing
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("cat.jpg"));
int totalBytesRead = 0;
//int percentCompleted = 0;
int i = -1;
while ((i = in.read()) != -1) {
out.write(i);
totalBytesRead++;
int old = percentCompleted;
percentCompleted = (int)(((double)totalBytesRead / (double)connection.getContentLength()) * 100.0);
pcs.firePropertyChange("downloading", old, percentCompleted);
System.out.println(percentCompleted); // makes download a bit slower, comment out for speed
}
// Close streams
out.close();
in.close();
}
}
}
Controller.java:
package download_progress_bar;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import java.awt.event.*;
import java.util.List;
import java.net.*;
import java.io.*;
import java.beans.*;
public class Controller {
private ProgressBar view;
private Downloader model;
private JProgressBar progressBar;
private SwingWorker<Void, Integer> worker;
public Controller(ProgressBar theView, Downloader theModel) {
view = theView;
model = theModel;
progressBar = view.getProgressBar();
// Add button listener to the "Start Download" button
view.addButtonListener(new ButtonListener());
}
class ButtonListener implements ActionListener {
/**
* Invoked when user clicks the button.
*/
public void actionPerformed(ActionEvent evt) {
view.turnOnButton(false);
progressBar.setIndeterminate(true);
// NOTE: Instances of javax.swing.SwingWorker are not reusable,
// so we create new instances as needed
worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("progress")) {
progressBar.setIndeterminate(false);
progressBar.setValue(worker.getProgress());
}
}
});
worker.execute();
}
}
class Worker extends SwingWorker<Void, Integer> implements PropertyChangeListener {
/*
* Download task. Executed in worker thread.
*/
@Override
protected Void doInBackground() throws MalformedURLException {
model.addListener(this);
try {
String src = "https://lh3.googleusercontent.com/l6JAkhvfxbP61_FWN92j4ulDMXJNH3HT1DR6xrE7MtwW-2AxpZl_WLnBzTpWhCuYkbHihgBQ=s640-h400-e365";
model.setURL(src);
model.download();
} catch (IOException ex) {
System.out.println(ex);
this.cancel(true);
}
return null;
}
/*
* Executed in event dispatching thread
*/
@Override
protected void done() {
try {
if (!isCancelled()) {
get(); // throws an exception if doInBackground throws one
System.out.println("File has been downloaded successfully!");
}
} catch (InterruptedException x) {
x.printStackTrace();
System.out.println("There was an error in downloading the file.");
} catch (ExecutionException x) {
x.printStackTrace();
System.out.println("There was an error in downloading the file.");
}
view.turnOnButton(true);
}
/**
* Invoked in the background thread of Downloader.
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
this.setProgress((int) evt.getNewValue());
System.out.println("..." + this.getProgress() + "% completed");
}
}
}
Main.java:
package download_progress_bar;
import javax.swing.SwingUtilities;
/**
* Runs the download progress bar application.
*/
public class Main {
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Create view
ProgressBar view = new ProgressBar();
// NOTE: Should model/controller be created outside invokeLater?
// Create model
Downloader model = new Downloader();
// Create controller
Controller controller = new Controller(view, model);
}
});
}
}
编辑:我更新了我的代码以反映建议的更改。但即使进行了更改,问题仍然存在。我仍然看到多次调用“...100% 完成”,调用次数随着每次后续下载而增加。比如我运行应用程序,第一次按下下载按钮,我会看到
...100% completed
我再次按下下载按钮。我明白了
...100% completed
...100% completed
我再次按下下载按钮...
...100% completed
...100% completed
...100% completed
等等。为什么会这样?
【问题讨论】:
-
这很可能是一个舍入错误。做类似
(int)(((double)totalBytesRead / (double)connection.getContentLength()) * 100.0)的事情不是更有意义吗? -
我的意思是,由于
int除法四舍五入的方式,最后几个字节可能会产生 100% 的值 - 但是仔细查看您的代码,我认为您的计算是错了 -
感谢@MadProgrammer 我已经编辑了我的问题,包括您对计算的更正和我的问题的改写,因为问题仍然存在
-
您可以在后台调用
setProgress():“出于性能目的,所有这些调用仅与最后一个调用参数合并为一个调用。” -
我唯一可以调用
setProgress()的地方是我的propertyChange()方法,这就是我遇到的问题。它被多次调用,调用次数随着每次后续下载而增加@trashgod
标签: java swing swingworker jprogressbar