Skip to main content

Playing with Color

Posted by kirillcool on January 17, 2007 at 9:40 AM PST

Seeing that this subject has popped up twice over the last two days (here and here), i decided to share a little trick to make a Color object mutable.

In general, the Color class doesn't allow changing the existing object. However, since it's not final, we can use the Delegate pattern and direct all methods to a single "master" color that is computed dynamically. The master color computation can depend, for example, on rollover state (illustrated below) which will allow creating rollover effects without triggering the property change events (on the "foregroundColor" property).

Here is how it's done.

public class RolloverForeground extends JFrame {



  public RolloverForeground() {

    this.setLayout(new FlowLayout());



    final JButton button = new JButton("sample");

    button.setForeground(new Color(0x000000) {

      private Color getDelegate() {

        return button.getModel().isRollover() ? Color.red : Color.black;

      }



      @Override

      public Color brighter() {

        return getDelegate().brighter();

      }



      @Override

      public synchronized PaintContext createContext(ColorModel cm,

          Rectangle r, Rectangle2D r2d, AffineTransform xform,

          RenderingHints hints) {

        return getDelegate().createContext(cm, r, r2d, xform, hints);

      }



      @Override

      public Color darker() {

        return getDelegate().darker();

      }



      @Override

      public boolean equals(Object obj) {

        return getDelegate().equals(obj);

      }



      @Override

      public int getAlpha() {

        return getDelegate().getAlpha();

      }



      @Override

      public int getBlue() {

        return getDelegate().getBlue();

      }



      @Override

      public float[] getColorComponents(ColorSpace cspace,

          float[] compArray) {

        return getDelegate().getColorComponents(cspace, compArray);

      }



      @Override

      public float[] getColorComponents(float[] compArray) {

        return getDelegate().getColorComponents(compArray);

      }



      @Override

      public ColorSpace getColorSpace() {

        return getDelegate().getColorSpace();

      }



      @Override

      public float[] getComponents(ColorSpace cspace, float[] compArray) {

        return getDelegate().getComponents(cspace, compArray);

      }



      @Override

      public float[] getComponents(float[] compArray) {

        return getDelegate().getComponents(compArray);

      }



      @Override

      public int getGreen() {

        return getDelegate().getGreen();

      }



      @Override

      public int getRed() {

        return getDelegate().getRed();

      }



      @Override

      public int getRGB() {

        return getDelegate().getRGB();

      }



      @Override

      public float[] getRGBColorComponents(float[] compArray) {

        return getDelegate().getRGBColorComponents(compArray);

      }



      @Override

      public float[] getRGBComponents(float[] compArray) {

        return getDelegate().getRGBComponents(compArray);

      }



      @Override

      public int getTransparency() {

        return getDelegate().getTransparency();

      }

    });



    this.add(button);

    button.getModel().addChangeListener(new ChangeListener() {

      public void stateChanged(ChangeEvent e) {

        button.repaint();

      }

    });



    this.setSize(200100);

    this.setLocationRelativeTo(null);

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  }



  public static void main(String[] argsthrows Exception {

    SwingUtilities.invokeLater(new Runnable() {

      public void run() {

        new RolloverForeground().setVisible(true);

      }

    });

  }

}

The most interesting part is the getDelegate method in our foreground color. It returns black or red depending on the rollover state of the model. All the Color methods use this color, delegating the real implementation to it. Screenshot of the application in default state:

default.png

Screenshot of the application in rollover state:

rollover.png

Note that this technique can be easily extended to smoothly animate the foreground color from black to red without triggering property change events along the way.

Related Topics >>