1

Take this code:

public static void main(String[] args) {
    try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException e) {
        e.printStackTrace();
    }
    JFrame frame = new JFrame();

    JProgressBar jpb = new JProgressBar();

    frame.add(jpb);

    frame.setLocationByPlatform(true);
    frame.pack();
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    jpb.setMaximum(1000);
    for(int i = 0; i < 1000; i++) {
        System.out.println("Processing " + i);
        jpb.setValue(i);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

and run it. You'll notice the JProgressBar graphic only updates every 5%.

Is this changeable? Can it update with more precise changes like every 1%?

(note: the default UIManager is more precise. Remove the WindowsLookAndFeel code to see).

ryvantage
  • 13,064
  • 15
  • 63
  • 112
  • Well, you are explicitly telling JVM to use the `WindowsLookAndFeel` `UIManager`. This property is implementation dependent and may even vary between different editions of Windows. The only thing you can try to do is create your own version of JProgressBar and implement the behavior yourself. – initramfs Feb 16 '15 at 05:52
  • Well, either you're going to block the Event Dispatching Thread or violate the single thread rules of Swing... – MadProgrammer Feb 16 '15 at 05:53

1 Answers1

4

You're at risk of blocking the Event Dispatching Thread (Thread.sleep) or you are violating the single thread rules of Swing by updating the UI outside of the Event Dispatching Thread

Take a look at Concurrency in Swing for more details

Progress

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JProgressBar pb = new JProgressBar();
                pb.setStringPainted(true);

                SwingWorker worker = new SwingWorker() {
                    @Override
                    protected Object doInBackground() throws Exception {
                        for (int index = 0; index < 1000; index++) {

                            int progress = Math.round((index / 1000f) * 100f);
                            setProgress(progress);
                            Thread.sleep(50);

                        }
                        return null;
                    }
                };
                worker.addPropertyChangeListener(new PropertyChangeListener() {

                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        SwingWorker worker = (SwingWorker) evt.getSource();
                        if ("progress".equals(evt.getPropertyName())) {
                            pb.setValue(worker.getProgress());
                        }
                    }

                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());
                frame.add(pb);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                worker.execute();

            }
        });
    }

}

Updated

So, after digging about the WindowProgressBarUI, it would seem that, that's the way it's designed to work...that is, it paints in chunks/blocks.

You can use setStringPainted as I did in the example, which uses a different painting branch, but this might not suit your requirements

You can have a read through JProgressBar: low values will not be displayed for more details, but essentially, this is how the UI delegate is designed, so as to meet the requirements of the OS look and feel requirements

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thanks for the concern about Swing. [I am very aware of concurrency in Swing](http://stackoverflow.com/questions/20260372/swingworker-progressbar/20270356#20270356) this was just a quick throw together to illustrate the issue. I will look at the `setStringPainted` method. – ryvantage Feb 16 '15 at 13:32