Mac OS X Java 开发指南

来源:互联网 发布:nginx是什么 编辑:程序博客网 时间:2024/05/17 07:49
 Mac OS X Java 开发指南


本地平台集成

应用程序和本地环境整合得越好,用户在使用时需要学习的东西就越少。一个好的应用程序看起来就好象是对主机平台的一种扩展。本章将讨论一些技术细节,这些细节将有助于使应用程序的观感和Mac OS X的用户界面更加一致。

本部分的内容有:

设计用户界面
AppleScript
系统属性


设计用户界面

J2SE的跨平台设计要求用户界面具有很大的灵活性,以适应多个操作系统。而另一方面,Aqua的设计目标则是在单一平台上提供最好的用户体验。本章的内容将有助于使您的Java程序的外观和特性与本地的应用程序更加接近。事实上,遵循这里所提出的建议,同样也可能使应用程序的外观和行为更接近其它平台的本地程序。这里要讨论的仅仅是设计决断方面的一个小子集,但是它们是很常见的问题,经常会在Java应用程序中碰到。如果需要Aqua用户界面的完全指南,请参见苹果人机界面指南(Apple Human Interface Guidelines)一书。

菜单

在各个平台上,菜单的外观和行为都有所不同。然而不幸的是,很多Java程序员在写程序时只考虑一个平台。这个小节的内容将讨论一些各个平台的通用技术,以提高菜单在Mac OS X上显示和行为的性能。

菜单条

“JMenuBars”部分中讨论了如何从JFrames对象中获取菜单,并将菜单移动到Mac OS X的菜单条中。把菜单放在苹果菜单条上是一个非常值得鼓励的事,但是这并不能自动提供Mac OS X菜单的本地体验,有以下两个原因:

  • 在Mac OS X平台上,应用程序如果被激活,则它的菜单条总是能看见的,无论当前是否有窗口打开。

  • 菜单条中包含所有应用程序可能使用的菜单。如果某个菜单对于当前焦点窗口来说没有作用,则菜单的标题会被置灰。如果一个菜单所包含的菜单项只对窗口的内容有作用,而当前没有窗口打开,则这个菜单也要置灰。菜单并不因为当前焦点窗口的变化而出现或者消失。

简而言之,除了有些时候部分菜单项被置灰,菜单条应该总是被显示,并且看起来总是一样的。把菜单从应用程序转移出来并放到苹果菜单条上,是保持平台兼容性的第一个大的步骤。您如果要获得其它特性,可能需要在代码级别上重新考虑如何显示菜单,这取决于应用程序的具体设计。一个可能的解决方案是使用带有菜单工厂的离屏JFrame对象,它在应用程序被激活时总是在屏幕的最前方(foremost)。

应用程序菜单

任何一个使用AWT/Swing,或者被封装成可双击启动的应用程序包的Java程序,在启动时都自动带有一个应用程序菜单,类似于Mac OS X上的本地应用。缺省情况下,这个应用程序菜单以Java主类的全名作为菜单标题。通过使用-Xdock:name命令行属性,或者通过设置应用程序的信息属性列表CFBundleName的值,可以改变这个名字。根据Aqua的界面风格指南,您所指定的应用程序菜单的名称应该不超过16个字符。图1显示的是一个应用程序菜单。


图1 Mac OS X上Java程序的应用程序菜单

Figure 1 Application menu for a Java application in Mac OS X

定制应用程序菜单的下一步就是使您自己的处理代码在选择应用程序菜单的菜单项时被调用。苹果公司在com.apple.eawt包中为这一步提供支持,应用程序(Application)和应用程序适配器(ApplicationAdaptor)类中提供一种方法,可以处理预置(Preferences),关于(About),和放弃(Quit)这几个菜单项。

如果需要更多的信息,请见Java 1.4 API:苹果的扩展J2SE 5.0苹果扩展参考部分。在Xcode的缺省Swing应用程序工程中,也可以找到如何使用这些技术的例子,您可以简单地在Xcode中打开一个新工程,在工程助手(Project Assistant)对话框中选择Java Swing应用程序(Java SwingApplication)工程模板,生成的新工程就使用了所有这些菜单项的处理函数。

如果您的应用程序要部署到别的平台上,而在该平台预置,关于,和放弃菜单项需要放在菜单条的其它位置(如文件或者编辑菜单),则您可能需要根据主机平台的操作系统做一个条件菜单来进行放置。比较好的条件菜单放置方法是为Mac OS X上的这些菜单项增加一个新的实例。这个小修改在相当程度上可以使您的Java应用程序感觉起来更象Mac OSX上的本地应用程序。

