bri*_*akh 1 java swing swingworker jprogressbar
我正在练习Swing,当用户按下"开始下载"按钮时,我编写了一个下载进度条来下载图像.下载工作.问题是在我的终端中,我可以看到同一个事件(propertyChange)被多次触发,每次后续下载的次数都会增加.我用检查点调试了我的代码,但我仍然不确定为什么会发生这种情况.
更具体地说,在我的终端我看到了类似的东西
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
Run Code Online (Sandbox Code Playgroud)
当我希望看到"...... 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);
}
}
Run Code Online (Sandbox Code Playgroud)
Downloader.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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:我已更新我的代码以反映建议的更改.但即使在进行了更改之后,问题仍然存在.我仍然看到多次调用"... 100%已完成",随后的每次下载都会增加调用次数.例如,我运行应用程序并第一次按下载按钮,我会看到
...100% completed
Run Code Online (Sandbox Code Playgroud)
我再次按下载按钮.我知道了
...100% completed
...100% completed
Run Code Online (Sandbox Code Playgroud)
我再次按下载按钮......
...100% completed
...100% completed
...100% completed
Run Code Online (Sandbox Code Playgroud)
等等.为什么会这样?
有可能的是,由于计算百分比的方式,当仍有一些工作需要完成时它会报告100%
在我测试期间,我观察到了
//...
98
...
99
99
...
100
Run Code Online (Sandbox Code Playgroud)
因此在代码完成之前重复了很多值.
我注意到你的下载代码中存在一些问题/奇怪之处,主要是因为你完全忽略了percentCompleted属性,所以我把它改成了更像......
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();
}
}
Run Code Online (Sandbox Code Playgroud)
对我来说,我会略微改变代码,而不是......
@Override
protected void process(List<Integer> chunks) {
int percentCompleted = chunks.get(chunks.size() - 1); // only interested in the last value reported each time
progressBar.setValue(percentCompleted);
if (percentCompleted > 0) {
progressBar.setIndeterminate(false);
progressBar.setString(null);
}
System.out.println("..." + percentCompleted + "% completed");
}
/**
* Invoked when a progress property of "downloading" is received.
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("downloading")) {
publish((Integer) evt.getNewValue());
}
}
Run Code Online (Sandbox Code Playgroud)
您应该利用SwingWorker内置的进度支持,例如......
/**
* Invoked when a progress property of "downloading" is received.
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
setProgress((int)evt.getNewValue());
}
Run Code Online (Sandbox Code Playgroud)
这将意味着您将需要一个连接PropertyChangeListener到SwingWorker
/**
* 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 ("progress".equals(evt.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue(worker.getProgress());
}
}
});
worker.execute();
}
Run Code Online (Sandbox Code Playgroud)
副作用这是,你知道有手段也被通知时SwingWorker的state变化来检查时看到它是DONE
好了,去了的代码,再以后,我可以看到你添加一个新的PropertyChangeListener来model,你执行了EVERY TIMESwingWorker
/*
* Download task. Executed in worker thread.
*/
@Override
protected Void doInBackground() throws MalformedURLException, InterruptedException {
model.addListener(this); // Add another listener...
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;
}
Run Code Online (Sandbox Code Playgroud)
因为它model是一个实例字段Controller,所以它具有累积效应.
一种解决方案可能只是将其Downloader作为侦听器添加到model,但这需要您确保对UI执行的任何更新都正确同步.
一个更好的,通用的解决方案是在工作人员完成后添加支持以移除监听器
public class Downloader {
//...
public void removeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
Run Code Online (Sandbox Code Playgroud)
然后在SwingWorkers done方法中,删除监听器...
/*
* Executed in event dispatching thread
*/
@Override
protected void done() {
model.removeListener(this);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
108 次 |
| 最近记录: |