Java 手风琴菜单,视觉效果和自定义
创建一个用作手风琴菜单的智能 Java 组件。解释了如何创建视觉过渡效果和简单的图标管理。还展示了如何创建一个具有简单级联管理功能的组件层次结构。
引言

本文展示了 Swing 编程的许多方面。首先,它展示了嵌套 JComponents
的层次结构。它解释了如何创建一个 NULL
布局组件,其中内部组件具有完全的自由度。它还通过一个巧妙的技巧来实现内部组件的动画,带来出色的视觉效果。
父组件提供了许多方法来修改 AccordionMenu
的视觉外观,正如您从截图中所见。只需几行代码,您就可以完全更改 AccordionMenu
的外观。特别是,您可以使用自定义图像作为项目图标,从而实现完全个性化。
所有源代码都已完全注释,因此下面我将仅解释这项工作中的一些重要方面。
背景
此 JComponent
的结构相当简单

从图中可以看出,该组件由三个重要部分组成。
AccordionRootItem
是显示为主菜单项的文本对象。AccordionLeafItem
是显示为主根下方叶子菜单项的文本对象。AccordionBranch
是主根下的 JPanel
容器,其中显示叶子;它通过折叠和显示每个分支来创建手风琴效果。
主 JComponent
是父容器,它根据上图中左侧所示的参数显示 AccordionItem
。menuSize
是每个根菜单项的大小,此值乘以根菜单项的数量,即可计算出分支可用的空间。
顺便说一句,每个单独的 AccordionItem
都可以直接从外部访问(接下来我们将看到如何),因此您可以单独修改它们;例如,您可以更改单个叶子项或菜单根的视觉外观。
最后,我们可以看到每个 AccordionItem
都是 JLabel
的简单继承,因此我们可以为每个项目简单地设置一个图标图像,正如您从屏幕截图中看到的。
现在我们可以深入了解一些方面。(如果您有兴趣的话。 :))
AccordionMenu
这是主容器类。我没有完全展示它,因为它太长了,所以我只解释关键部分。
首先,我们可以看到它有两个默认的嵌套构造函数。
public AccordionMenu() {
this.addComponentListener(getDefaultComponentAdapter());
this.setLayout(null);
this.leafMap = new TreeMap<AccordionRootItem, List<AccordionLeafItem>>();
}
public AccordionMenu(String menuDescriptor) {
this();
createMenusFromDescriptor(menuDescriptor);
}
第一个是基本构造函数。它负责创建 TreeMap
元素,这是此对象的核心模型。TreeMap
包含菜单的结构,同时将每个 AccordionItem
链接在一起,以便快速单独访问每个元素。如果您使用此构造函数,则必须调用特殊方法来创建菜单结构。
public void addNewMenu(String menuName, String menuTitle) {...}
public void addNewLeafTo(String menuName, String leafName, String leafTitle) {...}
addNewMenu
用于创建一个新的根菜单项,之后,您可以调用 addNewLeafTo
来创建一个链接到选定父菜单的新叶子菜单项。
如果您使用第二个构造函数,则可以通过 String
传递菜单结构。您可以查看代码注释以了解它如何通过 createMenusFromDescriptor
方法工作;但是,菜单结构是以下类型:
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;";
它是一个简单的 string
,尽管它是多行书写的。它具有标准的正则表达式分隔符(必须使用);但是,您可以通过尝试使用它来更好地理解。
在源代码的 NewJFrame.java
文件中,展示了 AccordionMenu
的用法,其中解释了两种构造函数的用法。
核心动画算法是
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();
}
未声明的变量是类属性。它只是创建一个易失性线程,该线程在完成任务时终止。考虑到 branchAvaiableSpace
是用于显示分支的全部可用空间,它创建了两个子变量 hidingSize
和 showingSize
并减少/增加它们。它们是隐藏分支和显示分支的两个新尺寸。(您可以测试可执行文件以了解我的意思。 :))
然而,AccordionMenu
的重写的 paintComponent
方法忽略了动画的存在,它只是简单地绘制了两个分支(隐藏的和显示的)以及两个相应的大小(hidingSize
和 showingSize
)。动画线程而是修改这些参数。
您可以从外部修改动画的 timeStep
。值越高,动画越慢。
其他方法
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) {...}
它们是级联方法。它们只是遍历每个 Accordion
项并调用相应的方法。在某些情况下,主组件会提供更新 UI 树以避免视觉错误。
遍历 Accordion
项的方法是
public AccordionLeafItem getLeaf(String name) {...}
public List<AccordionLeafItem> getLeafsOf(String menuName) {...}
public List<AccordionLeafItem> getAllLeafs() {...}
public AccordionRootItem getMenu(String name) {...}
public List<AccordionRootItem> getMenus() {...}
在 NewJFrame.java 文件中,您可以看到一些使用这些遍历方法的示例;但是,它们非常简单,其名称也解释了它们的功能。
代码使用
为了“自由知识”(这是我的目标),我一如既往地发布了源代码。您可以使用这些代码进行修改和扩展这项工作。但是,可执行的 BIN 文件也可以用作 Jar 库文件链接到您的项目中,因此您可以轻松使用此组件而无需担心其工作原理。
如果您愿意,您也可以直接在 NetBeans 可视化编辑器中使用它,因为它是一个 Java Bean;不好的是,我更喜欢不使用虚假的菜单树(例如 JTree
所做的)来初始化它,因为类构造函数可能会变得难以理解。
历史
- 2010 年 11 月 8 日:初次发布