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

时间:2009-01-23 16:27:37

标签: java user-interface swing

在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应用程序框架?
  • 别的什么?

6 个答案:

答案 0 :(得分:4)

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

Swing Application Framework

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

该网站值得一读。

答案 1 :(得分:1)

您可以使用专用的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));
 }

}

希望有所帮助

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

另请参阅this question,这与您提出的内容几乎相同。

答案 4 :(得分:0)

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

这是Tested source code。它是有效的,但是它是一个很大的方法而且难看 - 如果你使用它就重构它。我可能会在接下来的几天内修复它,我一直希望有一个这样的副本可以继续使用。

---原帖

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

new Menu("File...");

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

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

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

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

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

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

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

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

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

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

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

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

这真的不是硬代码,应该花一两个小时然后你再也不用写新菜单了(“......再说一遍。相信我,这种工具总是值得的。

编辑:

我这些天总是编码数据驱动。通常我会以正常的方式对一些东西进行原型设计,识别模式和重构 - 如果你正确地进行重构,那么数据总是会有所影响,而你剩下的就是美观,紧凑和可维护。

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

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

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

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

答案 5 :(得分:0)

  1. 为您的应用程序创建基本操作;这将在以后显着帮助你
  2. 在代码中创建操作,而不是支持基本操作的子类
  3. 要组织它们,它将取决于您对它们的处理方式,并且您可能采用一种方式组织某些操作,而其他方式则采用不同的方式。这一切都取决于。

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

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

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