Nicolas Frankel is an IT consultant with 10 years experience in Java / JEE environments. He likes his job so much he writes technical articles on his blog and reviews technical books in his spare time. He also tries to find other geeks like him in universities, as a part-time lecturer. Nicolas is a DZone MVB and is not an employee of DZone and has posted 231 posts at DZone. You can read more from them at their website. View Full User Profile

Flamingo Tutorial

06.28.2010
| 26101 views |
  • submit to reddit

In this article, I will provide you with the documentation to easily use the Flamingo framework and more precisely, its ribbon widget.

Introduction

Never say that Microsoft never innovates: in Office, it introduced an interesting concept, the ribbon band.

The ribbon band is a toolbar of sort. But whereas toolbars are fixed, ribbons layout can change according to the width they display. If you have such an application, just play with it for a few seconds and you will see the magic happens.

Recent versions of Swing do not have such widgets. However, I found the Flamingo project on java.net. Examples made with Flamingo look awfully similar to Office.

Trying to use Flamingo for the first time is no small feat since there’s no documentation on the Web, apart from Javadocs and the source for a test application. The following is what I understood since I began my trial and error journey.

The basics

Semantics
  • the ribbon is the large bar on the screenshot above. There can be only a single ribbon for a frame
  • a task is a tabbed group of one or more band. On the screenshot, tasks are Page Layout, Write, Animations and so on
  • a band is a group of one or more widgets. On the screenshot, bands are Clipboard, Quick Styles, Font and so on
Underlying concepts

The core difference between buttons in a toolbar and band in a ribbon bar is that bands are resizable. For examples, these are the steps for displaying the Document band, in regard to both its relative width and the ribbon width.

The last step is known as the iconified state. When you click on the button, it displays the entire band as a popup.

Your first ribbon

Setup

In order to use the Flamingo framework, the first step is to download it. If you’re using Maven, tough luck! I didn’t find Flamingo in central nor java.net repositories. So download it anyway and install it manually in your local (or enterprise) repository. For information, I choosed the net.java.dev.flamingo:flamingo location.

The frame

If you are starting from scratch, you’re lucky. Just inherit from JRibbonFrame: the method getRibbon() will provide you a reference to the ribbon instance. From there, you will be able to add tasks to it.

However, chances are you probably already have your own frame hierachy. In this case, you have to instantiate a JRibbon and add it on the NORTH location of your BorderLayout-ed frame.

In both cases, the result should be something akin to that:

Adding a task

Tasks represent logical band grouping. They look like tabs and act the part too. Let’s add two such tasks aptly named “One” and “Two”.

public class MainFrame extends JRibbonFrame {

public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {

MainFrame frame = new MainFrame();

frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);

RibbonTask task1 = new RibbonTask("One");
RibbonTask task2 = new RibbonTask("Two");

frame.getRibbon().addTask(task1);
frame.getRibbon().addTask(task2);
}
});
}
}

Notice the getRibbon() method on the JRibbonFrame. It is the reference on the ribbon bar.

Also notice that the addTask() method accepts a task but also a varargs of JRibbonBand. And if you launch the above code, it will fail miserably with the following error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot have empty ribbon task
at org.jvnet.flamingo.ribbon.RibbonTask.<init>(RibbonTask.java:85)
at MainFrame$1.run(MainFrame.java:37)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Adding bands

To satisfy our Flamingo friend, let’s add a ribbon band to each task. The constructor of JRibbonBand takes two argument, the label and an instance of a previously unknown class, ResizableIcon. It will be seen in detail in the next section.

As for now, if you just create the RibbonTask with a reference to the JRibbonBand and launch the application, you will get such an error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot have empty ribbon task
at org.jvnet.flamingo.ribbon.RibbonTask.<init>(RibbonTask.java:85)
at MainFrame$1.run(MainFrame.java:37)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Remember that bands are resizable? Flamingo needs information on how to do it. Before initial display, it will check that those policies are consistent. By default, they are not and this is the reason why it complains: Flamingo requires you to have at least the iconified policy that must be in the last place. In most cases, however, you’ll want to have at least a normal display in the policies list.

Let’s modify the code to do it:

JRibbonBand band1 = new JRibbonBand("Hello", null);
JRibbonBand band2 = new JRibbonBand("world!", null);

band1.setResizePolicies((List) Arrays.asList(new IconRibbonBandResizePolicy(band1.getControlPanel())));
band2.setResizePolicies((List) Arrays.asList(new IconRibbonBandResizePolicy(band1.getControlPanel())));

RibbonTask task1 = new RibbonTask("One", band1);
RibbonTask task2 = new RibbonTask("Two", band2);

The previous code let us at least see something:

Adding buttons (at last!)

Even if the previous compiles and runs, it still holds no interest. Now is the time to add some buttons!

JCommandButton button1 = new JCommandButton("Square", null);
JCommandButton button2 = new JCommandButton("Circle", null);
JCommandButton button3 = new JCommandButton("Triangle", null);
JCommandButton button4 = new JCommandButton("Star", null);

