使用Actions在Java中创建菜单,工具栏和其他组件的正确方法

在Java Swing应用程序中编写菜单的简单方法是执行以下操作:

JMenu fileMenu = new JMenu("File"); JMenuItem openItem = new JMenuItem("Open..."); openItem.addActionListener(new ActionListener() { /* action listener stuff */ } ) fileMenu.addMenuItem(openItem); 

经验丰富的开发人员将认识到可以通过各种机制访问操作 – 菜单,工具栏按钮,甚至系统中的其他工作流程。 那个人更有可能写:

 Action openAction = new AbstractAction(); openAction.setName("Open..."); openAction.addActionListener(new ActionListener() { /* action listener stuff */ } ) ... JMenuItem openItem = new JMenuItem(openAction); 

我的问题是, 管理这些Action对象的最佳方法什么,以便可以跨菜单,工具栏等使用它们?

  • 创建一个返回特定操作的工厂类?
  • 在一些实用程序类中将所有操作声明为private static final Action
  • 利用Java应用程序框架?
  • 别的什么?

我开发的需要在菜单,工具栏和其他按钮上使用相同操作的应用程序已使用Swing Application Framework完成。

Swing应用程序框架

此框架将允许您拥有一个资源文件,您可以在其中定义所有菜单文本,工具提示和图标。 我认为图标是关键,你不必自己加载它们。 此外,如果您需要启用/禁用任何操作,则可以覆盖该方法以控制其状态。

该网站值得一读。

您可以使用专用的Map javax.swing.actionmap对所有abstractAction进行分组。 见http://java.sun.com/javase/6/docs/api/javax/swing/ActionMap.html

此外,每个JComponent都有一个内部actionMap(getActionMap())。

 class MyComponent extends JPanel { public static final String ACTION_NAME1="my.action.1"; public MyComponent() { AbstractAction action= new AbstractAction() { ... } getActionMap().put(ACTION_NAME1,action); ... menu.add(getActionMap().get(ACTION_NAME1)); } } 

希望能帮助到你

Action是一个糟糕的抽象 – 一个ActionListener焊接到一个穷人的Map

当然不要将它们分配给static因为它们是可变的并且还需要一些上下文来有效地操作。

我对GUI编程的一般建议是要注意它实际上与任何其他编程领域大致相同。 遵循通常的良好做法。 值得注意的是,分层,关注点分离,很少使用(实现)inheritance,并且不会写出大量的漏洞。

也看到这个问题 ,这与你提出的问题非常相似。

  1. 为您的应用程序创建基本操作; 这将在以后很快帮助你
  2. 您可以在代码中创建操作,而不是支持基本操作的子类

要组织它们,它将取决于你在做什么,你可能会采取一种方式组织一些行动,而另一些方式则采用不同的方式。 这一切都取决于。

您想要的是在代码中找到/创建操作的一致方法。

根据您的UI,您可能需要区分“静态”操作(即应用程序中始终可用的内容,例如菜单系统)和仅在特定屏幕或某些位置创建的动态操作。

在任何情况下,使用专门的基本操作的具体子类将帮助您保持这些内容的有序性。 您想要的是在代码中指定标签,助记符和图标等内容。

编辑:我觉得人们不相信这是可能或容易的,所以我做了 – 从头开始​​花了大约一个小时 – 如果我只用一种方法作为目标而不是用了40分钟反映出来为每个菜单项分开方法。

这是经过测试的源代码 。 它是有效的,但它是一个很大的方法而且难看 – 如果你使用它就重构它。 我可能会在接下来的几天内修复它,我一直希望有一个这样的副本来保持重用。

—原帖

首先,请记住将代码与数据分开。 这意味着你永远不应该键入:

 new Menu("File..."); 

字符串“File …”是数据。 如果你开始这样思考,你会发现你的问题会自行解决。

首先,您需要构建一些数据。 您需要将“文件…”和“保存”放入菜单中。 我通常从一个字符串数组开始(你可以很容易地移动到一个文件)

 new String[]{"File...","+Save","Load"...} 

这是我开始使用的简单模式之一。 然后你可以解析+符号并用它来表示“当你添加这个时,在菜单中下拉一个级别”

这只是一个愚蠢的约定,如果你不喜欢它就发明你自己。

下一步是将其绑定到要运行的代码。 你可以让他们都调用相同的方法,但屁股的痛苦(巨人切换声明)。 一种可能性是在读取数据时使用reflection来绑定方法名称。 这是一个解决方案(同样可能不符合您的口味)

 new String[]{"File...[fileMenu]","+Save[saveMenu]","Load[loadMenu]"...} 

然后你用方括号解析出来的东西,反过来把它挂钩到当前类中的方法然后你就可以了。

在这一点上我总是有一种诱惑,我已经学会了它,因为它永远不会解决。 诱惑是使用第一组数据(“文件…”)并操纵它以适应某些模式并自动绑定到您的代码(在这种情况下,删除所有非alpha字符,使第一个字母小写和附加“菜单”以获取正确的方法名称)。 随意尝试这个,它非常有吸引力,看起来很光滑,但是当它不满足某些需要时(例如在不同的子菜单中具有完全相同名称的两个菜单项),准备放弃它。

另一种方法是,如果您的语言支持闭包,那么您实际上可以在同一个地方创建文件名和闭包。

无论如何,一旦你开始这样的编码,你会发现你所有的菜单结构都是一个10行的方法,你可以改变它以满足你的需要。 我有一个案例,我必须将一组菜单更改为按钮层次结构,我在2分钟内完成。

最后,您可以使用此模式轻松设置操作对象并更改它们的使用方式(在单个位置,单行代码中),以便您进行实验。 有很多方法可以使用它们,但是如果你不按照我在这里推荐的方式进行操作,你将最终不得不在每个菜单项中重新实现每次更改,这真的很烦人 – 在一次更改后你与您刚刚实施数据驱动的解决方案相比,浪费的时间会更多。

这真的不是硬代码,应该花费一两个小时然后你就不必编写新的菜单(“……再次。相信我,这种工具总是值得的。

编辑:

我这几天总是编码数据驱动。 通常我会以正常的方式对一些东西进行原型设计,识别模式和重构 – 如果你正确地进行重构,那么数据总是会被排除,你留下的东西是美观,紧凑和可维护的。

我可以在不到1/2个小时内完成我上面提到的建议(可能需要一个小时来完成reflection版本)。 这几乎总是和使用未经过制作的版本一样长,从那时起,每次更改都会节省您的成本。

这与人们喜欢ruby非常相似,除了ruby之外,他们似乎在代码中插入了更多的数据(这使得完全从代码中提取数据非常困难,这对于国际化来说总是一个很好的目标)。

嗯,我提到过,如果你擅长提取这样的数据,i18n几乎是免费的吗?

我建议你稍微尝试一下,看看你的想法。 如果让您感到不舒服,则无需将控件嵌入字符串中。 我倾向于使用字符串/对象数组,因为它们很容易输入,在编码时仍然在文件中,以后稍微外化,但如果您喜欢YML或XML或属性文件,请使用您感觉舒服的任何内容用 – 只需从代码中提取数据!