Monday, August 30, 2010

GWT: iPhone Like Buttons

In a new entry dedicated to GWT we're going to learn how to create an iPhone like button.

The objective is to create a rounded button with an image and support for badges.

Let's compare the iPhone button with the result of our tutorial
iPhone Button
Our CoolButton

Do you like it? Then stay with me for the How To.

Create the new Widget

We'll start creating a new Composite Widget, we'll call it CoolButton.

public class CoolButton extends Composite implements HasClickHandlers,
  ClickHandler {

 private final AbsolutePanel wrapper;
 private final PushButton pushButton;
 private final Label badge;
 
 public CoolButton(Image buttonIcon) {
  
  wrapper = new AbsolutePanel();
  badge = new Label();
  badge.setStyleName("badges");
  badge.setVisible(false);

  pushButton = new PushButton(buttonIcon);
  pushButton.setStyleName("CoolButton");

  wrapper.add(pushButton, 0, 20);
  wrapper.add(badge, 40, 10);
  wrapper.setWidth("75px");
  wrapper.setHeight("80px");

  this.addClickHandler(this);

  initWidget(wrapper);
 }
 
 @Override
 public HandlerRegistration addClickHandler(ClickHandler handler) {
  return addDomHandler(handler, ClickEvent.getType());
 }

 @Override
 public void onClick(ClickEvent event) {
  //toggleButton.fireEvent(event);
 }

 public void setEnabled(boolean enabled) {
  pushButton.setEnabled(enabled);
 }

 public void setBadge(int total) {
  if (total > 0) {
   badge.setVisible(true);
   badge.setText(String.valueOf(total));
  } else {
   badge.setVisible(false);
  }
 }
}

We'll analyze this code:

We're creating a Composite Widget, so basically we're creating a wrapper around already existing Widgets and adding some logic on top.

private final AbsolutePanel wrapper;
 private final PushButton pushButton;
 private final Label badge;

For you Widget we're using an AbsolutePanel, a PushButton and a Label.

The PushButton is our main component. It's a real button and displays an image, so it's perfect for our needs. The Label will be used to show the badge. Finally, the AbsolutePanel will allow us to layout the components, overlapping them to create the desired effect.

public CoolButton(Image buttonIcon) {
  
  wrapper = new AbsolutePanel();
  badge = new Label();
  badge.setStyleName("badges");
  badge.setVisible(false);

  pushButton = new PushButton(buttonIcon);
  pushButton.setStyleName("CoolButton");

  wrapper.add(pushButton, 0, 20);
  wrapper.add(badge, 40, 10);
  wrapper.setWidth("75px");
  wrapper.setHeight("80px");

  this.addClickHandler(this);

  initWidget(wrapper);
 }

In the constructor we create the badge label, and hide it by default. We'll show it later if needed. We create also the button with the specified image.

The tricky part here is the use of AbsolutePanel. The AbsolutePanel doesn't resize automatically to show properly all its children, as other LayoutPanels, so we need to position everything manually and make sure that it will fit.

wrapper.add(pushButton, 0, 20);

We place the button in the coordinates 0,20 that leaves some vertical space on top of the button that will be needed to place the badge.

wrapper.add(badge, 40, 10);

The badge is placed in the coordinates 40,10 so vertically will overlap with the button, and horizontally will be close to the right side.

wrapper.setWidth("75px");
wrapper.setHeight("80px");

Finally we make sure we've enough space to show everything.

All these coordinates and sizes are hardcoded, because they're relative to the image size.

The rest of the code doesn't have too much mystery. Since it's a button we need to add the needed code to fire the ClickEvents and register a ClickHandler. Also we need a method to set the badge and properly show and hide it.

With that we've our widget done. Now we need to attack the styling, so take a look to the CSS.

.badges {
 text-align: center;
 display: inline-block;
 font-size: 16px;
 color: white;
 background: #ff3333;
 border: 2px solid white;
 padding: 0px 5px;
 -moz-border-radius: 15px;
 -webkit-border-radius: 15px;
 -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
 -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
}

The badges style is quite simple, but let's check the different elements:
  • color: Defines de text color
  • background: defines the color of the background
  • border: defines the white border
  • border-radius: defines de rounding border (for Firefox and Webkit)
  • box-shadow: defines a tiny shadow for the badge (also for Firefox and Webkit)
.CoolButton {
 padding-left: 2px;
 padding-top: 2px;
 cursor: pointer;
 border: 1px solid black;
 -moz-border-radius: 5px;
 -webkit-border-radius: 5px;
 -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
 -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
 background: #00b5e2;
}

The button style is again the same CSS elements.

Note: That's not the case in our example, but GWT doesn't recognize properly some CSS styles, for those cases we need to wrap it in a literal call

background: -moz-linear-gradient(top, #e4e4e4, #cfcfcf);
background: literal("-webkit-gradient(linear, 0% 0%, 0% 100%, from(#e4e4e4), to(#cfcfcf))");

And that's it. You can download the code from the next link: iPhoneDemo - CoolButton

See you soon.

3 comments:

  1. Nice! Can I use it in commercial application?

    ReplyDelete
  2. @Anonymous Sure you can. Everything I publish here can be freely reused.
    Just remember to send me a link or screenshot to see the final result ;)

    ReplyDelete
  3. Thanks! Will let you know once site is complete.

    ReplyDelete