band1.addCommandButton(button1, TOP);
band1.addCommandButton(button2, MEDIUM);
band1.addCommandButton(button3, MEDIUM);
band1.addCommandButton(button4, MEDIUM);

Too bad there’s no result! Where are our buttons? Well, they are well hidden. Remember the resize policies? There’s only one, the iconified one and its goal is only to display the iconified state. Just update the policies line with the code:

band1.setResizePolicies((List) Arrays.asList(new CoreRibbonResizePolicies.None(band1.getControlPanel()),
new IconRibbonBandResizePolicy(band1.getControlPanel())));

The result looks the same at first, but when you resize the frame, it looks like this:

Even if it’s visually not very attractive, it looks much better than before. We see the taks, the name of the band and the labels on our four buttons.

Resizable icons

The JCommandButton‘s constructor has 2 parameters: one for the label, the other for a special Flamingo class, the ResizableIcon. Since Flamingo is all about displaying the same button in different sizes, that’s no surprise. Resizable icons can be constructed from Image, ico resources or even SVG.

Let’s add an utility method to our frame, and spice up our UI:

public static ResizableIcon getResizableIconFromResource(String resource) {

return ImageWrapperResizableIcon.getIcon(MainFrame.class.getClassLoader().getResource(resource), new Dimension(48, 48));
}
...
JCommandButton button1 = new JCommandButton("Square", getResizableIconFromResource("path"));
JCommandButton button2 = new JCommandButton("Circle", getResizableIconFromResource("to"));
JCommandButton button3 = new JCommandButton("Triangle", getResizableIconFromResource("the"));
JCommandButton button4 = new JCommandButton("Star", getResizableIconFromResource("resource"));

band1.addCommandButton(button1, TOP);
band1.addCommandButton(button2, MEDIUM);
band1.addCommandButton(button3, MEDIUM);
band1.addCommandButton(button4, MEDIUM);

This is somewhat more satisfying:

Choosing policies

Now we’re ready to tackle Flamingo’s core business, resizing management. If you have Office, and played with it, you saw that the resizing policies are very rich. And we also saw previously that with only two meager policies, we can either see the iconified display or the full display.

Let’s see how we could go further. You probably noticed that the addCommandButton() of JRibbonBand has 2 parameters: the button to add and a priority. It is this priority and the policy that Flamingo use to choose how to display the band.

Priorities are the following: TOP, MEDIUM and LOW.

Policies are:

Policy Description
CoreRibbonResizePolicies.None Command buttons will be represented in the TOP state (big button with label and icon)
CoreRibbonResizePolicies.Mid2Mid Command buttons that have MEDIUM priority will be represented in the MEDIUM state (small button with label and icon)
CoreRibbonResizePolicies.Mid2Low Command buttons that have MEDIUM priority will be represented in the LOW state (small button only icon)
CoreRibbonResizePolicies.High2Mid Command buttons that have HIGH priority will be represented in the MEDIUM state
CoreRibbonResizePolicies.High2Low Command buttons that have HIGH priority will be represented in the LOW state
CoreRibbonResizePolicies.Low2Mid Command buttons that have LOW priority will be represented in the MEDIUM state
CoreRibbonResizePolicies.Mirror Command buttons will be represented in the priority they were assigned to
IconRibbonBandResizePolicy Command buttons will be not represented. The entire band will be represented by a command button that when pressed will show a popup of the unconstrained band

Now, you have all elements to let you decide which policies to apply. There’s one rule though: when setting policies, the width of the band must get lower and lower the higher the index of the policy (and it must end with the IconRibbonBandResizePolicy) let you’ll get a nasty IllegalStateException: Inconsistent preferred widths (see above).

Let’s apply some policies to our band:

band1.setResizePolicies((List) Arrays.asList(
new CoreRibbonResizePolicies.None(band1.getControlPanel()),
new CoreRibbonResizePolicies.Mirror(band1.getControlPanel()),
new CoreRibbonResizePolicies.Mid2Low(band1.getControlPanel()),
new CoreRibbonResizePolicies.High2Low(band1.getControlPanel()),
new IconRibbonBandResizePolicy(band1.getControlPanel())));

This will get us the following result:

Note: there won’t be any iconified state in my example since the band does not compete for space with another one.

More features

Flamingo’s ribbon feature let you also:

  • add standard Swing components to the ribbon
  • add a menu on the top left corner

  • integration with standard Look and Feels

Those are also undocumented but are much easier to understand on your own.

It also has other features:

  • Breadcrumb bar
  • Command button strips and panels

Conclusion

Flamingo is a nice and powerful product, hindered by a big lack of documentation. I hope this article will go one step toward documenting it.

Here are the sources for this article in Eclipse/Maven format.

To go further:

 

From http://blog.frankel.ch/flamingo-tutorial

Published at DZone with permission of Nicolas Frankel, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Jing Ge replied on Wed, 2010/06/30 - 2:56am

Thanks for the great tutorial!

Walter Laan replied on Wed, 2010/06/30 - 2:57am

To get cast the Arrays.asList without warning use:

