Je pratique Swing et j'ai codé une barre de progression de téléchargement pour télécharger une image lorsque l'utilisateur appuie sur le bouton "Démarrer le téléchargement". Le téléchargement fonctionne. Le problème est que dans mon terminal, je peux voir que le même événement (propertyChange
) est lancé plusieurs fois, le nombre de fois augmente avec chaque téléchargement ultérieur. J'ai débogué mon code avec des points de contrôle, mais je ne sais toujours pas pourquoi cela se produit.Pourquoi ma barre de progression de téléchargement envoie-t-elle plusieurs fois le même événement?
Pour être plus précis, dans mon terminal je vois quelque chose comme
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
...100% completed
quand j'attends à voir « ... 100% terminé » une seule fois. Le nombre de "... 100% terminé" affiché s'accumule à chaque téléchargement. Je ne suis pas sûr si cela affecte la performance de mon téléchargement, mais je me demande pourquoi cela se passe.
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);
}
}
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();
}
}
}
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);
}
});
}
}
EDIT: J'ai mis à jour mon code pour refléter les changements suggérés. Mais même après avoir fait les changements, le problème persiste. Je vois encore plusieurs invocations de "... 100% achevées", le nombre d'invocations augmentant à chaque téléchargement. Par exemple, je lance l'application et appuyez sur le bouton de téléchargement pour la première fois, je verrai
...100% completed
J'appuyez à nouveau sur le bouton de téléchargement. Je vois
...100% completed
...100% completed
j'appuie sur le bouton de téléchargement à nouveau ...
...100% completed
...100% completed
...100% completed
et ainsi de suite. Pourquoi cela arrive-t-il?
Il s'agit probablement d'une erreur d'arrondi. Ne serait-il pas plus logique de faire quelque chose comme '(int) (((double) totalBytesRead/(double) connection.getContentLength()) * 100.0)' – MadProgrammer
Je veux dire qu'il est possible que les derniers octets puissent générer 100 % value dû à la façon dont la division 'int' est arrondie - mais après avoir regardé un peu plus près votre code, je pense que votre calcul est faux – MadProgrammer
Merci d'avoir rattrapé ce @MadProgrammer J'ai édité ma question pour inclure votre correction au calcul et une reformulation de ma question, puisque le problème persiste – briennakh