窗口菜单

苹果人机界面指南一书中建议,所有的Mac OSX应用程序都需要提供一个窗口(Window)菜单,来跟踪当前打开的窗口。这个窗口菜单中包含一个窗口列表,其中当前激活的窗口用检查标志加以标识。选择一个给定窗口菜单项会使对应的窗口变成激活窗口,显示在屏幕的最前面。新建的窗口应该加入到这个菜单列表中,而被关闭的窗口则应从列表中删除掉。在典型的情况下,菜单项的顺序就是窗口打开的顺序。苹果人机界面指南中有关于窗口菜单的更具体的指南。

速器(键盘快捷方式)

由于各个平台的修正键(Modifier)不尽相同,我们不应该用javax.swing.KeyStroke方法对菜单项的加速键进行显式的指定。正确的加速键应该通过调用java.awt.Tookit.getMenuShortcutKeyMask方法来从系统中获得,而不是自行定义。

调用上述方法时,当前平台的工具箱会返回正确的屏蔽键。这个方法会检测当前的工作平台,然后根据这个平台信息推测出正确的键。举例来说,假定要把拷贝(Copy)项添加到菜单中,则使用getMenuShortcutKeyMask方法意味着您可以使用列表2中简单的代码来代替"“应用程序包的内容”部分中复杂的代码。

列表1 根据当前主机平台显式设定加速键

JMenuItem jmi = new JMenuItem("Copy");
    String vers = System.getProperty("os.name").toLowerCase();
    if (vers.indexOf("windows") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK));
    } else if (vers.indexOf("mac") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.META_MASK));
    }

列表2 使用getMenuShortcutKeyMask方法设定修正键

JMenuItem jmi = new JMenuItem("Copy");
jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));

MacOSX平台上缺省的修正键是Command键。可能还会使用其它的一些修正键,如Shift,Option,或者Control,但是Command键是基本的修正键,它强调应用程序即将要做的是一个命令,而不是一般性的输入。请注意,各个平台并没有在用户界面的行为上保持一致,当您为一个菜单项分配键盘快捷方式时,不要把Macintosh用户习惯使用的键盘命令覆盖掉。请参见苹果人机界面指南,书中有一个最常用或者被保留的键盘快捷方式(即等价的键盘操作方式)的列表。

记忆键

MenuItems从JAbstractButto类中继承了记忆键的概念。在菜单上下文中,记忆键是菜单键的快捷方式,是由一个修正键和另一单一字符结合而成。在设定了JMenuItem的记忆键之后,Java会为JMenuItem或者JMenu的标题上的记忆字母加下滑线,结果应类似图2所示的菜单。

请注意:图2只是为了论证记忆键。虽然这个图中菜单条位于窗口内部,但在您的开发中应该通过设定apple.laf.useScreenMenuBar这一系统属性,把菜单放置到Mac OS X菜单条中。在Mac OS X菜单条记忆键不会被绘制。请参见Java系统属性部分。


图2 Mac OS X中的记忆键

Figure 2 Mnemonics in Mac OS X

这个菜单的某些地方并不符合Aqua界面风格的指导原则,包括如下几个方面:

  • 菜单上含有多余的信息,快捷方式已经在菜单项的右边定义了,则没有必要定义记忆键。

  • 菜单的信息并不精确。请注意,这个例子中的保存(Save)和另存为(Save As)这两个菜单标题中的S字母都带有下滑线。

  • 菜单上的部分信息会使用户界面变得混乱。

如果您使用apple.laf.useScreenMenuBar这一系统属性的话,则部分问题可以自动地得到处理。然而对您来说有一个更好的方法,就是不在Mac OS X平台上使用记忆键。如果在其它平台上需要记忆键,则可以在构造用户界面时有条件地调用setMnemonics()方法(根据平台的需要)来进行设定。

那么,如何不使用Java的记忆键,而又能得到记忆键的功能呢?如果您已经在setAccelerator方法中为某个菜单项定义了一个按键系列,则这个按键系列会自动地进入到菜单中。例如,列表3中的代码可以Page Setup菜单项的加速键设置为Command-Shift-P。图3显示了这段代码执行后的表现,同时也显示了对其它菜单项进行类似设定的结果。请注意,表示Command和Shift键的符号被自动包括在菜单的标题中。

列表3 设定一个加速键

