65.9K
CodeProject 正在变化。 阅读更多。
Home

Java 手风琴菜单,视觉效果和自定义

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (9投票s)

2010年11月8日

Apache

4分钟阅读

viewsIcon

65981

downloadIcon

5852

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

引言

screen1.PNG

本文展示了 Swing 编程的许多方面。首先,它展示了嵌套 JComponents 的层次结构。它解释了如何创建一个 NULL 布局组件,其中内部组件具有完全的自由度。它还通过一个巧妙的技巧来实现内部组件的动画,带来出色的视觉效果。

父组件提供了许多方法来修改 AccordionMenu 的视觉外观,正如您从截图中所见。只需几行代码,您就可以完全更改 AccordionMenu 的外观。特别是,您可以使用自定义图像作为项目图标,从而实现完全个性化。

所有源代码都已完全注释,因此下面我将仅解释这项工作中的一些重要方面。

背景

JComponent 的结构相当简单

structure.PNG

从图中可以看出,该组件由三个重要部分组成。

AccordionRootItem 是显示为主菜单项的文本对象AccordionLeafItem 是显示为主根下方叶子菜单项的文本对象AccordionBranch 是主根下的 JPanel 容器,其中显示叶子;它通过折叠和显示每个分支来创建手风琴效果。

JComponent 是父容器,它根据上图中左侧所示的参数显示 AccordionItemmenuSize 是每个根菜单项的大小,此值乘以根菜单项的数量,即可计算出分支可用的空间。

顺便说一句,每个单独的 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 是用于显示分支的全部可用空间,它创建了两个子变量 hidingSizeshowingSize 并减少/增加它们。它们是隐藏分支和显示分支的两个新尺寸。(您可以测试可执行文件以了解我的意思。 :))

然而,AccordionMenu 的重写的 paintComponent 方法忽略了动画的存在,它只是简单地绘制了两个分支(隐藏的和显示的)以及两个相应的大小(hidingSizeshowingSize)。动画线程而是修改这些参数。

您可以从外部修改动画的 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 日:初次发布
© . All rights reserved.