1

Continuing from Setting the colors of a JProgressBar text I would like to be able to set the text colors of JProgressBars in my program dependent upon process state and without moving away from System look and feel.

From one of the answers on Setting the colors of a JProgressBar text , it looks like I can only set text colors without changing the UI to a single set of values, so I could choose neutral values that look decent regardless of the foreground and progress, but I would like to fully customize the JProgressBars.

Below is some example code with three look and feels trying to switch from "good" to "bad" status. Obviously, the BasicProgressBarUI can be switched over fully, but the Metal default and System look and feel cannot, they can be assigned different default values at creation, but once created, they appear to be fixed.

SSCCE:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.basic.BasicProgressBarUI;


@SuppressWarnings("serial")
public class ProgressBarTester extends JFrame {

    public ProgressBarTester() {
        super( "Progress Bar Tester" );
        setLayout( new BorderLayout() );

        JPanel goodPanel = new JPanel( new FlowLayout() );
        goodPanel.setLayout( new FlowLayout() );
        JPanel badPanel = new JPanel( new FlowLayout() );
        badPanel.setLayout( new FlowLayout() );

        JProgressBar plainBarG = new JProgressBar();
        plainBarG.setValue( 50 );
        plainBarG.setStringPainted( true );
        plainBarG.setForeground( Color.green.darker() );

        plainBarG.setUI( new BasicProgressBarUI() {

            protected Color getSelectionBackground() {
                return Color.green.darker();
            }
            protected Color getSelectionForeground() {
                return Color.white;
            }
        } );
        goodPanel.add( plainBarG );


        JProgressBar plainBarB = new JProgressBar();
        plainBarB.setValue( 50 );
        plainBarB.setStringPainted( true );
        plainBarB.setForeground( Color.red.darker() );

        plainBarB.setUI( new BasicProgressBarUI() {

            protected Color getSelectionBackground() {
                return Color.red.darker();
            }
            protected Color getSelectionForeground() {
                return Color.black;
            }
        } );
        badPanel.add( plainBarB );

        UIManager.put( "ProgressBar.selectionForeground", Color.white );
        UIManager.put( "ProgressBar.selectionBackground", Color.green.darker() );

        JProgressBar javaDefaultG = new JProgressBar();
        javaDefaultG.setValue( 50 );
        javaDefaultG.setStringPainted( true );
        javaDefaultG.setForeground( Color.green.darker() );
        goodPanel.add( javaDefaultG );

        UIManager.put( "ProgressBar.selectionForeground", Color.black );
        UIManager.put( "ProgressBar.selectionBackground", Color.red.darker() );

        JProgressBar javaDefaultB = new JProgressBar();
        javaDefaultB.setValue( 50 );
        javaDefaultB.setStringPainted( true );
        javaDefaultB.setForeground( Color.red.darker() );
        badPanel.add( javaDefaultB );

        try {
            UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        UIManager.put( "ProgressBar.selectionForeground", Color.white );
        UIManager.put( "ProgressBar.selectionBackground", Color.green.darker() );

        JProgressBar systemG = new JProgressBar();
        systemG.setValue( 50 );
        systemG.setStringPainted( true );
        systemG.setForeground( Color.green.darker() );
        goodPanel.add( systemG );

        UIManager.put( "ProgressBar.selectionForeground", Color.black );
        UIManager.put( "ProgressBar.selectionBackground", Color.red.darker() );

        JProgressBar systemB = new JProgressBar();
        systemB.setValue( 50 );
        systemB.setStringPainted( true );
        systemB.setForeground( Color.red.darker() );
        badPanel.add( systemB );

        this.add( goodPanel, BorderLayout.NORTH );
        this.add( badPanel, BorderLayout.SOUTH );

        pack();
        setVisible( true );

        try {
            Thread.sleep( 2000 );
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        plainBarG.setForeground( Color.red.darker() );
        plainBarG.setUI( new BasicProgressBarUI() {
            protected Color getSelectionBackground() {
                return Color.red.darker();
            }
            protected Color getSelectionForeground() {
                return Color.black;
            }
        } );
        plainBarB.setForeground( Color.green.darker() );
        plainBarB.setUI( new BasicProgressBarUI() {

            protected Color getSelectionBackground() {
                return Color.green.darker();
            }
            protected Color getSelectionForeground() {
                return Color.white;
            }
        } );

        javaDefaultG.setForeground( Color.red.darker() );
        javaDefaultB.setForeground( Color.green.darker() );

        systemG.setForeground( Color.red.darker() );
        systemB.setForeground( Color.green.darker() );
    }

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

}

As shown in the SSCCE, it is possible to independently set the text colors, but not to do so dynamically. All JProgressBars start with some form of "good" or "neutral" status and may eventually change to a "bad" status.

Community
  • 1
  • 1
chirality
  • 887
  • 7
  • 12
  • 1
    I am a bit confused: do you want to dynamically change the colour of the JProgressBar say, based on the progress status? – Anto Mar 14 '14 at 20:27
  • 1
    In my program, it's actually based on the status of a file transfer controlled by another thread and the color-coding is based on the state of the thread/transfer. I'm thinking Green for working, Yellow for stall, Red for any kind of premature disconnect or failure. I can get the status and change the color of the bar without any issues. – chirality Mar 14 '14 at 20:47
  • It would be possible to `setStringPainted(false);` and paint the text yourself. I believe that's the only way. I would recommend *not* doing this with the system LAF. It will look very bad on, for example, Mac OS X where the completed bar is blue. – Radiodef Mar 14 '14 at 23:51

1 Answers1

3

As you observe, the UI delegate controls how it uses the specified colors. While updating the colors dynamically is superficially appealing, it invites usability problems with respect to hue and contrast. Instead, consider adding a dynamically colored Border or Icon. An example of the former is seen here; an example of the latter is seen here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Doesn't tell me how to do it, but it does give me a good reason not to do so. – chirality Mar 14 '14 at 23:57
  • I'd use a `PropertyChangeEvent`, for [example](http://stackoverflow.com/a/10523401/230513), to notify the ancillary indicator. – trashgod Mar 15 '14 at 00:33