JMenuItem saveAsItem = new JMenuItem("Save As...");
    saveAsItem.setAccelerator(
        KeyStroke.getKeyStroke(KeyEvent.VK_S, (java.awt.event.InputEvent.SHIFT_MASK | (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()))));
    saveAsItem.addActionListener(new ActionListener(  ) {
      public void actionPerformed(ActionEvent e) { System.out.println("Save As...") ; }
    });
    fileMenu.add(saveAsItem);

图3 文件(File)菜单

Figure 3 A File menu

这种方式和记忆键并不十分相像,但是值得注意的是Mac OS X提供了键盘和辅助设备漫游菜单的支持,这些功能可以在系统预置(System Preferences)中的键盘和万能辅助面板中进行配置。

请注意: 由于ALT_MASK修正符的值在Macintosh上与Option键相对应,所以如果您使用getMenuShortcutKeyMaskALT_MASK连接而成的组合键来为Windows平台设定Control-Alt屏蔽键,则在Macintosh平台上这个屏蔽键会变成Command-Option。

菜单项的图标和特殊字符

正如记忆键那样,菜单项的图标在Mac OS X的Swing上也是可以使用的。虽然一些应用程序的确显示了菜单图标—其中最引人注目的是Finder中的Go菜单,但是这种图标并不是Aqua界面的标准组成部分。您可能需要根据不同平台的风格有条件地应用这些图标。

Aqua指定了一个专门供菜单使用的特殊字符的集合,如果您对在菜单上使用特殊字符的信息感兴趣,请参见苹果人机界面指南一书。

上下文菜单

上下文菜单在Java中称为弹出菜单,Mac OS X平台完全支持这种菜单。在Mac OSX上,上下文菜单可以通过Control-click键或者right-click来触发。请注意,虽然Control-click和right-click会产生相同的结果,但它们是不同的鼠标事件。在Windows平台上,鼠标的右键是上下文菜单的标准触发器。

上下文菜单触发器的不同可能会导致一些零碎和有条件执行的代码。这两种触发器的一个重要的共同点就是使用鼠标点击。同样地,我们也有一个好方法来保证您的程序可以解析正确的上下文菜单出发器,那就是通过调用java.awt.event.MouseEvent.isPopupTrigger方法来请求AWT进行这个解析。

上述的方法在java.awt.event.MouseEvent包中定义,因为当某个构件上的鼠标事件被检测到时,需要在该构件的java.awt.event.MouseListener这个鼠标事件侦听方法中激活上下文菜单。重要的是如何以及何时可以检测正确的事件,在Mac OS X平台上,弹出触发器被设定为MOUSE_PRESSED,而在Windows上则被设定为MOUSE_RELEASED,即鼠标松开的时机。为了程序的可移植性,这两种情况都需要加以考虑,如“Java应用程序的Info.plist文件”部分所示。

列表4 使用~来检测上下文菜单是否激活

JLabel label = new JLabel("I have a pop-up menu!");
 
label.addMouseListener(new MouseAdapter(){
    public void mousePressed(MouseEvent e) {
       evaluatePopup(e);
    }
 
    public void mouseReleased(MouseEvent e) {
       evaluatePopup(e);
    }
 
    private void evaluatePopup(MouseEvent e) {
       if (e.isPopupTrigger()) {
          // show the pop-up menu...
       }
    }
});

在设计上下文菜单时,请记住永远不要把这种菜单作为用户访问某个软件功能的唯一通道。上下文菜单为一些经常使用且与菜单项相关联的命令提供便利的访问通道,但它不应该是主要的或唯一的访问方式。

构件

在设计用户界面时,需要记住几个关键的概念,本章下面的部分将讨论这些概念。

构件的布局和尺寸调整

在布置构件的时候,不要显式设定构件的x和y坐标的值,而是应该使用AWT布局管理器。布局管理器使用一些抽象的位置常数来确定构件在特定环境中的精确位置,它既考虑了每一个单独构件的尺寸,同时又维护了各个构件在容器中相对的位置关系。

在一般情况下,不要显式设定构件的尺寸。每一种观感都有它自己的字体风格和尺寸,这些字体的尺寸会影响到含有文本的构件所需要的尺寸。显式地把一个调整好尺寸的构件移动到另一个带有更大字体的观感中可能会带来问题。有一个最安全的方法,可以使您以一种可移植的方式获得构件正确和最小的尺寸,那就是改变原来的布局方法,或者增加另一个布局方法,或者把构件的最小和最大尺寸设置为它的喜好尺寸。当使用后面的方法时,要用到setSize()getPreferredSize()这两个方法。

