Monday, January 14, 2008

Flat Button using swing

I always wonder about how people creating such a nice button and swing controls in swing. Swing provides the most customizable user interface. Though I am not expert in it, just tried myself to do something that will give me a better understanding of how all these things fit together.

All the components in swings are rendered by ComponentUI, so a component delegates the painting job to ComponentUI. For example, a JButton will be a rendered by BasicButtonUI (not exactly!). So ComponentUI which renders a component is decided based on the LAF (look & feel) selected. So to create a new look, you just create ComponentUI for all the swing components.

windows look and feel for JButton is provided by WindowsButtonUI.

When I first read, this I thought it's bit difficult to understand, actually it's pretty easy to understand. So I tried to create a Flat Button (you can think of them as buttons that appear on toolbar in standard Microsoft Office App.).

To create your own UI for Button, we start by extending MetalButtonUI, by just overriding some painting function, we can create customized LAF for the Button.

The picture below shows the button that I tried to implement.

Java code below shows how it is done:

public class JFlatButton extends JButton
{

protected PaintProperties paintProp;

public JFlatButton(String text)
{
super(text);
}

public JFlatButton(Action action) {
super(action);
}

public JFlatButton(Icon icon) {
super(icon);
}

public JFlatButton(String text,Icon icon) {
super(text,icon);
}

public void setUI(ButtonUI ui) {
// defaults
if(paintProp==null) {
paintProp = new PaintProperties();
paintProp.borderColor = Color.BLACK;
paintProp.rollOver1 = Color.WHITE;
paintProp.rollOver2 = Color.LIGHT_GRAY;

paintProp.pressed1 = paintProp.rollOver2;
paintProp.pressed2 = paintProp.rollOver1;
}
// override the UI
// always set our UI instead of platform based LAF!
super.setUI(new MetalButtonUI() {
protected void paintButtonPressed(Graphics g, AbstractButton b) {
// if back group paint required
if ( b.isContentAreaFilled() ) {
Dimension size = b.getSize();
Graphics2D g2d = (Graphics2D) g;

Paint paint = g2d.getPaint(); // save to restore

GradientPaint pt = new GradientPaint(new Point2D.Float(0,0),paintProp.pressed1,new Point2D.Float(0,b.getHeight()),paintProp.pressed2);
g2d.setPaint(pt);
g.fillRect(0, 0, size.width, size.height);

g.setColor(paintProp.borderColor);
g.drawRect(0,0,size.width-1,size.height-1);

g2d.setPaint(paint);
}
}

public void update(Graphics g, JComponent c) {
// under normal condition, don't render the background
// on roll over render my custom color
if(model.isRollover()) {
drawBG(g,c);
}
paint(g, c);
}

protected void drawBG(Graphics g,JComponent b) {
Graphics2D g2d = (Graphics2D) g;
Paint paint = g2d.getPaint(); // save to restore
GradientPaint pt = new GradientPaint(new Point2D.Float(0,0),paintProp.rollOver1,new Point2D.Float(0,b.getHeight()),paintProp.rollOver2);
g2d.setPaint(pt);
// fill button
g.fillRect(0,0,b.getWidth()-1,b.getHeight()-1);

// draw border line
g.setColor(paintProp.borderColor);
g.drawRect(0,0,b.getWidth()-1,b.getHeight()-1);

g2d.setPaint(paint); // restore
}

protected void paintFocus(Graphics g, AbstractButton b,
Rectangle viewRect, Rectangle textRect, Rectangle iconRect){
// nothing, focus not rendered!
}
});
// override some properties
this.setBorderPainted(false);
this.setOpaque(false);
}

public void paintComponent(Graphics g) {
super.paintComponent(g);
}

public PaintProperties getPaintProp() {
return paintProp;
}

public void setPaintProp(PaintProperties paintProp) {
this.paintProp = paintProp;
}
}

Code is self explanatory and sufficiently commented. (I don't understand some code, I will always sun's similar implementation. They are commented well! really well!!!)

After reading about this and implementing something with what I learned, really helped me to understand it clearly.

Here you can find something similar to this but with JTabbedPane .. Really good resource..

Happy swinging!!

3 comments:

Danilo said...
This comment has been removed by the author.
Danilo said...

I don't understand a part! What is PaintProperties() ?

Tinu said...

Thanks for the code, but it seems you've forgotten to post the class "PaintProperties".