使用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,并且不会写出大量的漏洞。
也看到这个问题 ,这与你提出的问题非常相似。
- 为您的应用程序创建基本操作; 这将在以后很快帮助你
- 您可以在代码中创建操作,而不是支持基本操作的子类
要组织它们,它将取决于你在做什么,你可能会采取一种方式组织一些行动,而另一些方式则采用不同的方式。 这一切都取决于。
您想要的是在代码中找到/创建操作的一致方法。
根据您的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或属性文件,请使用您感觉舒服的任何内容用 – 只需从代码中提取数据!