大部分的布局管理器和容器都尊重一个构件的喜好尺寸(preferred size),因此通常不必调用上述的方法。然而当您的用户界面变得越来越复杂的时候,您会发现,调用这个方法可以很方便地得到带有很多子构件的容器的喜好尺寸。

给构件添加色彩

由于一个特定的观感总是倾向于使大部分构件—如果不是所有构件的话,都具有统一的颜色和风格,您可能对定制一个与标准用户界面类的观感相匹配的新构件感兴趣。这个想法是完全合乎逻辑的,但是会增加维护性和移植性方面的代价。我们很容易就可以为构件设置一个自己认为与当前观感相一致的显式颜色,然而在使用另一种观感时,该构件可能会变成让您自己感到惊讶的、不常见的非标准构件。为了保证定制构件和标准构件相匹配,可以向UIManager类查询当前观感期望的颜色。举例来说,一个定制的窗口对象包含一些标准的轻量级构件,但是需要描画那些没有被子构件覆盖的背景,以便和其它的应用程序容器和窗口相匹配。为了完成这个工作,可以调用下面的方法:

myPanel.setBackground(UIManager.getColor("window"))

这个方法返回一个和当前观感相匹配的颜色。使用这些标准方法的另一个好处是它们能提供更加专门的背景,这种背景不容易被重新构造,比如在Aqua容器和窗口中使用的带有条纹的背景。

窗口和对话框

在Mac OS X平台上,窗口的坐标和镶边(inset)与Java SDK是互相兼容的。窗口的边界是指窗口边框的外界,窗口坐标的(0,0)点在标题栏的左上角。getInsets方法返回一个数量,这个数量代表窗口中需要填入的内容的空间大小。这些空间是用来分隔构件与窗口的边界的,会影响进行窗口精确定位的应用程序,特别是使用全屏窗口的程序。

MacOS X平台的窗口行为和其它平台有所不同。举例来说,在Mac OSX上我们可以打开一个没有任何窗口的应用程序,窗口最小化后停留在Dock上;还有,内容可变的窗口总是带有滚动条。这个小节将突出介绍您应该知道的窗口细节,并讨论如何处理Mac OS X平台上的窗口行为。

多文档界面的使用

javax.swing.JDesktopPane类中的多文档界面(MDI)模型在Mac OSX上可能会产生混淆不清的用户体验。JDesktopPane中的最小化窗口会根据JDesktopPane对象的尺寸变化而移动位置。在JDesktopPane中,窗口最小化后停留在底部的面板中;而独立的窗口最小化后则停留在Dock上,而且JDesktopPane限制用户任意移动窗口。即JDesktopPane中的子窗口以及JDesktopPane窗口的本身。通常情况下,Mac OSX的用户通过多个自由浮动的独立窗口和一个屏幕最上方的菜单条与应用程序进行交互。用户可以在自己的视图或者整个桌面上任意布置这些窗口,或者在不同应用窗口之间进行切换(这些窗口可以同属一个应用程序,也可以来自不同的应用程序);在使用某个特定的应用程序时,用户在视觉上不会被限制在屏幕上的某个区域。在制作Mac OS X的应用程序,请尽可能避免使用javax.swing.JDesktopPanes类。

在某些时候,如果不使用JDesktopPane,就会出现一些窗口相关的问题,没有简单的办法可以解决。举例来说,在您的Java应用程序中可能需要一个类似于浮动工具条的实体,在Swing中称为“内部工具窗口”,无论当前哪一个文档被激活,这个窗口一直保持在最前面。目前,除了使用JDesktopPane外,Java没有其它方法可以提供这种窗口,然而如果是开发新的工程,您可能要考虑使用一个单一的动态容器,来设计平台中立的用户界面,就正如JBuilder或者LimeWire这样的应用程序。如果您要将现有的基于MDI的应用程序从其它平台移植到Macintosh上,而不想重构代码,则Mac OS X也可以支持J2SE 1.4规范中指定的MDI模型。

带有滚动条的窗口(使用ScrollPanes)

