Skip to main content

Crazy JButton painters

Posted by alexfromsun on October 5, 2006 at 10:51 AM PDT

Inspired by some latest blogs I decided to find the most hacky and crazy method to paint on a component


I chose JButton as the most well-known component to play with



The goal is to implement some custom painting to a button without subclassing it and with no custom UI delegate

Custom component

As you probably know all Swing components are Containers,
that means that you can add any children components to them.
However not all Swing components support it, e.g. it usually doesn't make sense to add children to a components like JSlider or JProgressBar

JButton is not supposed to be used as a Container but we certainly can try:

JButton button = new JButton("I am a JButton");         
// JButton's layout is null by default
button.setLayout(new FlowLayout());
        
button.add(new JButton("Surprise!"));

It is the clue to the first trick, you can add a translusent component to the button to make it looks different

button.setLayout(new BorderLayout()); 
button.add(new ComponentPainter());
                    
button.revalidate();
button.repaint();
.                    
.

class ComponentPainter extends JPanel {
    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g.create();
        // It enables painting outside the component's border
        g2.setClip(null);
        // Make it translucent
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
        // Take button's insets into account
        Insets insets = button.getInsets();
        // Custom painting
        g2.setColor(Color.ORANGE);
        g2.fillOval(-insets.left, -insets.top, button.getWidth(), button.getHeight());
        g2.setColor(Color.GRAY);
        g2.drawOval(-insets.left, -insets.top, button.getWidth(), button.getHeight());
        g2.dispose();
    }
}

Unusual border

Borders in Swing are used to paint decorations around a Component,

but we are going to break the rules and create a Border instance to paint to the whole component's area

class BorderPainter extends AbstractBorder { 
   
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
        g2.setPaint(new GradientPaint(0, 10, Color.BLACK, 10, 10, Color.RED, true));
        // We are free to paint wherever we want :-)
        g2.fillRect(0, 0, button.getWidth(), button.getHeight());
    }
}

The only problem with this approach is that a component can have only one border, and new border will replace the old one.

javax.swing.border.CompoundBorder will help us to compose the existing border with the additional one:

Border oldBorder = button.getBorder(); 
button.setBorder(new CompoundBorder(oldBorder, new BorderPainter()));

Misuse of Icon

The last dirty trick involves javax.swing.Icon

it has a paintIcon() method with suggested coordinates as parameters, but as you can guess we are not going to use them.

There might be the same problem as we already mentioned for Borders:
you can set only one icon per a button


But it is not a problem for a real hacker :-)

Icon oldIcon = button.getIcon(); 
button.setIcon(new IconPainter(oldIcon));
.
.
class IconPainter implements Icon {
    private final Icon delegateeIcon;

    public IconPainter(Icon innerIcon) {
        this.delegateeIcon = innerIcon;
    }

    public int getIconWidth() {
        if (delegateeIcon != null) {
            return delegateeIcon.getIconWidth();
        }
        return 0;
    }

    public int getIconHeight() {
        if (delegateeIcon != null) {
            return delegateeIcon.getIconHeight();
        }
        return 0;
    }

    public void paintIcon(Component c, Graphics g, int x, int y) {
        if (delegateeIcon != null) {
            delegateeIcon.paintIcon(c, g, x, y);
        }
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
        g2.setPaint(new GradientPaint(10, 0, Color.RED, 10, 10, Color.BLUE, true));
        // It is a special icon, it is not interesed in x, y and icon's size 
        g2.fillRect(0, 0, button.getWidth(), button.getHeight());
    }
}

Demo

There is a demo with all this crazy stuff implemented

I have finally found the modern and fresh look for my buttons !

Source code

Conclusion

Despite the fact the provided tricks work

please consider this blog as a joke


I hope no one takes it seriously and starts using something like described "IconPainter"

Have a nice day

alexp

;-)

Related Topics >>