band1.setResizePolicies(Arrays.<RibbonBandResizePolicy>asList(...));

but a var args overload would be nicer. If the setResizePolicies method accepted List<? extends RibbonBandResizePolicy> it would work as well.

Michael Eric replied on Wed, 2012/09/26 - 3:35pm

Great tutorial, helped me a lot with integrating flamingo into a project. Only thing i missed is how to use a JCommandButton with a JCommandPopupMenu (button.setPopupCallback(your PopupPanelCallback etc.).

linux archive 

Brijesh Chaparala replied on Wed, 2012/03/14 - 4:39am

After running the following code i can only see a blank frame… can some one help me out by finding the errors in this and please send me the code if anyone got the right output displayed

exception:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Inconsistent preferred widths
Ribbon band 'Hello has the following resize policies
    org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies$None with preferred width 4
    org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies$Mirror with preferred width 4
    org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies$Mid2Low with preferred width 4
    org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies$High2Low with preferred width 4
    org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy with preferred width 46
org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies$High2Low with pref width 4 is followed by resize policy org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy with larger pref width

    at org.pushingpixels.flamingo.internal.utils.FlamingoUtilities.checkResizePoliciesConsistency(FlamingoUtilities.java:579)
    at org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand.setRibbonTask(AbstractRibbonBand.java:539)
    at org.pushingpixels.flamingo.api.ribbon.RibbonTask.<init>(RibbonTask.java:90)
    at NewFrame$1.run(NewFrame.java:80)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
    at java.awt.EventQueue.access$000(EventQueue.java:84)
    at java.awt.EventQueue$1.run(EventQueue.java:602)
    at java.awt.EventQueue$1.run(EventQueue.java:600)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

 

CODE:

import java.awt.Dimension;
import java.util.Arrays;
import java.util.List;
import javax.swing.SwingUtilities;
import org.pushingpixels.flamingo.api.common.JCommandButton;
import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon;
import org.pushingpixels.flamingo.api.common.icon.ResizableIcon;
import org.pushingpixels.flamingo.api.ribbon.*;
import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies;
import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy;

;

public class NewFrame extends JRibbonFrame {

/** Serial version unique id. */

private static final long serialVersionUID = 1L;

public static ResizableIcon getResizableIconFromResource(String resource) {

return ImageWrapperResizableIcon.getIcon(NewFrame.class
.getClassLoader().getResource(resource), new Dimension(48, 48));
}

/**
* Entry point method.
*
* @param args
* Application arguments
*/
public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {
private RibbonElementPriority TOP;
private RibbonElementPriority MEDIUM;

@Override
public void run() {

NewFrame frame = new NewFrame();

frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);

JRibbonBand band1 = new JRibbonBand(“Hello”,getResizableIconFromResource(“48px-Crystal_Clear_app_Staroffice.png”));
JRibbonBand band2 = new JRibbonBand(“world!”, null);

JCommandButton button1 = new JCommandButton(“Square”,getResizableIconFromResource(“48px-Crystal_Clear_app_kthememgr.png”));
JCommandButton button2 = new JCommandButton(“Circle”,getResizableIconFromResource(“48px-Crystal_Clear_app_ksame.png”));
JCommandButton button3 = new JCommandButton(“Triangle”,getResizableIconFromResource(“48px-Crystal_Clear_app_error.png”));
JCommandButton button4 = new JCommandButton(“Star”,getResizableIconFromResource(“48px-Crystal_Clear_action_bookmark.png”));

band1.addCommandButton(button1, TOP);
band1.addCommandButton(button2, MEDIUM);
band1.addCommandButton(button3, MEDIUM);
band1.addCommandButton(button4, MEDIUM);

band1.setResizePolicies((List) Arrays.asList(
new CoreRibbonResizePolicies.None(band1.getControlPanel()),
new CoreRibbonResizePolicies.Mirror(band1.getControlPanel()),
new CoreRibbonResizePolicies.Mid2Low(band1.getControlPanel()),
new CoreRibbonResizePolicies.High2Low(band1.getControlPanel()),
new IconRibbonBandResizePolicy(band1.getControlPanel())));
band2.setResizePolicies((List) Arrays
.asList(new IconRibbonBandResizePolicy(band2
.getControlPanel())));

RibbonTask task1 = new RibbonTask(“One”, band1);
RibbonTask task2 = new RibbonTask(“Two”, band2);

frame.getRibbon().addTask(task1);
frame.getRibbon().addTask(task2);

frame.getRibbon().setApplicationMenu(new RibbonApplicationMenu());
}
});
}
}

Angel Sanchez replied on Thu, 2012/05/10 - 1:20pm

Where can I get  Flamingo library as a zip package? 

Liezel Jandayan replied on Tue, 2012/05/22 - 9:48pm

So, are they saying that 1% of global Java developers were suddenly laid off in October? That seems unlikely!-Steven Delarge

Cristopher Juan... replied on Sat, 2012/09/22 - 10:06pm

Please can reupload the library to install my NetBeans and Eclipse

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.