在MacOSX上,无论窗口内容的尺寸是否超过窗口的尺寸,支持滚动的文档窗口都会显示一个滚动条,而滚动滑块则只在内容的尺寸超过窗口可视区域时才会显示出来。这可以避免使用户在感觉上以为可视区域的尺寸在改变。缺省情况下,对于一个Swing的JFrame对象,无论其尺寸如何被调整,都不会出现滚动条。在一个框架(frame)对象中支持内容滚动的最简单方法,就是把这个框架构件放到一个JScrollPane对象中,然后再把这个JScrollPane对象添加到另一个父框架对象中。然而在缺省情况下,只有当面板中内容的尺寸超过窗口尺寸时,JScrollPane对象的滚动条才会出现,因此如果您在应用程序中使用了JScrollPane,则可能需要把JScrollPane的滚动条策略设置为“总是显示滚动条”,即使在窗口内容的尺寸比窗口可视区域的尺寸小的时候。“制作Java应用程序包”部分中显示的就是这样的一个实例。

列表5 设置JScrollBar的策略,使之更像Aqua

JScrollPane jsp = new JScrollPane();
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

这个设定使得滚动条总是显示在窗口上,并且在内容的尺寸大于可视区域的尺寸时显示滚动滑块。滚动条的缺省策略是AS_NEEDED,可能和其它平台的风格结合得更加紧密,因此您可能需要考虑有条件实现这个设定。

文件选择对话框

ava.awt.FileDialogjavax.swing.JFileChooser类是Java应用程序访问文件系统的两种快速而简单的手段。虽然这两种方法各有各的优点,但java.awt.FileDialog类非常有助于使Java应用程序的行为和各个平台的本地应用程序相类似。这两者之间的区别在Mac OS X平台上表现尤其明显,如图4图5所示。


图4 用java.awt.FileDialog创建的对话框

Figure 4 Dialog created with java.awt.FileDialog


图5 用javax.swing.jFileChooser创建的对话框

Figure 5 Dialog created with javax.swing.jFileChooser

AWT的FileDialog类自动采用列视图(Column-view)来进行文件的浏览,而Swing的JFileChooser类则使用漫游试图(navigation),这和Mac OS X的本地应用程序不同。如果不是需要JFileChooser类提供的功能的话,您可能需要使用java.awt.FileDialog类。由于FileDialog类型的对话框是模式对话框,在屏幕上总是处于别的可视构件之上,因此不会发生那些混合使用Swing和AWT构件通常会带来的问题。

在使用AWT FileDialog类时,您可能希望用户选择一个目录而不是选择一个文件。在这种情况下,请调用FileDialog实例中的setProperty.invoke()方法来使用apple.awt.fileDialogForDirectories属性。

窗口修改指示器

在Mac OS X上,如果窗口的内容在最后一次保存之后发生了变化,窗口通常会标记出与变化关系紧密的组件(widget)。使用这种技术,可以使应用程序的行为更像一个本地应用程序,同时和用户的期望相符合。

为了使用窗口修改指示器,您需要使用Swing的windowModified属性。在JComponent的任何子类中,可以使用putClientProperty()方法来设置这个属性。该属性的值可以是Boolean.TRUE或者Boolean.FALSE。

如果您需要关于使用窗口修改指示器的更多的信息,请回顾技术说明 Q&A QA1146:说明Swing中文档窗口的变化的内容。

AppleScript

MacOSX使用Apple事件来进行进程间通讯。Apple事件是一种高级别的语义事件,应用程序可以把它发送给自己,也可以发送给别的应用程序。您可以利用AppleScript来将基于这些事件的动作脚本化,也可以让用户通过AppleScript在一定程度上控制您的应用程序,而不必在程序中加入本地代码。Java应用程序可以通过以下两种方法来支持AppleScript:

  • 如果您使用了com.apple.eawt包中提供的Application和ApplicationAdaptor类,则通过实现ApplicationAdaptor类中的时间处理函数,您的应用程序就可以产生和处理一些类似打印(Print)和打开(Open)的基本事件。这两个类的信息请参见 Java 1.4 API:苹果的扩展J2SE 5.0苹果扩展参考部分。

  • 使用System Events程序中提供的GUI脚本编程方法。如果您使用System Events,就可以书写AppleScript脚本来选择菜单项,点击按键,在文本域中输入文本,以及对Java程序的GUI做一般性的控制。

如果您需要更多关于AppleScript的信息,请参见AppleScript入门一书。

系统属性

Mac OS X的专用属性使您只须做最少的工作,就可以把一些系统本地的行为加入到Java应用程序中去。在Java系统属性章节中提供了一个目前已经支持的Mac OS X系统属性的完全列表,包括如何使用这些属性。


转载自 http://developer.apple.com.cn

原创粉丝点击