Introduction
This article shows many aspects of Swing programming. First of all, it shows the hierarchy of nested JComponents
. It explains how to create a NULL
layout component where inner components have full degree of freedom. It also creates a simple trick to animate inner components with a great visual effect.
The father component provides many methods to modify visual aspect of AccordionMenu
as you can see from the screenshot. With a few code lines, you can full change an AccordionMenu
look. Especially, you can use your custom images for item icons letting you do a full personalization.
All source code is full commented, so I will explain below only few important aspects of this work.
Background
The structure of this JComponent
is quite simple:
As you can see from the image, the component is made of three important pieces.
AccordionRootItem
is the Text Object displayed as Root Menu Item. AccordionLeafItem
is the Text Object displayed as Leaf Menu Item under a main root. AccordionBranch
is the JPanel
container under a main root, in which leafs are displayed; it creates an accordion effect by collapsing and showing each branch.
The main JComponent
is the father container that displays AccordionItem
s by parameters shown in the left side of image above. menuSize
is the size of each Root Menu Item, by this value multiplied for the number of Root Menu Items, it calculates the free space available for a branch.
By the way, each single AccordionItem
can be directly reached from outside (next, we will see how), so you can modify them individually; For example, you can change visual aspect of a single Leaf Item or Menu Root.
Finally, we can see that each AccordionItem
is a simple inheritance of JLabel
, so we can simply set an icon image to each item as you can see from the screen shot.
Now we can see some aspects deeply. (If are interested. :))
AccordionMenu
This is the main container class. I don't show it entirely because is too long, so I explain only the crucial part.
First, we can see that it has two default nested constructors:
public AccordionMenu() {
this.addComponentListener(getDefaultComponentAdapter());
this.setLayout(null);
this.leafMap = new TreeMap<AccordionRootItem, List<AccordionLeafItem>>();
}
public AccordionMenu(String menuDescriptor) {
this();
createMenusFromDescriptor(menuDescriptor);
}
The first is the basic constructor. It provides to create the TreeMap
element that is the core model of this object. TreeMap
contains structure of menu, and at the same time links each AccordionItem
together for a fast link to each element separately. If you use this constructor, you have to call special methods to create structure of menu:
public void addNewMenu(String menuName, String menuTitle) {...}
public void addNewLeafTo(String menuName, String leafName, String leafTitle) {...}
addNewMenu
provides to create a new Root Menu Item, after that, you can call addNewLeafTo
to create a new Leaf Menu Item linked to selected father menu.
If you use the second constructor, you can pass the menu structure by String
. You can see code comments to understand how it works by createMenusFromDescriptor
method; however, the menu structure is of type:
String menuDescriptor = ""
+ "Menu One,menu1:"
+ "Sub Menu 1,submenu1.1;"
+ "Sub Menu 2,submenu1.2;"
+ "Sub Menu 3,submenu1.3;"
+ "!"
+ "Menu Two,menu2:"
+ "Sub Menu 1,submenu2.1;"
+ "Sub Menu 2,submenu2.2;"
+ "Sub Menu 3,submenu2.3;"
+ "!"
+ "Menu Three,menu3:"
+ "Sub Menu 1,submenu3.1;"
+ "Sub Menu 2,submenu3.2;"
+ "Sub Menu 3,submenu3.3;"
+ "!"
+ "Menu Four,menu4:"
+ "Sub Menu 1,submenu4.1;"
+ "Sub Menu 2,submenu4.2;"
+ "Sub Menu 3,submenu4.3;";
It is a simple string
although is multi row written. It has standard regex separators (must use); but you can better understand by trying to use it.
In NewJFrame.java
file in source code, there is the showcase of AccordionMenu
where it is explained how to use two kind of constructors.
The core animation algorithm is:
private void startAnimation() {
Thread thread = new Thread(new Runnable() {
public void run() {
showingSize = 0;
hidingSize = branchAvaiableSpace;
int step = 30;
while (hidingSize > 0) {
showingSize += step;
hidingSize -= step;
update();
repaint();
try {
Thread.sleep(timeStep);
} catch (InterruptedException ex) {
Logger.getLogger(AccordionMenu.class.getName()).log
(Level.SEVERE, null, ex);
}
}
showingSize = branchAvaiableSpace;
hidingSize = 0;
update();
repaint();
}
});
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
Not-declared variables are class properties. It simply creates a volatile thread that dies when it finishes its task. Considering that branchAvaiableSpace
is the full space available for displaying a branch, it creates two sub variables hidingSize
and showingSize
and decreases/increases them. They are the new two dimensions of branch that are hiding and the branch that is showing. (You can test the executable file to see what I mean. :))
However paintComponent
overridden method of AccordionMenu
ignores that there is an animation, it simple draws both branches (hiding and showing branches) with two respective size ( hidingSize
and showingSize
). The animation Thread instead modifies these parameters.
You can externally modify timeStep
for the animation. Higher values slow the animation.
Other methods:
public void setBackground(Color back) {...}
public void setMenuIcons(ImageIcon normal, ImageIcon selected) {...}
public void setAllLeafIcons(ImageIcon normal, ImageIcon selected) {...}
public void setLeafIcons(String menuName, ImageIcon normal, ImageIcon selected) {...}
public void setMenuBorders(Border border) {...}
public void setMenuHorizontalAlignment(int alignment) {...}
public void setLeafHorizontalAlignment(int alignment) {...}
public void setSelectionColor(Color selectionColor) {...}
public void setFont(Font font) {...}
public void setForeground(Color fg) {...}
They are cascade methods. They simply browse each Accordion
Item and call respective method of the same name. In some cases, the main component provides to update UI tree to avoid visual errors.
The methods to browse Accordion
Items are:
public AccordionLeafItem getLeaf(String name) {...}
public List<AccordionLeafItem> getLeafsOf(String menuName) {...}
public List<AccordionLeafItem> getAllLeafs() {...}
public AccordionRootItem getMenu(String name) {...}
public List<AccordionRootItem> getMenus() {...}
In NewJFrame.java file, you can see some examples to use these browsing methods; however they are very simple, their name also explains their function.
Code Usage
For "Free Knowledge" (that is my aim), I posted as always the source code. You can use that code to modify it and expand this work. However the executable BIN file also can be used as Jar Library file to link in your projects, so you can simply use this component without worrying about how it works.
If you want, you can also use it directly in NetBeans visual editor because is a Java Bean; the bad thing is that I preferred to not initialize it with a fake menu tree (for example like JTree
does) because class constructor could become difficult to understand.
History
- 8th November, 2010: Initial post