Introduction

screen1.PNG

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:

structure.PNG

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 AccordionItems 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
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架