使用PropertyChangeListener更新Swing组件时JLabel不更新问题
Fixing JLabel Updates in Your Java Download Manager
Let's break down why your downloadedSize and sizeOfFile JLabels aren't updating during downloads, then fix this step by step.
What's Going Wrong
- No property change events for custom fields: Your
Downloadclass usessetProgress()(which automatically fires a "progress" property change event) for the progress bar, but your custom fields likedownloadedSize,sizeOfFile, anddownloadSpeeddon't trigger any property change events when they update. The listener has no way to know these values changed. - Listener doesn't handle custom field updates: Your
DownloadPanelPropertyListeneronly reacts to "progress" and "downloadPanel" events—there's no logic to update the JLabels whendownloadedSizeorsizeOfFilechanges. - Incorrect byte-to-MB conversion: Your current code displays raw byte counts as "MG" (probably meant MB), which is misleading and incorrect (1 MB = 1024 * 1024 bytes).
- Direct field assignments: You're updating
downloadedSizeandsizeOfFiledirectly indoInBackground()instead of using setter methods that trigger property change events.
Step-by-Step Fixes
1. Add Property Change Events to Custom Fields in Download
Update your setter methods to fire property change events whenever these values update. This lets listeners know the data has changed:
public class Download extends SwingWorker<Void,Void> implements Serializable,Runnable { // ... existing fields ... public void setDownloadedSize(int downloadedSize) { int oldValue = this.downloadedSize; this.downloadedSize = downloadedSize; firePropertyChange("downloadedSize", oldValue, downloadedSize); } public void setSizeOfFile(int sizeOfFile) { int oldValue = this.sizeOfFile; this.sizeOfFile = sizeOfFile; firePropertyChange("sizeOfFile", oldValue, sizeOfFile); } public void setDownloadSpeed(int downloadSpeed) { int oldValue = this.downloadSpeed; this.downloadSpeed = downloadSpeed; firePropertyChange("downloadSpeed", oldValue, downloadSpeed); } // ... rest of your class ... }
2. Update doInBackground() to Use Setters
Instead of directly assigning values to fields, use the setters you just created to trigger events:
public Void doInBackground () { // ... existing code ... // When setting sizeOfFile for the first time: if (sizeOfFile == -1) { setSizeOfFile(contentLength); // Use setter instead of direct assignment } while (status == CURRENT) { // ... existing buffer logic ... int read = stream.read(buffer); if (read == -1) break; file.write(buffer, 0, read); setDownloadedSize(this.downloadedSize + read); // Use setter here setProgress((int) (((double) getDownloadedSize() / (double) getSizeOfFile()) * 100)); } // ... rest of your method ... }
3. Modify the Property Listener to Update JLabels
Update DownloadPanelPropertyListener to handle the new property events and refresh the corresponding labels. We'll also fix the byte-to-MB conversion:
public class DownloadPanelPropertyListener implements PropertyChangeListener { private Download download; public DownloadPanelPropertyListener(Download download) { this.download = download; } @Override public void propertyChange(PropertyChangeEvent evt) { String propName = evt.getPropertyName(); DownloadPanel panel = download.getDownloadPanel(); switch (propName) { case "progress": panel.getJpb().setValue((Integer) evt.getNewValue()); // Update progress percentage label (make sure it's a member variable in DownloadPanel) panel.getProgressBarValueLabel().setText("%" + evt.getNewValue()); break; case "downloadedSize": case "sizeOfFile": updateSizeLabels(panel); break; case "downloadSpeed": panel.getDownloadSpeedLabel().setText(String.format("%d Kbs", (Integer) evt.getNewValue())); break; } } // Helper method to update the size label with correct MB conversion private void updateSizeLabels(DownloadPanel panel) { int downloadedBytes = download.getDownloadedSize(); int totalBytes = download.getSizeOfFile(); double downloadedMB = downloadedBytes / (1024.0 * 1024.0); double totalMB = totalBytes / (1024.0 * 1024.0); panel.getDownloadedSizeLabel().setText(String.format("%.2f MB / %.2f MB", downloadedMB, totalMB)); } }
4. Fix DownloadPanel for Accessibility and Initialization
Update DownloadPanel to make labels accessible via getters, and fix the initial label text with correct conversion:
public class DownloadPanel extends JPanel { private Download download; private JProgressBar jpb = new JProgressBar(0,100); private JLabel downloadSpeedLabel; private JLabel downloadedSizeLabel; private JLabel progressBarValueLabel; // Make this a member variable public DownloadPanel (Download d) { download = d; this.addPropertyChangeListener("downloadPanel", new DownloadPanelPropertyListener(download)); // Initialize progress bar int initialProgress = (int) (((double) download.getDownloadedSize() / (double) download.getSizeOfFile()) * 100); jpb.setValue(initialProgress); jpb.setBounds(100,25,400,10); jpb.setIndeterminate(false); // Initialize progress percentage label progressBarValueLabel = new JLabel("%" + initialProgress); progressBarValueLabel.setBounds(510,18,25,20); add(progressBarValueLabel); // Initialize speed label with correct value downloadSpeedLabel = new JLabel(String.format("%d Kbs", download.getDownloadSpeed())); add(downloadSpeedLabel); // Initialize size label with correct MB conversion double downloadedMB = download.getDownloadedSize() / (1024.0 * 1024.0); double totalMB = download.getSizeOfFile() / (1024.0 * 1024.0); downloadedSizeLabel = new JLabel(String.format("%.2f MB / %.2f MB", downloadedMB, totalMB)); add(downloadedSizeLabel); } // Add getters for all components the listener needs to access public JProgressBar getJpb() { return jpb; } public JLabel getDownloadSpeedLabel() { return downloadSpeedLabel; } public JLabel getDownloadedSizeLabel() { return downloadedSizeLabel; } public JLabel getProgressBarValueLabel() { return progressBarValueLabel; } }
Key Notes to Remember
- Always use setters for observable fields: Directly modifying fields won't trigger property change events, so your UI will never know to update.
- Swing updates must happen on the EDT: Luckily,
SwingWorkerautomatically delivers property change events to the EDT, so you don't need to wrap UI updates inSwingUtilities.invokeLater()here—but keep this in mind for other Swing code. - Convert units correctly: Raw byte counts are not user-friendly; convert to KB/MB using floating-point division to avoid integer truncation.
内容的提问来源于stack exchange,提问作者Mahdi Ghajary
相关产品推荐
相关产品推荐

