You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

  1. No property change events for custom fields: Your Download class uses setProgress() (which automatically fires a "progress" property change event) for the progress bar, but your custom fields like downloadedSize, sizeOfFile, and downloadSpeed don't trigger any property change events when they update. The listener has no way to know these values changed.
  2. Listener doesn't handle custom field updates: Your DownloadPanelPropertyListener only reacts to "progress" and "downloadPanel" events—there's no logic to update the JLabels when downloadedSize or sizeOfFile changes.
  3. 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).
  4. Direct field assignments: You're updating downloadedSize and sizeOfFile directly in doInBackground() 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, SwingWorker automatically delivers property change events to the EDT, so you don't need to wrap UI updates in SwingUtilities.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:36:46