使用Swing Application FrameWork (Using Swing Application Framework)

来源:互联网 发布:阿里云服务器华东地域 编辑:程序博客网 时间:2024/05/22 06:43

 

原文地址http://java.sun.com/developer/technicalArticles/javase/swingappfr/

 

 

Using the Swing Application Framework (JSR 296)

 

使用Swing应用程序框架(JSR296

作者 John O'Conner  @20077

 

 

Articles Index

If you've developed many applications using a Swing-based graphical user interface (GUI), you've probably solved some common problems over and over again. Those problems include managing the application life cycle, event handling, threading, localizable resources, and maybe even persistence.

 

文章索引

如果你以前用Swing开发过很多GUI应用,那你可能会对某些常见的问题一遍一遍地处理。这些问题包括:程序生存周期的管理,事件处理,线程,资源本地化,也可能还包括持久化。

 

To save time and effort, you can develop reusable libraries and small application frameworks to use on each new project. An application framework can provide much of the common infrastructure that most applications share. A framework is a reusable library of classes and functionality that help you design and implement applications using consistent designs and patterns.

 

为了节省时间和精力,你可以开发一些可重用的库和一些小的应用框架,把这些库与框架用在新工程上。 一个应用程序框架可以提供在大部分程序中共同使用的通用基础结构。通过提供一致的设计模式,这样一个由可重用的类库与功能组成的框架,可以帮助你更好地设计与实现程序。

 

If you develop Swing applications, you can benefit from the Swing Application Framework, which is currently being developed as part of Java Specification Request (JSR) 296.

 

如果你在开发以Swing为基础的程序,那你可以从”Swing应用程序框架中受益。它现在还正在被开发,并且是”Java规范请求(JSR)296”的一部分。

 

Contents

Framework Scope

Framework Project Files

Framework Architecture

Application Life Cycle

Resource Management

Actions

Tasks

Session State

Local Storage

Summary

For More Information

Framework Scope

 

内容:

l  框架范围

l  框架工程文件

l  框架结构

l  应用程序生命周期

l  资源管理

l  操作

l  任务

l  会话状态

l  本地存储

l  总结

 

框架范围

 

The term framework can cause apprehension because frameworks can be large, complicated, and overbearing. For small applications, a framework can introduce more complexity than the original system it supposedly helps. However, the Swing Application Framework has goals that minimize any burdensome effects that a larger framework might cause.

 

框架这个名词容易让人有误解,因为框架可以非常大,非常复杂,甚至无法忍受。对一些小程序来说,使用框架往往会比不用框架引入更高的复杂度。然而,Swing应用程序框架就有一个明确的目标:最小化一个的框架可能会带来的麻烦。

 

The framework's primary goal is to provide the kernel of a typical Swing application, helping programmers to get started quickly and to adopt best practices for just a few elements common to every Swing application: architecture, lifecycle, resource management, event handling, threading, session state, and local storage.

 

这个框架的主要目标是为普遍的Swing程序提供一个内核,它可以帮助程序员快速进入开发,并且最好地实践这些Swing程序中所普遍用到的元素:结构,生命周期,资源管理,事件处理,线程,会话状态以及本地存储。

 

 

Framework Project Files

框架的工程文件

 

As you read about the framework, you may be tempted to experiment with it yourself. Don't resist. You can download the Swing Application Framework project files from its online project location at java.net. Navigate to the project's documents and files section. The project is in the early stages at this time. The examples and source code in this article run correctly with version .50, the project's current version.

 

当你了解了这个框架,你大概也想自己去试试。 不要犹豫! 你可以在Swing应用程序框架的工程地址:java.net上下载。点击工程文档与文件”. 这个工程现在还处于早期开发阶段。这个文章所用到的例子以及代码在0.50版本上都可以正确运行,这也是此工程的当前版本。

 

Note that the JSR 296 reference implementation libraries, documentation, and source files change frequently, so you may have to adjust or modify the source code in this article as a result. Project files generally follow the following naming conventions, where <version> represents a changing version number:

 

请注意:JSR296参考实现的库、文档与代码文件都还时常变动,所以你也有可能需要调整与修改本文中的示例代码。 这些工程文件通常都符合命名规范,既<version>版本号代表一个变化的版本数字。

ApplicationFramework-<version>.jar (库文件)

ApplicationFramework-<version>-doc.jar (文档)

ApplicationFramework-<version>-src.zip (代码)

 

You can use the framework by downloading just the library and documentation. If you're interested in implementation details, consider downloading the source as well, which is easily compiled with an ANT project-based integrated development environment (IDE) such as the NetBeans IDE. Once you have the framework implementation, put its jar file in your compiler and runtime classpath so that your application can use its application programming interface (API).

The framework's API exists in just one package: application. Although a few subpackages hold text and icon resources, all the API is in the application package itself.

 

你只需要下载库文件和文档就可以开始使用了。如果你对它的具体实现很感兴趣,那你也可以考虑把源代码下载回来。它可以很轻松地用一个支持”ANT”的集成开发环境来编译,例如Netbeans IDE. 当你有了框架的库文件,把它放在你的运行时classpath中,这样子你的程序就可以使用框架所提供的API了。 框架的API只存在在一个包中:application. 虽然还有一些文本以及图标资源在其它包中,但是所有的API都在application包中。

 

Framework Architecture

 

框架结构

 

Two classes help you manage your application: ApplicationContext and Application. The Application and ApplicationContext objects have a one-to-one relationship.

 

有两个类来帮助你管理你的程序:ApplicationContext(程序上下文) Application(程序) . 这两个是一对一的关系。

 

The ApplicationContext provides services to the application as shown in Figure 1.

 

ApplicationContextApplication提供服务,如图所示

 

 

 

Those services include the following:

Localizable resource management

Task services and monitoring

Event-action management

Session-state storage

 

这些服务包括以下内容:

资源的本地化管理

任务服务以及监视

事件---操作(Event-Action)的管理

会话状态存储

 

 

 

All Swing Application Framework applications must subclass either the Application class or its SingleFrameApplication subclass. The Application class provides lifecycle methods for launching the application, starting the user interface (UI), and shutting down.

 

所有使用Swing应用程序框架的程序都必须继承Application类或它的子类SingleFrameApplication. Application类提供了管理程序生命周期的方法,包括:启动程序,开始用户界面以及关闭。

 

The SingleFrameApplication adds a default main GUI frame, retrieves and injects default resources, and uses the ApplicationContext to save and restore simple session state. Session state includes UI component location, size, and configuration.

 

SingleFrameApplication添加了一个默认的主界面,为其获取以及注入默认资源,并且使用ApplicationContext上下文来保存与恢复会话状态。会话状态包括UI组件的位置,大小以及其它设置信息。

 

Both superclasses provide an ApplicationContext, but the SingleFrameApplication class provides additional default behaviors that use the context. You probably will not use Application as your superclass. Most likely, the SingleFrameApplication class provides the default behavior you need. In the future, other subclasses should be available to handle multiframe applications as well. Regardless of the superclass, your application will use its ApplicationContext instance to access most services that the framework provides.

 

这两个超类都提供一个ApplicationContext,但是SingleFrameApplication类提供了一些使用上下文的默认行为。我猜你大概不会用Application类作为你的超类。在更多的情况下,SingleFrameApplication提供了你所需要的默认行为。在将来,其它的对应多窗口应用程序的子类(如MultiFrameApplication)也应该及时出现。 但是,不管你用哪一个做为你的程序的超类,你都将从ApplicationContext使用框架所提供的绝大部分服务。

 

Application Life Cycle

 

应用程序生命周期。

All applications have a life cycle of events that are called in a specific order. Your primary Application or SingleFrameApplication subclass should start in its static main method and should launch the application from that point. The supported life cycle includes the following methods, called in this order:

 

所有的程序都有一个由特定顺序的事件组成的生命周期。你的程序,不论是继承Application还是SingleFrameApplication都要从它自己的静态main方法开始并且从这个地方引导应用程序。框架中每一个生命周期都有一个方法与之对应,它们按照以下顺序转移:

 

launch -- You must call this framework method.

initialize -- The framework will invoke this optional overridden method.

startup -- The framework will invoke this overridden method.

ready -- The framework will invoke this optional overridden method.

exit -- You must call this framework method.

shutdown -- The framework will invoke this optional overridden method.

 

Launch(发启)---你必须使用这个方法

Initialize(初始化)—框架会调用这个可选的方法

Startup(启动)—框架会调用这个方法

Ready(就绪)----框架会调用这个可选的方法

Exit(退出)----你必须使用这个方法

Shutdown(关闭)----框架会调用这个可选的方法

 

Your application's minimum requirement is to call the launch method and to override the startup method. By calling the launch method, your application begins the life cycle. The launch method will then call the initialize, startup, and ready methods. You should also handle your application frame's closing by calling the exit method, which will eventually call the shutdown method. A few examples should help make the sequence more clear.

 

你的程序的最小需求是呼叫launch方法,并且覆盖startup方法。通过调用launch这个方法,你的程序的生命周期自此开始。Launch方法之后会调用initialize , startup , ready 这三个方法。 你同时也应该调用exit方法来处理你的程序的退出,它最终会调用shutdown方法关闭整个应用。一些例子会帮助你更清晰地认识这个过程。

 

Application Launch, Initialization, and Startup

You must create and initialize your GUI on the Swing event dispatch thread (EDT). Many application developers forget this important step. Code Example 1 shows a basic Swing application without the framework, and the code examples that follow add the framework.

 

应用的发起,初始化,启动

你需要在”swing事件分发线程”EDT的基础上创建和初始化你的界面。很多开发人员忘了这重要的一点。示例1展示了一个没有使用框架的swing程序,下面又展示了一个使用了框架的程序。

 

示例1

public class BasicApp implements Runnable {

 

    JFrame mainFrame;

    JLabel label;

 

    public void run() {

        mainFrame = new JFrame("BasicApp");

        label = new JLabel("Hello, world!");

        label.setFont(new Font("SansSerif", Font.PLAIN, 22));

        mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        mainFrame.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {

                mainFrame.setVisible(false);

                // Perform any other operations you might need

                // before exit.

                System.exit(0);

            }

        });

        mainFrame.add(label);

        mainFrame.pack();

        mainFrame.setVisible(true);

    }

 

    public static void main(String[] args) {

        Runnable app = new BasicApp();

        try {

            SwingUtilities.invokeAndWait(app);

        } catch (InvocationTargetException ex) {

            ex.printStackTrace();

        } catch (InterruptedException ex) {

            ex.printStackTrace();

        }

    }

}

 

 

Adding the basic framework for this example does not really reduce your workload, but you have to start somewhere. Still, even with an example this simple, the framework does several important jobs. Review Code Example 2, which implements the same "Hello, world!" functionality within the framework, and see the description that follows it.

 

为这么这个简单的例子加入框架确实也没什么意义,但是我们总发从一个地方开始。然而,就算为这么简单的示例代码,框架也的确做了一些重要的工作。请看下面的示例2,它用框架实现了相同的”Hello, world!”功能。

 

示例2

public class BasicFrameworkApp extends Application {

    private JFrame mainFrame;

    private JLabel label;

 

    @Override

    protected void startup() {

        mainFrame = new JFrame("BasicFrameworkApp");

        mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        mainFrame.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {

                mainframe.setVisible(false);

                exit();

            }

        });

        label = new JLabel("Hello, world!");

        mainFrame.add(label);

        mainFrame.pack();

        mainFrame.setVisible(true);

    }

 

    public static void main(String[] args) {

        Application.launch(BasicFrameworkApp.class, args);

    }

}

 

 

Even in this simple example, the framework does several important jobs. First, by using the launch method, you always ensure that the UI starts on the EDT, something many developers simply forget. For more information about interacting with the EDT, you can read Improve Application Performance With SwingWorker in Java SE 6. Second, this code uses a distinct, well-defined startup method to create and display the UI components. Finally, by using the Application.launch method, you begin the application life cycle, which means that the framework will call your overridden lifecycle methods -- such as startup -- at important points in the application lifetime.

 

即使是这么简单的例子,框架也的确做了一些重要的工作。首先,通过调用launch方法来引导你的应用,你总可以保证你的界面是在EDT基础上启动的,这是很多开发者都忘记的一点。关于EDT的更多信息,请阅读Java Se 6中用Swingworkier 提高程序性能。其二,在示例中使用了很清晰并且良好定义的startup方法,用它来创建和显示界面。最后,使用Application.launch方法,开启了程序的生命周期,这意味着框架会自动调用你被你覆盖的生命周期方方法--- startup , 它是程序生活周期中重要的一环。

 

 

All framework applications must override the startup method. Use this method to create and display the UI. The framework will call this method on the EDT as a result of your invoking the launch method. In Code Example 2, the startup method creates a frame, adds a window listener for closing the frame and exiting, and displays the simple frame.

 

所有的框架程序都必须覆盖startup方法。用这个方法创建和显示界面。在你调用了launch方法后,框架会在EDT基础上调用这个startup方法。在示例2中,startup方法创建了一个frame,为关闭事件加入了一个listener,并且把这个frame显示出来。

 

The launch method calls the application's optional initialize method just prior to calling the startup method. You can use the initialize method to perform any initial configuration or setup steps. For example, you can process command-line arguments from within the initialize method. You can also check a database connection or set system properties. In short, the framework provides this method for any non-UI related setup that your application may need before displaying the UI. The Application and SingleFrameApplication classes provide an empty method body for the initialize method. The method does nothing by default.

 

Launch方法会在调用startup方法之前调用一个可选的initialize(初始化)方法。你可以用这个initialize方法来执行一些初始化设置。例如,你可以在initialize方法中处理命令行参数,你也可以检查数据库连接或者设置系统的属性(Properties)。简单来说,框架提供这个initialize方法用来在显示界面之前执行一切与界面无关的设置工作。ApplicationSingleFrameApplication类都提供了一个空的initialize方法。在默认情况下,这个方法是不干啥事的~

 

Code Example 3 subclasses the SingleFrameApplication class. Subclassing the SingleFrameApplication class has many benefits over using the Application class. For example, a SingleFrameApplication already has a primary JFrame instance as its main window. The superclass already overrides many of the lifecycle events to provide default behavior. The SingleFrameApplication class injects resources, implements a simple WindowAdapter object to handle window closings, implements a shutdown method, and performs basic operations to save and restore a session. In general, you should probably avoid subclassing the Application class directly. Using SingleFrameApplication provides your application with helpful default behaviors.

 

示例3继承了SingleFrameApplication类。继承这个类要比继承Application类多一些好处。例如,SingleFrameApplication已经包含了一个JFrame实例作为主窗口。它同时也已经覆盖了一部分生命周期事件,提供一些默认行为。它可以注入资源,还实现了一个WindowAdapter类用来处理窗口关闭,并且执行一些基本的操作去保存和恢复会话。总得来说,你应该避免直接继承Application类。SingleFrameApplication类为你的程序提供了一些很有用的默认行为。

 

示例3

public class BasicSingleFrameApp extends SingleFrameApplication {

    JLabel label;

 

    @Override

    protected void startup() {

        getMainFrame().setTitle("BasicSingleFrameApp");

        label = new JLabel("Hello, world!");

        label.setFont(new Font("SansSerif", Font.PLAIN, 22));

        show(label);

    }

 

    public static void main(String[] args) {

        Application.launch(BasicSingleFrameApp.class, args);

    }

 

}

 

 

As you can see, the basic "Hello, world!" application gets noticeably shorter in Code Example 3. The only new APIs here are the show and getMainFrame method calls. A SingleFrameApplication subclass can use the show method to provide its main, default UI component. The superclass adds the component -- a label in the case of Code Example 3 -- to the main frame and displays the main frame with that component.

 

如同在示例中看到的,简单的”hello, world!”程序明显变短! 唯一的新的APIshow方法和getMainFrame方法。一个SingleFrameApplication的子类可以用show方法来显示它默认的界面。在示例3中,为主界面加入了一个标签,并且用show方法来显示出来。

 

The Ready State

After initialization and startup, the framework calls another lifecycle method that you can optionally override. The framework calls your application's ready method after all initial GUI events related to your UI startup have been processed. Overriding the ready method is your opportunity to perform tasks that will not delay your initial UI. Place here any work that depends on your UI being ready and visible.

 

就绪状态

在初始化和启动之后,框架会调用另一个可选的生命周期。框架会在完成所有的界面相关的初始化工作之后调用你的ready方法。覆盖ready方法为你提供了一个机会来执行一些任务但不会延迟你的初始界面。把所有要在界面显示之后才做的事情放在这个方法里。

 

Application Exit and Shutdown

The Application class implements an exit method to gracefully shut down the application. According to the Application implementation, a graceful shutdown involves asking any ExitListener objects whether exiting is possible, then alerting those same listeners that the Application will actually shut down, calling the shutdown method, and finally calling the System.exit method.

 

程序的退出和关闭

Application类实现了exist方法来优雅地关闭应用。根据Application的实现,一个优雅的关闭包括:向所有的ExitListener询问是否可以关闭,向这些Listener通知应用就将要关闭,调用shutdown方法,最终调用System.exit方法彻底结束。

 

But the Application class does not call the exit method directly. Your application should do this if it subclasses the Application class. However, the SingleFrameApplication class does this for you when you close the main window frame. It implements a WindowListener that calls the exit method when you close the application's main window frame. Regardless of how you finally call the exit method, you should override the shutdown method to perform application-specific cleanup before the application terminates completely. The shutdown method is your opportunity to close database connections, save files, or perform any other final tasks before your application finally quits.

 

但是Application类是不会直接调用exit方法的。如果你继承了Application类,那你就应该自己做这件事情。然而,如果你继承自SingleFrameApplication类,那当你关闭窗口的时候,它就替你完成了上述的工作。它实现了一个WindowListener,它会在你关闭窗口的时候调用exit方法。但不管你最终是怎么调用exit方法的。你都应该覆盖exit方法在程序彻底关闭之前来执行一些应用特定的清理工作。Shutdown方法是你关闭数据库连接,保存文件或者执行一些最后任务的好机会。

 

The SingleFrameApplication superclass implements a simple shutdown method. It saves its window-frame session state and includes all secondary frame state as well. For this reason, you should remember to call super.shutdown() if you override this method. Code Example 4 shows you what to do.

 

SingleFrameApplication类实现了一个简单的shutdown方法,它保存所有窗口的会话状态。正因如此,你应该在你覆盖的shutdown方法中记着调用super.shutdown(). 示例4展开了如何去做。

 

示例4

@Override

protected void shutdown() {

    // The default shutdown saves session window state.

    super.shutdown();

    // Now perform any other shutdown tasks you need.

}

 

 

Implement the Application.ExitListener interface to allow your application the chance to veto or approve requests to exit the application. The default exit algorithm includes calls to all listeners before calling the shutdown method. By implementing the ExitListener interface, you can alert your customers or users of the impending shutdown operation and even allow them to stop the shutdown.

 

实现Application.ExitListener接口,它可以允许你否决或者同意退出操作。默认的exit算法会在调用shutdown方法之前先查看所有的listener. 通过实现ExitListener接口,你可以提醒你的客户程序正在退出,并且还可以允许他们取消关闭操作。

 

The ExitListener interface has two methods:

public boolean canExit(EventObject e)

public void willExit(EventObject e)

Use the canExit method to respond to the exit request. Return a true value to allow the exit, false otherwise. The willExit method is simply an alert notification, but you can also use it for any preparations you need for the ensuing shutdown.

 

ExitListener接口有两个方法

Public Boolean canExit(EventObject e)

Public void willExit(EventObject e)

使用canExit方法去回答退出请求。它返回一个true代表可以退出,返回一个false代表不可以退出。willExit方法仅仅起提示作用,但你也可以用它来执行一些退出前的准备工作。

 

Code Example 5 shows how you might implement an ExitListener object. Notice that the example calls the exit method, which is implemented by the Application superclass. The exit method notifies all ExitListener objects and calls the shutdown method only if all listeners approve the request to exit.

 

示例5向你展示了如何实现ExitListener。请注意,例子调用了exit方法,它是在Application类中实现的了。Exit方法会提醒所有的ExitListener对象,如果所有的listener都同意退出,框架会调用shutdown方法最终关闭程序。

 

示例5

public class ConfirmExit extends SingleFrameApplication {

    private JButton exitButton;

 

    @Override

    protected void startup() {

        getMainFrame().setTitle("ConfirmExit");

        exitButton = new JButton("Exit Application");

        exitButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                exit(e);

            }

 

        });

        addExitListener(new ExitListener() {

            public boolean canExit(EventObject e) {

                boolean bOkToExit = false;

                Component source = (Component) e.getSource();

                bOkToExit = JOptionPane.showConfirmDialog(source,

                                "Do you really want to exit?") ==

                                JOptionPane.YES_OPTION;

                return bOkToExit;

            }

            public void willExit(EventObject event) {

 

            }

        });

        show(exitButton);

    }

 

    @Override

    protected void shutdown() {

        // The default shutdown saves session window state.

        super.shutdown();

        // Now perform any other shutdown tasks you need.

        // ...

    }

 

    /**

     * @param args the command-line arguments

     */

    public static void main(String[] args) {

        Application.launch(ConfirmExit.class, args);

    }

 

}

 

 

Code Example 5 defines a SingleFrameApplication that contains a single JButton in its main frame. When you click that button or attempt to close the main window, the application's ExitListener confirms the user request. Figure 2 shows the application responding through its listener interface.

 

示例5定义了一个SingleFrameApplication子类,主界面包含一个JButton按钮。当你点击这个button或者当你试图关闭主窗口的时候,程序中的ExitListener将会向用户询问是否真的要退出。

 

 

Resource Management

 

Most applications use common resources such as text, icons, colors, and font definitions. If you ever want to localize your application to other locales or platforms, you should externalize resources to facilitate easy modifications or translations to other languages. Resources are defined in ResourceBundle implementations, which are usually ListResourceBundle subclasses or properties files.

 

资源管理

 

大部分应用都用到了一些常用的资源,比如text文本,icons图标和字体定义。如果你想让你的程序可以在其它地区或平台本地化,那你应该把这些资源“外化”,这样可以很容易地修改和翻译成其它语言。资源是在ResourceBundle实现中定义的,一般用到的是ListResourceBundle子类或者Properties文件。

 

The framework helps you define and organize resources for individual classes and for the entire application. Resources that are shared throughout the application should exist in a ResourceBundle file named after your Application subclass name. Resources for a specific form or class should exist in a ResourceBundle file named after that specific class. In both cases, the ResourceBundle implementations must exist in a resources subpackage immediately below that of the class for which they provide resources.

 

框架可以帮助你对每一个类或者整个程序定义以及组织资源。整个程序都通用的资源应该在一个ReourceBundle文件中,这个文件的名字应该以你的程序名打头。而特定的一个窗体或者某一个类的资源也是保存在一个reourcebundle文件中,它的名字是以特定的类名为开头。不论哪种情况,ReourceBundle文件都必须保存在一个resource包中,这个包使用它的类在同一级的classpath中。

 

Table 1 shows the relationship among an application class or form, its ResourceBundle name, and the ResourceBundle file name.

 

1展示了类或窗体,ResourceBundle名,ResourceBundle文件名,这三者之间的关系

ResourceBundle

ResourceBundle文件名

 

demo.MyApp

demo.resources.MyApp

demo/resources/MyApp.properties

demo.hello.HelloPanel

demo.hello.resources.HelloPanel

demo/hello/resources/HelloPanel.properties

demo.hello.ExitPanel

demo.hello.resources.ExitPanel

demo/hello/resources/ExitPanel.properties

 

Instead of loading and working with ResourceBundle files directly, you will use the ResourceManager and ResourceMap framework classes to manage resources. A ResourceMap contains the resources defined in a specific ResourceBundle implementation. A map also contains links to its parent chain of ResourceMap objects. The parent chain for any class includes the ResourceMap for that specific class, the application subclass to which the class belongs, and all superclasses of your application up to the base Application class.

 

你应该使用ResourceManager(资源管理器)和ResourceMap(资源映射)这两个类来管理资源,而不是直接读取ResourceBundle文件。一个ResourceMap包含一个指定的ResourceBundle中中所有资源。一个ResourceMap也同时包含一个指向上一级ResourceMap的引用。任何一个类对应的ResourceMap的父链都包含这个特定类的所有资源,这个类的子类的资源,以及所有的从这个类到最底层的Application类之间所有类的资源。

 

The ResourceManager is responsible for creating maps and their parent chains when you request resources. You will use the ApplicationContext to retrieve ResourceManager and ResourceMap objects.

You have three options for working with resources:

Manually load and use ResourceMap objects and their resources.

Use the framework to automatically inject resources into the UI components that need them.

Use the framework to automatically inject resources into the object fields that need need them.

 

在你请求资源的时候,ResourceManager负责创建ResourceMap和它们的父链。你可以通过ApplicationContext获取ResourceManagerResourceMap

 

你有三种方法来使用资源:

手动加载和使用ResourceMap和它的资源

用框架自动将资源注入到界面部件中

用框架自动将资源注入到对象的属性中。

 

Manual Resource Management

The ApplicationContext class provides access to the ResourceManager and its ResourceMap instances. Retrieve the context using the getContext method of your Application instance. Use the context to retrieve a resource manager and map. Use the map to retrieve resources for your application or specific class. Code Example 6 shows how to retrieve resources and apply them to UI components.

 

手动资源管理

ApplicationContext提供了对ResourceManagerResourceMap实例的访问。在Application实例中用getContext方法来获取ApplicationContext。用上下文来获取ResourceManagerResourceMap。用ResourceMap来为你的程序中的类提供资源。 示例6展示了如果获取资源以及如果把它们应用在UI组件上。

 

示例6

public class HelloWorld extends SingleFrameApplication {

    JLabel label;

    ResourceMap resource;

 

    @Override

    protected void initialize(String[] args) {

        ApplicationContext ctxt = getContext();

        ResourceManager mgr = ctxt.getResourceManager();

        resource = mgr.getResourceMap(HelloWorld.class);

    }

 

    @Override

    protected void startup() {

        label = new JLabel();

        String helloText = (String) resource.getObject("helloLabel", String.class);

        // Or you can use the convenience methods that cast resources

        // to the type indicated by the method names:

        // resource.getString("helloLabel.text");

        // resource.getColor("backgroundcolor");

        // and so on.

        Color backgroundColor = resource.getColor("color");

        String title = resource.getString("title");

        label.setBackground(backgroundColor);

        label.setOpaque(true);

        getMainFrame().setTitle(title);

        label.setText(helloText);

        show(label);

    }

    // ...

}

 

You can also retrieve a resource map using the convenience method in the ApplicationContext instance:

resource = ctxt.getResourceMap(HelloWorld.class);

 

你也可以用一个ApplicaContext提供的简便方法来获取ResourceMap

 

resource = ctxt.getResourceMap(HelloWorld.class);

 

In Code Example 6, the HelloWorld class uses three resources: a label's text, a color for the label background, and text for the frame's window title. It gets those resources from a resource map for the HelloWorld class:

resource = mgr.getResourceMap(HelloWorld.class);

 

在示例6中,HelloWorld类使用了三个资源: Label的文本、Label背景的颜色、主窗体的Title。这些资源都是从与HelloWorld类对应的资源映射中获取的。

 

resource = mgr.getResourceMap(HelloWorld.class);

 

Provide the Class instance that represents either your Application class or another specific class in your application. In this example, the resource manager will search and load a ResourceMap that contains resources from the resources/HelloWorld resource bundle. In this case, the bundle implementation is a file named resources/HelloWorld.properties. Code Example 7 shows part of the file.

在使用时给getResourceMap方法提供一个代表你的Application的或者某个特定的类的Class对象。在这个例子中。资源管理器(ResourceManager)将会搜索和加载一个ResourceMap实例,它包含着 resources/Helloworld下面的所有资源。 在这个例子中,ResourceBundle文件的具体实现是一个以”resources/HelloWorld.properties”为名的Properties文件。 示例7展示了这个文件的一部分内容:

 

示例7

helloLabel = Hello, world!

color = #AABBCC

title = HelloWorld with Resources

 

Figure 3 shows the HelloWorld application. Each of the resources -- the window title, the label text, and the label background color -- were manually retrieved and applied to each specific component. If you use manual resource management, you must provide code to perform all steps of retrieving and using the resources in the appropriate component.

 

3演示了HelloWorld程序。每一个资源,包括窗口标题,标签文本,标签背景颜色,都是手动获取并且放入组件的。如果你使用手动资源管理,那你就需要自己写代码来提供资源和获取资源。

 

 

3. 如果你使用手动资源管理,那你就必须编写所有代码去获取和使用资源。

 

Component Resource Injection

The framework can automatically inject resources into your components. Using the ResourceMap object's injectComponents method, you tell the framework to retrieve resources from its resource map chain and to apply them to components. This works very well when you provide your application's root window because the method recursively travels down containers, injecting UI components as it traverses the container hierarchy. To use the injectComponents method, you must use a naming convention that helps the framework match component names and their resources.

 

组件资源注入

 

框架可以自动地把资源注入到你的组件当中。使用ResourceMapinjectConponents方法,框架会从资源映射链(resource map chain)中获取资源并把它们注入到组件当中。当你向框架请求为主窗口注入资源时,这个方法就非常棒! 因为它会递归地访问每一级容器然后把资源一级一级地注入到组件当中。想要使用injectConponents方法,那你必须遵循命名规范来帮助框架匹配组件名和对应的资源。

 

The naming convention is simple. Just use the setName method on your UI components to give them a name. In your ResourceBundle files, use that name to define resources for that component. In the resource file, append a period (.) and the property name to the component name to define a resource for a specific property.

 

命名规范也很简单,只需要对你的界面组件使用setName方法给它们一个名字就行啦! 在你的ResourceBundle文件中,就用这个名字来指定组件。在资源文件中,通用使用(.)来指定资源具体对应与哪一个属性。

 

For example, if you want to define the text property of a button, you should name the button and use the button's name in the resource file. If the button's name is btnShowTime, you can define its resource text in the resource file using the same btnShowTime name. Because you want to set the text of the button, the resource name should be btnShowTime.text.

 

例如,如果你想指定一个buttontext属性,那你应该给这个button命名并且在资源文件中使用这个名字。如果button的名字是btnShowTime,那你就可以在资源文件中使用相同的btnShowTime指定此资源是属于btnShowTime这个button的。因为你想把这个资源注入到buttontext属性,所以资源名应该是 btnShowTime.text

 

Code Example 8 shows several resource definitions in a resources/ShowTimeApp.properties file. Use UI component names and their property names to define injectable resources.

 

示例8展示了在 resources/ShowTimeApp.properties文件中的资源定义。使用组件名和它们的属性名来指定需要注入的资源

 

示例8

btnShowTime.text = Show current time!

btnShowTime.icon = refresh.png

 

txtShowTime.text = Press the button to retrieve time.

txtShowTime.editable = false

 

 

Code Example 8 shows resources for two components: btnShowTime and txtShowTime. The btnShowTime.text resource defines the text that should be injected into a button component. The btnShowTime.icon resource defines the icon that the same button will display. The second component name is txtShowTime, which is a text component in the application.

 

示例8显示了两个组件的资源:btnShowTimetextShowTime. btnShowTime.text定义了应该注入到button组件中的文本。btnshowTime.icon定义了应该注入到这个button的图标。第二个组件名是textShowTime, 它在程序中是一个text组件。

 

You can call injectComponents directly, but you can save yourself some effort just by subclassing the SingleFrameApplication framework class. This class defines a show method that automatically injects component resources. Code Example 9 shows how to use component naming conventions, the SingleFrameApplication superclass, and the properties file shown in Code Example 8 to inject resources.

 

 

你可以直接调用injectConponents,但是你可以通用继承SingleFrameApplication类来省下这一步。这个类定义了一个show方法,它可以自动地为组件注入资源。示例9展现了如何使用命名规范,SingleFrameApplication类以及如何把示例8中的资源注入到组件中。

 

示例9

public class ShowTimeApp extends SingleFrameApplication {

    JPanel timePanel;

    JButton btnShowTime;

    JTextField txtShowTime;

 

    @Override

    protected void startup() {

        timePanel = new JPanel();

        btnShowTime = new JButton();

        txtShowTime = new JTextField();

 

        // Set UI component names so that the

        // framework can inject resources automatically into

        // these components. Resources come from similarly

        // named keys in resources/ShowTimeApp.properties.

        btnShowTime.setName("btnShowTime");

        txtShowTime.setName("txtShowTime");

 

        btnShowTime.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                Date now = new Date();

                txtShowTime.setText(now.toString());

            }

        });

 

        timePanel.add(btnShowTime);

        timePanel.add(txtShowTime);

        show(timePanel);

    }

    // ...

}

 

 

Code Example 9 does not explicitly load a ResourceMap. The framework does that for you. All you have to do is follow the naming conventions for components, create a ResourceBundle file using the application class name, and use the component names appropriately in the resource file. The framework will find and inject the resources for you. Figure 4 shows the ShowTimeApp running. Notice that the button label and icon are set correctly using resources from the resources/ShowTimeApp.properties file.

 

示例9中并没有显式地加载ResourceMap. 框架替你完成这个任务。你所要做的只是遵从命名规范为组件命名,创建一个使用类名的ResourceBundle文件以及在资源文件中使用恰当的命名。框架会找出对应的资源并且注入组件。图4展示了运行时的ShowTimeApp应用。请注意,button的标签和图标都通过 resources/ShowTimeApp.properties文件注入的。

 

在图4中,button的文本和图标都是注入的资源。

 

 

 

Field Resource Injection

The framework supports field resource injection too. You can inject resources into fields with types such as String, Color, and Font by marking those fields with the @Resource annotation. When you use the ResourceMap object's injectFields method, the framework will automatically inject resources into fields that you mark with this annotation.

 

属性资源注入

 

框架也同时支持属性资源注入。通过为属性添加@Resource元数据标记,你可以为属性注入有类型区别的资源,例如String,ColorFont。当你使用ResourceMapinjectField方法时,框架会自动把资源注入到你所标记的属性。

 

Field resource injection is similar to component resource injection because the framework handles the retrieval for you. However, important differences do exist. For example, field resource injection does not work on container hierarchies. When you inject field resources, the injectFields method works on a single target object's fields and does not traverse containers.

Again, you should put the resources in your application-level or class-level resource file. Remember to load those resources using the getResourceMap method, passing in the class object if you want class-level resources instead of application-level resources.

 

属性资源注入与组件资源注入很相似,因为两者都是由框架来替你获取资源。然后也存在一些重要的区别。例如,属性资源注入并不是工作在容器层次结构上的。当你使用属性资源注入时,injectFields方法只针对一个特别的目标来注入资源,它并不会用递归的方法来遍历整个容器。其次,你应该把资源放在Application 级或Class级的资源文件中(application-level or class-level resource file)。请记信用getResourceMap方法来载这些资源,把这些资源类对象中或者整个应用当中。

 

By default, field resources should have a name that contains their class name as a prefix. For example, if the class MyApp has a field named myIcon, the resource will have the name MyApp.myIcon in the ResourceBundle file and in the corresponding ResourceMap object.

Code Example 10 shows how you can inject field resources into your code. Not only does this example inject the field, but it also uses class-specific resources defined in a resources/NameEntryPanel resource bundle. Mark fields with the @Resource annotation to use field injection.

 

在默认情况下,属性资源应该有一个名字,它以类名为开头。例如,如果MyApp类有一个属性叫作myIcon,那对应的资源也就会有一个Myapp.myIcon的键值出现在ResourceBundle文件和对应的ResourceMap对象中。

In order to inject the resources for the NameEntryPanel class, the resources must be defined in a class resource file. Code Example 11 shows the sample file, which defines a parameterized string with key NameEntryPanel.greetingMsg. You should name field resources in the resource file using the class name followed by a period (.) and the field name: <classname>.<fieldname>. That naming pattern is the default, but you can override this by providing a different name using the @Resource annotation's key element.

 

为了给NameEntryPanel类注入资源,资源必须在一个类资源文件”(class resource file)中指定出来。示例11展示了这个资源文件,它指定了一个名叫NameEntryPanel.greetingMsg带参数的字符串作为键值(key),你在给属性资源命名的时候要遵循命名规范,资源名还是以类名为打头,加一个(.),再加上属性名。默认情况下就是以这种方法来命名的。当然你也可以用其它的名字,只需要在给属性打@Resource标签的时候加上key值。(@Resource(key = .....) )

 

示例11

# resources/NameEntryPanel.properties

NameEntryPanel.greetingMsg = Hello, %s, this string was injected!

 

 

 

 

 

6 ResourceConverter把文本表示的资源转换为他们的最终资源对象。

 

The ResourceMap class provides support for parameterized strings. Code Example 10 uses a parameterized string in its resources but did not use ResourceMap to insert the parameter. The parameterized string is this:

 

ResourceMap提供对带参数的字符串的支持。示例10中使用了一个带参数的字符串但并不是用ResourceMap来插入参数。在示例中,带参数的字符串如下:

 

NameEntryPanel.greetingMsg = Hello, %s, this string was injected!

 

 

To use this same resource and insert the parameter as you retrieve the NameEntryPanel.greetingMsg text, you must provide an additional argument list to the getString method. In addition to the resource key, you should provide an argument list that contains the data to insert into the parameterized string. Code Example 13 shows code that could replace the original action handler code in Code Example 10. This new code uses the additional convenience functionality of getString to insert a name into the parameterized resource string.

 

要想使用这个带参数的资源,当你获取这个NameEntryPanel.greetingMsg时,你还需要给getString方法额外提供一个参数表。getString方法会把参数表的内容插入到带参数的字符串中。示例13替换了示例10中的事件处理。新的代码使用了getString方法来方便地把name插入到带参数的资源中。

 

示例13

private void btnGreetActionPerformed(java.awt.event.ActionEvent evt) {

    String personalMsg = resource.getString("NameEntryPanel.greetingMsg", txtName.getText());

    JOptionPane.showMessageDialog(this, personalMsg);

}

 

 

Action 动作

 

(下文中,大写的Action一般表现Action类,不翻译,小写的action代表一个操作或者动作,这里翻译为动作 .)

To handle UI events, you will implement an ActionListener object for a specific component. A more powerful form of an ActionListener is an AbstractAction object, which implements the javax.swing.Action interface. The Action interface allows you to define an event handler. In addition, it lets you associate an icon, text, mnemonic, and other visual elements with the event. Using the Action interface, often with an AbstractAction class, you can conveniently put all the relevant visual cues and the event-processing logic for a component in one place.

 

为了处理UI事件,你需要为指定的组件实现一个ActionListener对象。一个比ActionListener更好的形式是AbstractAction对象,它是一个抽象类,实现了javax.swing.Action接口。Action接口允许你指定事件处理器(event handler)。另外,它还可以让你将图标,文本,快捷方式以及其它视觉元素一起捆绑在一个事件上。使用Action接口,更一般的是使用AbstractAction类,你可以很方便地把一个组件的相关的视觉元素和事件处理逻辑放在同一个地方。

 

Another convenient benefit of using Action interfaces is that you can reuse the same action across multiple UI components. GUIs often provide multiple ways to accomplish a task. For example, to increase the font size of a piece of text, an application might provide both a menu and a button interface to perform the same task. The multiple different ways of accomplishing the same task should behave the same way, and you should enable or disable those actions simultaneously across all associated components -- something that Action interfaces will help you do.

 

另一个方便的地方是,使用Action接口,你可以让多个UI组件的使用同一个事件处理,达到重用的效果。GUI往往为同一个任务提供多个入口机制。 例如,为一块文本加大字体,即可以在菜单中提供此功能,也可以用一个界面上的button实现此功能。为了将从多种不同的方式实现同一功能,你需要同时开启或禁用绑定在同一个动作(action)上的多个组件,在这时,Action接口会起大作用。

 

Despite the convenience of using the Action interface, action objects have a few problems. First, visual properties should be localized. All too often, developers hardcode the action's visual elements, making localization difficult or impossible. Additionally, manually creating Action objects can be a chore, especially if you use all the visual components that are available.

The Swing Application Framework helps you manage actions in three ways:

 

虽然Action接口带来很多方便,但是Action对象却有一些问题。首先,可视的属性应该是本地化的。但是在绝大多数时候,开发人员往往直接把Action的视觉属性硬编码,这就使得本地化非常困难甚至不可能。另外,手动创建Action对象也非常繁琐,尤其是当你用到很多的可视组件的时候。

 

但是没关系,Swing应用程序框架可以为你轻松搞定,这里有三种方式用来它:

 

The framework provides the @Action annotation to mark and name methods that will be used by Action implementations.

An ActionManager class creates javax.swing.ActionMap instances for classes that contain @Action methods.

The framework supports localization of Action elements such as icons, text, and mnemonics.

 

框架提供了@Action元标记来标注和指定Action实现中用到的方法

ActionManager类为每个包含@Action标记方法的类创建一个ActionMap实例

框架提供对Action元素(图标,文本和快捷方式)的本地化支持。

 

The following discussion about Action objects uses a demo application called ActionApp and a panel named ResizeFontPanel. Figure 7 shows the demo UI, which provides the context for the Action class and annotation descriptions.

 

以下的关于Action对象的讨论用到了一个演示程序,名叫ActionApp 以及一个名叫ResizeFontPanel的面板。图7显示了这个演示的界面

 

7  多个组件使用同一个可用的Action对象。

 

The @Action Annotation

Define and name your Action objects using the @Action annotation. Mark Action event-handling methods with this annotation. The annotation has several optional elements, including a way to override the default name of the Action object. The default action name is the method name. Code Example 14 shows how to use this annotation with code that increases and decreases the font size for a text component.

 

@Action元标记

 

@Action元标记来指定你的Action对象。用@Action标记指定Action的事件处理方法。这个标记有好几个可选的参数,包括手动指定对应的Action名。在默认情况下,Action名就是方法名。示例14展示了如果在代码中使用@Action标记来加大和减小字体

 

示例14

public class ResizeFontPanel extends javax.swing.JPanel {

    ...

    @Action

    public void makeLarger() {

        Font f = txtArea.getFont();

        int size = f.getSize();

        if (size < MAX_FONT_SIZE) {

            size++;

            f = new Font(f.getFontName(), f.getStyle(), size);

            txtArea.setFont(f);

        }

    }

 

    @Action

    public void makeSmaller() {

        Font f = txtArea.getFont();

        int size = f.getSize();

        if (size > MIN_FONT_SIZE) {

            size--;

            f = new Font(f.getFontName(), f.getStyle(), size);

            txtArea.setFont(f);

        }

 

    }

    ...

}

 

 

Code Example 14 defines two Action methods with default names: makeLarger and makeSmaller. When invoked, the actions reset the font for a text area, which is named txtArea.

 

示例14定义了两个Action方法,一个叫makeLager,一个叫makeSmaller.当被调用的时候,这两个方法就修改TextArea中文字的字体大小.

 

The ActionMap class associates actions with UI components by using the component's setAction method. Use the Action object's name. In Code Examples 14 and 15, the names are makeLarger and makeSmaller. The ResizeFontPanel object has a button for both actions. Code Example 15 shows how to bind the Action objects with the components.

 

通过UI组件的setAction方法为其添入Action名名,ActionMap就可以将动作和UI组件连接在一起. 在示例1415,这两个名字分别是makeLarger makeSmaller. ResizeFontPanel对象有两个button, 示例15的代码将14中的两个动作与两个button连接在一起.

 

示例15

// ctx is the ApplicationContext instance.

ResourceMap resource = ctxt.getResourceMap(ResizeFontPanel.class);

resource.injectComponents(this);

ActionMap map = ctxt.getActionMap(this);

 

btnMakeLarger.setAction(map.get("makeLarger"));

btnMakeSmaller.setAction(map.get("makeSmaller"));

 

Notice that the ApplicationContext provides the ActionMap for this class. As a side note, the code also uses the context to inject component resources. An ActionMap instance stores all the Actions that are associated with this class. In this case, only two Action objects exist: makeLarger and makeSmaller. Retrieve the Action objects using the ActionMap instance's get method, providing the Action instance name. Bind the UI component and Action using the component's setAction method.

 

请注意,ActionContext为这个类提供了ActionMap. 顺便说一声,这个代码中也用到了组件注入. 一个ActionMap实例存储着所有与这个类相关的动作(actions. 在这个示例中,只有两个动作,一个是makeLarger 一个是makeSmaller . 使用ActionMap实例的get方法可以获得Action对象, 用的时候只需要填上动作的名字就可以了. 使用UI组件的setAction方法就可以将动作和UI组件绑定在一起.

 

Action Resources

Code Example 15 shows how to get the ActionMap for the ResizeFontPanel. It also shows how to call setAction on specific components using the Action objects defined by the @Action annotation. But where did the action's icon and text come from in Figure 7? That's also shown in Code Example 15. Take a closer look at the line that retrieves the ActionMap instance for the ResizeFontPanel object:

 

Action资源

 

示例15展示了如何在ResizeFontPanel中获得ActionMap. 示例也展示了如何在指定的组件上使用setAction方法连接用@Action标记的方法. 但是图7 中动作的图标和文字是从哪来的~? 这在示例15中也出现这个问题. 让我们仔细看看ResizeFontPanel中是如何获得ActionMap

 

ActionMap map = ctxt.getActionMap(this);

 

 

This single line of code does a lot of work behind the scenes. First, as described earlier, it creates Action instances for each method marked with an @Action annotation in the target object. Second, it retrieves resources for the Action from the target's ResourceMap. The ResourceMap for this ResizeFontPanel class includes properties from the resources/ResizeFontPanel.properties file. This file defines a few resource key-value pairs and includes the data as shown in Code Example 16. The makeLarger and makeSmaller actions have text and icons defined by this resource file. The getActionMap method includes these resources when it creates the ActionMap for the ResourceFontPanel object.

 

仅仅这么一行代码 ,其实在它背后有非常多的工作. 首先,它为每一个用@Action标记的方法创建一个Action实例. 然后从ResourceMap中为动作获得资源.ResizeFontPanel类的ResourceMap包含着resources/ResizeFontPanel.properties文件中的所有Properties. 文件中定义了一些键---值对来表示在示例16中用到的数据。makerLargermakeSmaller这两个动作的文本和图标都在这个文件中指定了。getActionMap方法在为ResizeFontPanel创建ActionMap的时候就包含了这些资源。

 

# resources/ResizeFontPanel.properties

makeLarger.Action.text = Increase font size

makeLarger.Action.icon = increase.png

 

makeSmaller.Action.text = Decrease font size

makeSmaller.Action.icon = decrease.png

 

Because an Action object's resources exist in a ResourceMap, you can localize the resources as appropriate for a specific operating system (OS) platform or locale. Follow the usual naming standards for creating alternate ResourceBundle files. You can find out more about localization and ResourceBundle naming by consulting the ResourceBundle documentation.

 

因为Action对象的资源也是在ResourceMap中保存的,所以你也可以对这些资源进行本地化处理,将其处理为面向特定OS或地区的资源。当然在使用时,你也可以在遵循命名规范的情况下创建自己的ResourceBundle文件。如果你对本地化以及ResourceBundle命名还有问题,可以参看ResourceBundle文档。

 

Tasks

 

Always use a background thread for input/output (I/O) bound or computationally intensive tasks. Long-running tasks can block the event dispatch thread (EDT). The new Swing Application Framework provides support for starting, stopping, and monitoring the progress of background tasks. Although the SwingWorker class in the Java SE 6 platform has most of what's needed, the framework provides a Task class to support more functionality.

 

Tasks任务

 

一定要使用后台线程来处理I/O或者运算密集的任务。长程(Long-run)运算的任务会阻塞EDT,使界面失去响应。新的Swing应用程序框架就提供了对后台任务的开始,停止,进度指示的功能。虽然Java SE6中的SwingWorker已经提供了大部分所需,但是框架还是提供了Task类来支持更强大的功能。

 

The Task class extends a SwingWorker implementation, which is similar to the SwingWorker available in Java SE 6. A TaskService class helps you execute tasks, and a TaskMonitor helps you monitor their progress.

 

Task类实际上继承了SwingWorker类,它当然与JavaSE6SwingWorker很相似。TaskService类帮助你执行task任务。其外,还有一个TaskMonitor类帮助你查看任务的进度。

 

The easiest way to use the Task class is to extend it, override one or more methods, and use the Task with an Action handler. You can can monitor the task and get intermediate result information as the task runs. This article will describe only the basic usage associated with event handling.

 

使用Task类最简单的办法就是去继承它,覆盖一个或更多个方法,然后在一个动作的事件处理方法中使用Task。你可以监视任务,在它运行的时候就得到它当前的结果及状态。这篇文章会向你讲述Task与事件处理的基本用法。

 

Code Example 17 creates a Task class. The NetworkTimeRetriever class overrides the doInBackground method, which is the method that performs your long-running task. The Task class defines several methods that communicate the success -- or failure -- of the completed task. One of those methods is the succeeded method. The Task superclass calls succeeded when the doInBackground method finishes its work. The succeeded method runs on the EDT, so you can use it to update GUI components when your task completes successfully. The NetWorkTimeRetriever is an inner class, so it has access to fields in the NetworkTimeApp class. The important field that it needs is the txtShowTime variable, which is a JTextField instance. The Task completes its duties when it finally displays the current time provided by the National Institute of Standards and Technology (NIST) time servers.

 

示例17创建了一个Task类。NetworkTimeRetriever类覆盖了doinBackGround方法,这是一个用来执行“长程”任务的方法。Task类还定义了其它一些方法,它们与任务的“成功”,“失败”,“完成”状态相关连。其中有一个“成功”(succeeded)方法,Task类会在doinbackground方法完成它的工作之后呼叫succeeded方法。Succeeded方法是在EDT基础上运行的,所以你可以在任务完成后,用它来刷新GUI组件。NetworkTimeRetriever是一个内部类,所以它可以访问到NetworkTimeAPp类的属性。这里有一个重要的属性是txtShowTIme变量,它是一个JTextField实例。NetworkTimeRetriever任务会在它从NIST时间服务器获得时候并显示出来之后完全它的使命。

 

示例17

public class NetworkTimeApp extends SingleFrameApplication {

    JPanel timePanel;

    JButton btnShowTime;

    JTextField txtShowTime;

 

    @Override

    protected void startup() {

        // Create components and so on.

        // ...

 

        // Retrieve and set Actions.

        ActionMap map = getContext().getActionMap(this);

        javax.swing.Action action = map.get("retrieveTime");

        btnShowTime.setAction(action);

        timePanel.add(btnShowTime);

        timePanel.add(txtShowTime);

        show(timePanel);

    }

 

    @Action

    public Task retrieveTime() {

        Task task = new NetworkTimeRetriever(this);

        return task;

    }

    // ...

 

    class NetworkTimeRetriever extends Task<Date, Void> {

 

        public NetworkTimeRetriever(Application app) {

            super(app);

        }

 

        @Override

        protected Date doInBackground() throws Exception {

            URL nistServer = new URL("http://time.nist.gov:13");

            InputStream is = nistServer.openStream();

            int ch = is.read();

            StringBuffer dateInput = new StringBuffer();;

            while(ch != -1) {

                dateInput.append((char)ch);

                ch = is.read();

            }

            String strDate = dateInput.substring(7, 24);

            DateFormat dateFormat = DateFormat.getDateTimeInstance();

            SimpleDateFormat sdf = (SimpleDateFormat)dateFormat;

            sdf.applyPattern("yy-MM-dd HH:mm:ss");

            sdf.setTimeZone(TimeZone.getTimeZone("GMT-00:00"));

            Date now = dateFormat.parse(strDate);

            return now;

        }

 

        @Override

        protected void succeeded(Date time) {

            txtShowTime.setText(time.toString());

        }

    }

}

 

In Code Example 17, a button prompts the user to retrieve the time from a network server. The button event handler has the @Action annotation, marking it as an Action handler in its class. The action has the name retrieveTime, and the application's ActionMap stores it. After you retrieve the Action and set the button's Action property, the button will contain the resources associated with it too. For example, the btnShowTime component has an icon and some text that are referenced in a ResourceBundle for the application. When you click on the button, its action handler method retrievetime will execute.

 

在示例17,这个button帮助用户从服务器获取时间。Button的事件处理用@Action标记,这就使它成为一个动作处理方法”(an Action Handler)。这动作名叫“retriveTime”,它存储在框架的ActionMap中。当你获得了这个动作并且正确设置之后,这个button也就有了retriveTime动作的所有资源。例如,btnShowTime组件包含一个图标和一些文本,它们存储在应用的ResourceBundle中。当你点击button,它的动作处理方法retrivetime就会被执行。

 

Notice that the retrieveTime method returns a Task. The method creates a NetworkTimeRetriever task and returns that object. The framework automatically runs it as a background thread. When the task completes successfully, it updates the UI with the network time. The combination of tasks, actions, and resource injection is a powerful and simple way to handle component event handling.

 

请注意:retriveTime方法返回一个任务。这个方法创建了一个NetworkTimeRetriever任务并将其返回。框架会自动把它作为一个后台线程来运行。当任务成功完成,它就会用时间服务器得到的信息更新UI上的txtShowTime组件。任务,动作以及资源注入三者的组合是一个强有力却有非常简单的工具来处理事件。

 

Figure 8 shows the running NetworkTimeApp application. This application is similar to the previous ShowTimeApp application. The primary difference is that the NetworkTimeApp class retrieves its time from a network time server. As a result, the "Retrieve time!" button must execute a long-running task as a background thread.

 

8展示了运行时的NetworkTimeApp程序。这个应用和之前的ShowTimeAPP非常相近。它们之间主要的区别是NetworkTimeApp是从一个时间服务器上获取时间的。因此,“Retrieve time”button必然也需要一个长程的任务来在后台执行。

 

 

8. 使用一个Task实例在后台执行一些长程任务。

 

 

Session State

会话状态

 

The Swing Application Framework provides a way to save session state when your application exits and restore the state when you restart. Session state is the graphical window configuration of your application. This state includes window size, internal frame locations, selected tabs, column widths, and other graphical properties. Saving the GUI session state allows you to restart the application and return to the same window or form at a later time.

 

Swing应用程序框架提供了一种方式允许你在程序退出时保存会话的状态,在下次启动时恢复到之前的状态。会话状态这个名词的意思就是你的程序中图形窗口的设置信息。主要包含窗口大小,内部窗口地址,选种的标签,列宽度,以及其它一些图形信息。保存会话状态可以让你重启程序并且回到上一次时窗口的位置与状态。

 

The SingleFrameApplication class implements session storage for you. When you restart any application subclass of SingleFrameApplication, all GUI geometry should be restored exactly as it was when you exited the application.

 

SingleFrameApplication类为你实现了会话保存。当你重启任何SingleFrameApplication的子类应用时,所有的图形界面的几何信息都会上和一回退出时一模一样。

 

If you use the Application class and want to store the session state, you must save and restore the state yourself. Fortunately, this is not difficult. Like most functionality in the framework, session storage management starts with the ApplicationContext class. Use the ApplicationContext and SessionStorage classes in the application startup method to restore state. Use these classes in the shutdown method to save state. Code Example 18 shows how to save and restore session state using the SessionStorage class:

 

如果你在使用Application类但也想保存会话状态,那你就需要自己动手来保存和恢复会话。幸运的是,这项工作并不复杂。就像框架所提供的其它功能,会话状态管理是从ApplicationContext开始的。在Applicationstartup方法中使用ApplicationContextSssionStorage类就可以恢复状态。在Application shutdown方法中保存状态。示例18展示了如何用SessionStorage保存和恢复会话状态

 

示例18

// ...

String sessionFile = "sessionState.xml";

ApplicationContext ctx = getContext();

JFrame mainFrame = getMainFrame();

 

@Override protected void startup() {

  //...

  /* Restore the session state for the main frame's component tree.

   */

  try {

    ctxt.getSessionStorage().restore(mainFrame, sessionFile);

  }

  catch (IOException e) {

    logger.log(Level.WARNING, "couldn't restore session", e);

  }

  // ...

}

 

@Override protected void shutdown() {

  /* Save the session state for the main frame's component tree.

   */

  try {

    ctxt.getSessionStorage().save(mainFrame, sessionFile);

  }

  catch (IOException e) {

    logger.log(Level.WARNING, "couldn't save session", e);

  }

  // ...

 

 

Note that applications will not usually need to create their own SessionStorage instance. Instead, you should use the ApplicationContext object's shared storage:

 

请注意:程序一般不需要自己去创建SessionStorage实例,而是使用ApplicationContext提供的保存。

SessionStorage ss = ctxt.getSessionStorage();

 

 

Local Storage

 

Session storage depends on the framework class LocalStorage. The LocalStorage class helps the SessionStorage class do its work, but it can also help you to store simple XML-encoded representations of any JavaBean component. The LocalStorage class uses the XMLEncoder and XMLDecoder classes to encode and decode XML files using your application's objects.

 

本地存储

 

会话存储依赖于框架的另一个类LocalStorageLocalStorage类帮助SessionStorage完成它的工作,并且它也可以帮助你用XML表示形式来持久化任意的JavaBean组件。LocalStorage使用XMLEncoderXMLDecored类编码和解码XML文件

 

Again, the ApplicationContext provides access to a shared LocalStorage instance. You should retrieve the LocalStorage instance like this:

 

再次提示,ApplicationContext提供了一个共用的LocalStorage实例,获取LocalStorage实例的方法如下:

 

LocalStorage ls = ctxt.getLocalStorage();

 

 

Now you can use the object's save and load methods to encode and decode objects to your local storage. The LocalStorage class uses your home directory as a base subdirectory for determining the default location of storage files.

 

现在,你就可以用这个ls实例的saveload方法将一个对象编码或解码成XML保存到你的本地存储中。LocalStorage类使用所在系统的home目录作为存储文件的默认地址。

 

Code Example 19 shows how to use the LocalStorage class to store a list of phone numbers named phonelist.xml. In this example, a JList component model is both loaded and saved with an application context's shared LocalStorage instance. The variable file contains the file name that will contain the list's contents. You can see the entire program listing for this and all other code examples in the demo source code, which is provided as a downloadable link at the end of this article.

 

示例19展示了如果使用LocalStorage类把一个电话号码列表保存到phoneList.xml中。在示例中,一个JList组件模型会被LocalStorage实例读取和加载。phoneList.xml文件会保存jList中的内容。你可以在完整的程序代码中找到这个和对其它格式保存的例子,程序的代码在文章结尾有下载。

 

示例19

@Action

public void loadMap() throws IOException {

    Object map = ctxt.getLocalStorage().load(file);

    listModel.setMap((LinkedHashMap<String, String>)map);

    showFileMessage("loadedFile", file);

}

 

@Action

public void saveMap() throws IOException {

    LinkedHashMap<String, String> map = listModel.getMap();

    ctxt.getLocalStorage().save(map, file);

    showFileMessage("savedFile", file);

}

 

 

Figure 9 shows the My Little Black Book application. Notice the tool tip and labels. All these resource items are in an application resource file. All the buttons and text fields have associated actions that provide labels, tips, and event handling.

 

9展示了我的小电话本程序。请注意看那工具提示(tool tip)和那些标签。所有这个资源项都是在资源文件中保存的。所有的buttontextField都有动作绑定,它提供了标签,提示以及事件处理。

 

 

 

 

9  程序使用LocalStorage保存数据。

 

 

The text field at the bottom of the application window shows the file name of the phone list. Files created by the LocalStorage class always exist in platform-specific locations on your host system. The files are, however, always stored under the application user's home directory.

 

在窗口底部的TextField显示数据的文件的文件名。用LocalStorage创建的文件总是和你所使用的系统平台相关的。但是默认情况下文件的地址却总是在保存在用户home目录下。

 

Summary

 

总结

 

The Swing Application Framework (JSR 296) provides a basic architecture and a set of commonly used services for Swing applications. Most applications must implement and manage lifecycle events, UI component event handling, threading, localizable resources, and simple persistence. The framework provides services for managing all these common needs, allowing you to concentrate on your application's unique functionality.

 

Swing应用程序框架(JSR 296)Swing应用提供了一个基础的结构和一组常用的服务。大部分程序都需要实现和管理生命周期,UI组件的事件处理,线程,资源本地化和简单的持久化。框架为这些常见需求提供了服务,这使你更加关注于你的程序的业务逻辑,而不是Swing的开发上。

 

Framework architecture includes Application and ApplicationContext classes. The ApplicationContext instance will provide help for session and local storage, task management, resource management, and most other services that the framework provides.

 

框架的结构主要包括ApplicationApplicationContext这两个类。ApplicationContext实例提供会话和数据的本地存储,任务管理,资源管理,和框架所提供的大部分其它服务。

 

Your framework application has a well-defined life cycle. Each lifecycle stage has a corresponding method that your application can override to provide its unique functionality.

 

应用程序应该有一个良好定义的生命周期。在用Swing应用程序框架时,每一个周期都有一个对应的方法,你可以覆盖它为你的程序提供特有的功能。

 

Framework applications have support for application- and class-level resources. Using the ResourceManager and ResourceMap classes, you can automatically inject those resources into your application, making them easy to localize.

 

框架支持程序级和类级两种资源。使用ResourceManagerResourceMap类,你可以将这些资源自动注入到你的程序,这使本地化大为简单。

 

The @Action annotation helps you create UI component event handlers. Using this annotation and ResourceMap objects, you can combine an action's functionality and visual elements in a single place, making the resulting Action object reusable and localizable.

 

@Action标记帮助你创建UI组件的事件处理。同时使用@ActionResourceMap对象,你可以把动作的功能和视觉元素在同一地方绑定起来,这可以让Action对象可重用化和本地化。

 

Some event handlers should run as background threads. Use the Task class to define background threads. The framework makes it easy to combine Action and Task instances to make your GUI run smoothly and reliably with long-running event handlers.

 

有一些事件处理应该放在后台线程运行。使用Task类来定义后台线程。框架可以轻松将ActionTask结合在一起,使你的UI运行流畅并且在长程事件处理中保持稳定。

 

Finally, the framework provides support for session state and local storage. Session state includes component geometry, screen location, selected tabs, column widths, and other UI state. You can save session state before the application exits, and you can restore the same state when the application starts again. Additionally, the framework gives you a simple storage API that lets you store local objects on your host platform.

 

最后,框架还提供了会话状态支持和本地存储。会话状态包括组件的几何特性,屏幕位置,标签的选种情况,列的宽度以及其它一些UI上的状态。你可以在程序退出之前保存会话,然后在程序再次开启的时候恢复会话状态。另外,框架还为你提供了简单的存储API让你把对象保存到你的本地系统上。

 

The Swing Application Framework is a work in progress. Expect some changes as the JSR 296 expert group revises both the specification and reference implementation before its final release. The expert group hopes to include the framework in a future release of the Java platform.

For More Information

 

Swing应用程序框架还处于开发中。各种各样的改动在它最终发布之前都可能会有。专家组希望Swing应用程序框架可以出现在未来的Jave平台上(JDK)。

 

Article demo project and source code

Online article Improve Application Performance With SwingWorker in Java SE 6

 

 

copyright © Sun Microsystems, Inc

flyinfeeling 翻译。

 

 

 

5 你可以同时使用属性注入和组件注入。

 

Conversions and Conveniences

The ResourceMap class provides automatic conversion for common resource types represented as text strings. This is useful because resource values in a .properties file are always simple text values. Resource converters, however, can convert the text representation of fonts, color, and icon resources into actual Font, Color, and Icon objects. Other ResourceConverter classes are available, but the following examples show how to represent these resource types. Of course, conversion of any .properties file value into a String type is always supported. Consult the framework documentation for information about other converters.

 

类型转换与一些方便技巧

ResourceMap提供了自动类型转换服务,它可以把常见的资源类型与Properties文件中的字符串自动转换。这非常有用。因为.properties文件中保存的总是文本值,但是资源转换器却可以把文本的字体,颜色,图标资源转换为实际的Font对象,Color对象,和Icon对象。当然不只这么几种,其它的ResourceConverter类也是有的。 而下面的例子就展示一下如何用文本来表示这些资源。当然,.properties文件值与String类型的转换总是被支持的。 关于资源转换器的更多信息你可以查阅框架文档。

 

 

Code Example 12 shows the resource file and the ConverterApp source code that demonstrates how to create and use resources with ResourceConverter objects.

 

示例12展示了资源文件和ConverterApp的代码,演示了如何用ResourceConverter对象来创建和使用资源。

 

示例12

# resources/ConverterApp.properties

Application.id = ConverterApp

Application.title = ResourceConverter Demo

 

msg = This app demos ResourceConverter.

font = Arial-BOLD-22

color = #BB0000

icon = next.png

 

**

public class ConverterApp extends SingleFrameApplication {

    protected void startup() {

        ApplicationContext ctx = getContext();

        ResourceMap resource = ctx.getResourceMap();

 

        String msg;

        Color color;

        Font font;

        Icon icon;

        JLabel label;

 

        // Use resource converters to convert text representations

        // of resources into Color and Font objects.

        msg = resource.getString("msg");

        color = resource.getColor("color");

        font = resource.getFont("font");

        icon = resource.getIcon("icon");

 

        label = new JLabel(msg);

        label.setOpaque(false);

        label.setForeground(color);

        label.setFont(font);

        label.setIcon(icon);

 

        show(label);

    }

    // ...

}

 

 

In Code Example 12, notice the format of the resource values. To use the resource converters, you must format the text representation of your resources in specific ways. Each resource type -- whether it be a font, icon, color, or other resource -- has a specific format.

 

在示例12中,请注意一下资源值的格式。为了正确使用资源转换器,你必须用特定的方式来格式化文本。每一种资源类型,不论是字体,图标,颜色,还是其它类型的资源,都有一种特定的格式与其对应。

 

For example, you should use the red (R), green (G), and blue (B) values -- commonly referred to as RGB values -- between 0 and 255 for colors. The accepted formats use RGB color values in any of these forms:

 

例如,你应该使用红色(R),绿色(G)和蓝色(B)来表示颜色,也就是大家常说的(RGB),每一种单色都有一个0~255的数值表示强度。用RGB值表示颜色时可接受的值是下面任意一种格式。

 

#RRGGBB for the hexadecimal representation of RGB values

#AARRGGBB for hexadecimal RGB values with an alpha channel (A)

R, G, B for decimal RGB values

R, G, B, A for decimal RGB values with an alpha channel

 

#RRGGBB  16进制代表的RGB

#AARRGGBB Alpha通道下的16进制RGB

R,G,B  10进制RGB

R,G,B,A  Alpha通道下的10进制RGB

 

Represent fonts using the form <name>-<style>-<size>. The name element is simply the font face name. Common names are Arial and Helvetica, for example. Styles can include values such as PLAIN, BOLD, or ITALIC. Finally, the size element is the font size. Examples of valid font resource values are Arial-PLAIN-12 or Helvetica-BOLD-22.

 

字体的表示格式是:<名字>-<样式>-<大小> <名字>代表的就是字体名,常见的如ArialHelvetica<样式>的值可以是正常,加粗斜体<大小>表示字体的大小。有效的字体表示如 Arial-PLAIN-12 Helvetica-BOLD-22

 

Icon resources are represented by their file names in your classpath. For example, if you want to create an icon for an image file in your resources package, you just use the image's file name.

 

图标资源是用类路径下的文件名表示的。例如,如果你想在resource包下面指定一个图标文件,那你只需要填写图标的文件名就可以了。

 

Figure 6 shows the result of running the ConverterApp code in Code Example 12. The color, font, and icon resources in the panel were converted by a ResourceConverter object.

 

6演示了示例12ConverterApp的运行结果。面板中的颜色,字体和图标资源都是用ResourceConverter转换的。

 

 

6 ResourceConverter把文本表示的资源转换为他们的最终资源对象。

 

The ResourceMap class provides support for parameterized strings. Code Example 10 uses a parameterized string in its resources but did not use ResourceMap to insert the parameter. The parameterized string is this:

 

ResourceMap提供对带参数的字符串的支持。示例10中使用了一个带参数的字符串但并不是用ResourceMap来插入参数。在示例中,带参数的字符串如下:

 

NameEntryPanel.greetingMsg = Hello, %s, this string was injected!

 

 

To use this same resource and insert the parameter as you retrieve the NameEntryPanel.greetingMsg text, you must provide an additional argument list to the getString method. In addition to the resource key, you should provide an argument list that contains the data to insert into the parameterized string. Code Example 13 shows code that could replace the original action handler code in Code Example 10. This new code uses the additional convenience functionality of getString to insert a name into the parameterized resource string.

 

要想使用这个带参数的资源,当你获取这个NameEntryPanel.greetingMsg时,你还需要给getString方法额外提供一个参数表。getString方法会把参数表的内容插入到带参数的字符串中。示例13替换了示例10中的事件处理。新的代码使用了getString方法来方便地把name插入到带参数的资源中。

 

示例13

private void btnGreetActionPerformed(java.awt.event.ActionEvent evt) {

    String personalMsg = resource.getString("NameEntryPanel.greetingMsg", txtName.getText());

    JOptionPane.showMessageDialog(this, personalMsg);

}

 

 

Action 动作

 

(下文中,大写的Action一般表现Action类,不翻译,小写的action代表一个操作或者动作,这里翻译为动作 .)

To handle UI events, you will implement an ActionListener object for a specific component. A more powerful form of an ActionListener is an AbstractAction object, which implements the javax.swing.Action interface. The Action interface allows you to define an event handler. In addition, it lets you associate an icon, text, mnemonic, and other visual elements with the event. Using the Action interface, often with an AbstractAction class, you can conveniently put all the relevant visual cues and the event-processing logic for a component in one place.

 

为了处理UI事件,你需要为指定的组件实现一个ActionListener对象。一个比ActionListener更好的形式是AbstractAction对象,它是一个抽象类,实现了javax.swing.Action接口。Action接口允许你指定事件处理器(event handler)。另外,它还可以让你将图标,文本,快捷方式以及其它视觉元素一起捆绑在一个事件上。使用Action接口,更一般的是使用AbstractAction类,你可以很方便地把一个组件的相关的视觉元素和事件处理逻辑放在同一个地方。

 

Another convenient benefit of using Action interfaces is that you can reuse the same action across multiple UI components. GUIs often provide multiple ways to accomplish a task. For example, to increase the font size of a piece of text, an application might provide both a menu and a button interface to perform the same task. The multiple different ways of accomplishing the same task should behave the same way, and you should enable or disable those actions simultaneously across all associated components -- something that Action interfaces will help you do.

 

另一个方便的地方是,使用Action接口,你可以让多个UI组件的使用同一个事件处理,达到重用的效果。GUI往往为同一个任务提供多个入口机制。 例如,为一块文本加大字体,即可以在菜单中提供此功能,也可以用一个界面上的button实现此功能。为了将从多种不同的方式实现同一功能,你需要同时开启或禁用绑定在同一个动作(action)上的多个组件,在这时,Action接口会起大作用。

 

Despite the convenience of using the Action interface, action objects have a few problems. First, visual properties should be localized. All too often, developers hardcode the action's visual elements, making localization difficult or impossible. Additionally, manually creating Action objects can be a chore, especially if you use all the visual components that are available.

The Swing Application Framework helps you manage actions in three ways:

 

虽然Action接口带来很多方便,但是Action对象却有一些问题。首先,可视的属性应该是本地化的。但是在绝大多数时候,开发人员往往直接把Action的视觉属性硬编码,这就使得本地化非常困难甚至不可能。另外,手动创建Action对象也非常繁琐,尤其是当你用到很多的可视组件的时候。

 

但是没关系,Swing应用程序框架可以为你轻松搞定,这里有三种方式用来它:

 

The framework provides the @Action annotation to mark and name methods that will be used by Action implementations.

An ActionManager class creates javax.swing.ActionMap instances for classes that contain @Action methods.

The framework supports localization of Action elements such as icons, text, and mnemonics.

 

框架提供了@Action元标记来标注和指定Action实现中用到的方法

ActionManager类为每个包含@Action标记方法的类创建一个ActionMap实例

框架提供对Action元素(图标,文本和快捷方式)的本地化支持。

 

The following discussion about Action objects uses a demo application called ActionApp and a panel named ResizeFontPanel. Figure 7 shows the demo UI, which provides the context for the Action class and annotation descriptions.

 

以下的关于Action对象的讨论用到了一个演示程序,名叫ActionApp 以及一个名叫ResizeFontPanel的面板。图7显示了这个演示的界面

 

 

 

7  多个组件使用同一个可用的Action对象。

 

The @Action Annotation

Define and name your Action objects using the @Action annotation. Mark Action event-handling methods with this annotation. The annotation has several optional elements, including a way to override the default name of the Action object. The default action name is the method name. Code Example 14 shows how to use this annotation with code that increases and decreases the font size for a text component.

 

@Action元标记

 

@Action元标记来指定你的Action对象。用@Action标记指定Action的事件处理方法。这个标记有好几个可选的参数,包括手动指定对应的Action名。在默认情况下,Action名就是方法名。示例14展示了如果在代码中使用@Action标记来加大和减小字体

 

示例14

public class ResizeFontPanel extends javax.swing.JPanel {

    ...

    @Action

    public void makeLarger() {

        Font f = txtArea.getFont();

        int size = f.getSize();

        if (size < MAX_FONT_SIZE) {

            size++;

            f = new Font(f.getFontName(), f.getStyle(), size);

            txtArea.setFont(f);

        }

    }

 

    @Action

    public void makeSmaller() {

        Font f = txtArea.getFont();

        int size = f.getSize();

        if (size > MIN_FONT_SIZE) {

            size--;

            f = new Font(f.getFontName(), f.getStyle(), size);

            txtArea.setFont(f);

        }

 

    }

    ...

}

 

 

Code Example 14 defines two Action methods with default names: makeLarger and makeSmaller. When invoked, the actions reset the font for a text area, which is named txtArea.

 

示例14定义了两个Action方法,一个叫makeLager,一个叫makeSmaller.当被调用的时候,这两个方法就修改TextArea中文字的字体大小.

 

The ActionMap class associates actions with UI components by using the component's setAction method. Use the Action object's name. In Code Examples 14 and 15, the names are makeLarger and makeSmaller. The ResizeFontPanel object has a button for both actions. Code Example 15 shows how to bind the Action objects with the components.

 

通过UI组件的setAction方法为其添入Action名名,ActionMap就可以将动作和UI组件连接在一起. 在示例1415,这两个名字分别是makeLarger makeSmaller. ResizeFontPanel对象有两个button, 示例15的代码将14中的两个动作与两个button连接在一起.

 

示例15

// ctx is the ApplicationContext instance.

ResourceMap resource = ctxt.getResourceMap(ResizeFontPanel.class);

resource.injectComponents(this);

ActionMap map = ctxt.getActionMap(this);

 

btnMakeLarger.setAction(map.get("makeLarger"));

btnMakeSmaller.setAction(map.get("makeSmaller"));

 

Notice that the ApplicationContext provides the ActionMap for this class. As a side note, the code also uses the context to inject component resources. An ActionMap instance stores all the Actions that are associated with this class. In this case, only two Action objects exist: makeLarger and makeSmaller. Retrieve the Action objects using the ActionMap instance's get method, providing the Action instance name. Bind the UI component and Action using the component's setAction method.

 

请注意,ActionContext为这个类提供了ActionMap. 顺便说一声,这个代码中也用到了组件注入. 一个ActionMap实例存储着所有与这个类相关的动作(actions. 在这个示例中,只有两个动作,一个是makeLarger 一个是makeSmaller . 使用ActionMap实例的get方法可以获得Action对象, 用的时候只需要填上动作的名字就可以了. 使用UI组件的setAction方法就可以将动作和UI组件绑定在一起.

 

Action Resources

Code Example 15 shows how to get the ActionMap for the ResizeFontPanel. It also shows how to call setAction on specific components using the Action objects defined by the @Action annotation. But where did the action's icon and text come from in Figure 7? That's also shown in Code Example 15. Take a closer look at the line that retrieves the ActionMap instance for the ResizeFontPanel object:

 

Action资源

 

示例15展示了如何在ResizeFontPanel中获得ActionMap. 示例也展示了如何在指定的组件上使用setAction方法连接用@Action标记的方法. 但是图7 中动作的图标和文字是从哪来的~? 这在示例15中也出现这个问题. 让我们仔细看看ResizeFontPanel中是如何获得ActionMap

 

ActionMap map = ctxt.getActionMap(this);

 

 

This single line of code does a lot of work behind the scenes. First, as described earlier, it creates Action instances for each method marked with an @Action annotation in the target object. Second, it retrieves resources for the Action from the target's ResourceMap. The ResourceMap for this ResizeFontPanel class includes properties from the resources/ResizeFontPanel.properties file. This file defines a few resource key-value pairs and includes the data as shown in Code Example 16. The makeLarger and makeSmaller actions have text and icons defined by this resource file. The getActionMap method includes these resources when it creates the ActionMap for the ResourceFontPanel object.

 

仅仅这么一行代码 ,其实在它背后有非常多的工作. 首先,它为每一个用@Action标记的方法创建一个Action实例. 然后从ResourceMap中为动作获得资源.ResizeFontPanel类的ResourceMap包含着resources/ResizeFontPanel.properties文件中的所有Properties. 文件中定义了一些键---值对来表示在示例16中用到的数据。makerLargermakeSmaller这两个动作的文本和图标都在这个文件中指定了。getActionMap方法在为ResizeFontPanel创建ActionMap的时候就包含了这些资源。

 

# resources/ResizeFontPanel.properties

makeLarger.Action.text = Increase font size

makeLarger.Action.icon = increase.png

 

makeSmaller.Action.text = Decrease font size

makeSmaller.Action.icon = decrease.png

 

Because an Action object's resources exist in a ResourceMap, you can localize the resources as appropriate for a specific operating system (OS) platform or locale. Follow the usual naming standards for creating alternate ResourceBundle files. You can find out more about localization and ResourceBundle naming by consulting the ResourceBundle documentation.

 

因为Action对象的资源也是在ResourceMap中保存的,所以你也可以对这些资源进行本地化处理,将其处理为面向特定OS或地区的资源。当然在使用时,你也可以在遵循命名规范的情况下创建自己的ResourceBundle文件。如果你对本地化以及ResourceBundle命名还有问题,可以参看ResourceBundle文档。

 

Tasks

 

Always use a background thread for input/output (I/O) bound or computationally intensive tasks. Long-running tasks can block the event dispatch thread (EDT). The new Swing Application Framework provides support for starting, stopping, and monitoring the progress of background tasks. Although the SwingWorker class in the Java SE 6 platform has most of what's needed, the framework provides a Task class to support more functionality.

 

Tasks任务

 

一定要使用后台线程来处理I/O或者运算密集的任务。长程(Long-run)运算的任务会阻塞EDT,使界面失去响应。新的Swing应用程序框架就提供了对后台任务的开始,停止,进度指示的功能。虽然Java SE6中的SwingWorker已经提供了大部分所需,但是框架还是提供了Task类来支持更强大的功能。

 

The Task class extends a SwingWorker implementation, which is similar to the SwingWorker available in Java SE 6. A TaskService class helps you execute tasks, and a TaskMonitor helps you monitor their progress.

 

Task类实际上继承了SwingWorker类,它当然与JavaSE6SwingWorker很相似。TaskService类帮助你执行task任务。其外,还有一个TaskMonitor类帮助你查看任务的进度。

 

The easiest way to use the Task class is to extend it, override one or more methods, and use the Task with an Action handler. You can can monitor the task and get intermediate result information as the task runs. This article will describe only the basic usage associated with event handling.

 

使用Task类最简单的办法就是去继承它,覆盖一个或更多个方法,然后在一个动作的事件处理方法中使用Task。你可以监视任务,在它运行的时候就得到它当前的结果及状态。这篇文章会向你讲述Task与事件处理的基本用法。

 

Code Example 17 creates a Task class. The NetworkTimeRetriever class overrides the doInBackground method, which is the method that performs your long-running task. The Task class defines several methods that communicate the success -- or failure -- of the completed task. One of those methods is the succeeded method. The Task superclass calls succeeded when the doInBackground method finishes its work. The succeeded method runs on the EDT, so you can use it to update GUI components when your task completes successfully. The NetWorkTimeRetriever is an inner class, so it has access to fields in the NetworkTimeApp class. The important field that it needs is the txtShowTime variable, which is a JTextField instance. The Task completes its duties when it finally displays the current time provided by the National Institute of Standards and Technology (NIST) time servers.

 

示例17创建了一个Task类。NetworkTimeRetriever类覆盖了doinBackGround方法,这是一个用来执行“长程”任务的方法。Task类还定义了其它一些方法,它们与任务的“成功”,“失败”,“完成”状态相关连。其中有一个“成功”(succeeded)方法,Task类会在doinbackground方法完成它的工作之后呼叫succeeded方法。Succeeded方法是在EDT基础上运行的,所以你可以在任务完成后,用它来刷新GUI组件。NetworkTimeRetriever是一个内部类,所以它可以访问到NetworkTimeAPp类的属性。这里有一个重要的属性是txtShowTIme变量,它是一个JTextField实例。NetworkTimeRetriever任务会在它从NIST时间服务器获得时候并显示出来之后完全它的使命。

 

示例17

public class NetworkTimeApp extends SingleFrameApplication {

    JPanel timePanel;

    JButton btnShowTime;

    JTextField txtShowTime;

 

    @Override

    protected void startup() {

        // Create components and so on.

        // ...

 

        // Retrieve and set Actions.

        ActionMap map = getContext().getActionMap(this);

        javax.swing.Action action = map.get("retrieveTime");

        btnShowTime.setAction(action);

        timePanel.add(btnShowTime);

        timePanel.add(txtShowTime);

        show(timePanel);

    }

 

    @Action

    public Task retrieveTime() {

        Task task = new NetworkTimeRetriever(this);

        return task;

    }

    // ...

 

    class NetworkTimeRetriever extends Task<Date, Void> {

 

        public NetworkTimeRetriever(Application app) {

            super(app);

        }

 

        @Override

        protected Date doInBackground() throws Exception {

            URL nistServer = new URL("http://time.nist.gov:13");

            InputStream is = nistServer.openStream();

            int ch = is.read();

            StringBuffer dateInput = new StringBuffer();;

            while(ch != -1) {

                dateInput.append((char)ch);

                ch = is.read();

            }

            String strDate = dateInput.substring(7, 24);

            DateFormat dateFormat = DateFormat.getDateTimeInstance();

            SimpleDateFormat sdf = (SimpleDateFormat)dateFormat;

            sdf.applyPattern("yy-MM-dd HH:mm:ss");

            sdf.setTimeZone(TimeZone.getTimeZone("GMT-00:00"));

            Date now = dateFormat.parse(strDate);

            return now;

        }

 

        @Override

        protected void succeeded(Date time) {

            txtShowTime.setText(time.toString());

        }

    }

}

 

In Code Example 17, a button prompts the user to retrieve the time from a network server. The button event handler has the @Action annotation, marking it as an Action handler in its class. The action has the name retrieveTime, and the application's ActionMap stores it. After you retrieve the Action and set the button's Action property, the button will contain the resources associated with it too. For example, the btnShowTime component has an icon and some text that are referenced in a ResourceBundle for the application. When you click on the button, its action handler method retrievetime will execute.

 

在示例17,这个button帮助用户从服务器获取时间。Button的事件处理用@Action标记,这就使它成为一个动作处理方法”(an Action Handler)。这动作名叫“retriveTime”,它存储在框架的ActionMap中。当你获得了这个动作并且正确设置之后,这个button也就有了retriveTime动作的所有资源。例如,btnShowTime组件包含一个图标和一些文本,它们存储在应用的ResourceBundle中。当你点击button,它的动作处理方法retrivetime就会被执行。

 

Notice that the retrieveTime method returns a Task. The method creates a NetworkTimeRetriever task and returns that object. The framework automatically runs it as a background thread. When the task completes successfully, it updates the UI with the network time. The combination of tasks, actions, and resource injection is a powerful and simple way to handle component event handling.

 

请注意:retriveTime方法返回一个任务。这个方法创建了一个NetworkTimeRetriever任务并将其返回。框架会自动把它作为一个后台线程来运行。当任务成功完成,它就会用时间服务器得到的信息更新UI上的txtShowTime组件。任务,动作以及资源注入三者的组合是一个强有力却有非常简单的工具来处理事件。

 

Figure 8 shows the running NetworkTimeApp application. This application is similar to the previous ShowTimeApp application. The primary difference is that the NetworkTimeApp class retrieves its time from a network time server. As a result, the "Retrieve time!" button must execute a long-running task as a background thread.

 

8展示了运行时的NetworkTimeApp程序。这个应用和之前的ShowTimeAPP非常相近。它们之间主要的区别是NetworkTimeApp是从一个时间服务器上获取时间的。因此,“Retrieve time”button必然也需要一个长程的任务来在后台执行。

 

 

8. 使用一个Task实例在后台执行一些长程任务。

 

 

Session State

会话状态

 

The Swing Application Framework provides a way to save session state when your application exits and restore the state when you restart. Session state is the graphical window configuration of your application. This state includes window size, internal frame locations, selected tabs, column widths, and other graphical properties. Saving the GUI session state allows you to restart the application and return to the same window or form at a later time.

 

Swing应用程序框架提供了一种方式允许你在程序退出时保存会话的状态,在下次启动时恢复到之前的状态。会话状态这个名词的意思就是你的程序中图形窗口的设置信息。主要包含窗口大小,内部窗口地址,选种的标签,列宽度,以及其它一些图形信息。保存会话状态可以让你重启程序并且回到上一次时窗口的位置与状态。

 

The SingleFrameApplication class implements session storage for you. When you restart any application subclass of SingleFrameApplication, all GUI geometry should be restored exactly as it was when you exited the application.

 

SingleFrameApplication类为你实现了会话保存。当你重启任何SingleFrameApplication的子类应用时,所有的图形界面的几何信息都会上和一回退出时一模一样。

 

If you use the Application class and want to store the session state, you must save and restore the state yourself. Fortunately, this is not difficult. Like most functionality in the framework, session storage management starts with the ApplicationContext class. Use the ApplicationContext and SessionStorage classes in the application startup method to restore state. Use these classes in the shutdown method to save state. Code Example 18 shows how to save and restore session state using the SessionStorage class:

 

如果你在使用Application类但也想保存会话状态,那你就需要自己动手来保存和恢复会话。幸运的是,这项工作并不复杂。就像框架所提供的其它功能,会话状态管理是从ApplicationContext开始的。在Applicationstartup方法中使用ApplicationContextSssionStorage类就可以恢复状态。在Application shutdown方法中保存状态。示例18展示了如何用SessionStorage保存和恢复会话状态

 

示例18

// ...

String sessionFile = "sessionState.xml";

ApplicationContext ctx = getContext();

JFrame mainFrame = getMainFrame();

 

@Override protected void startup() {

  //...

  /* Restore the session state for the main frame's component tree.

   */

  try {

    ctxt.getSessionStorage().restore(mainFrame, sessionFile);

  }

  catch (IOException e) {

    logger.log(Level.WARNING, "couldn't restore session", e);

  }

  // ...

}

 

@Override protected void shutdown() {

  /* Save the session state for the main frame's component tree.

   */

  try {

    ctxt.getSessionStorage().save(mainFrame, sessionFile);

  }

  catch (IOException e) {

    logger.log(Level.WARNING, "couldn't save session", e);

  }

  // ...

 

 

Note that applications will not usually need to create their own SessionStorage instance. Instead, you should use the ApplicationContext object's shared storage:

 

请注意:程序一般不需要自己去创建SessionStorage实例,而是使用ApplicationContext提供的保存。

SessionStorage ss = ctxt.getSessionStorage();

 

 

Local Storage

 

Session storage depends on the framework class LocalStorage. The LocalStorage class helps the SessionStorage class do its work, but it can also help you to store simple XML-encoded representations of any JavaBean component. The LocalStorage class uses the XMLEncoder and XMLDecoder classes to encode and decode XML files using your application's objects.

 

本地存储

 

会话存储依赖于框架的另一个类LocalStorageLocalStorage类帮助SessionStorage完成它的工作,并且它也可以帮助你用XML表示形式来持久化任意的JavaBean组件。LocalStorage使用XMLEncoderXMLDecored类编码和解码XML文件

 

Again, the ApplicationContext provides access to a shared LocalStorage instance. You should retrieve the LocalStorage instance like this:

 

再次提示,ApplicationContext提供了一个共用的LocalStorage实例,获取LocalStorage实例的方法如下:

 

LocalStorage ls = ctxt.getLocalStorage();

 

 

Now you can use the object's save and load methods to encode and decode objects to your local storage. The LocalStorage class uses your home directory as a base subdirectory for determining the default location of storage files.

 

现在,你就可以用这个ls实例的saveload方法将一个对象编码或解码成XML保存到你的本地存储中。LocalStorage类使用所在系统的home目录作为存储文件的默认地址。

 

Code Example 19 shows how to use the LocalStorage class to store a list of phone numbers named phonelist.xml. In this example, a JList component model is both loaded and saved with an application context's shared LocalStorage instance. The variable file contains the file name that will contain the list's contents. You can see the entire program listing for this and all other code examples in the demo source code, which is provided as a downloadable link at the end of this article.

 

示例19展示了如果使用LocalStorage类把一个电话号码列表保存到phoneList.xml中。在示例中,一个JList组件模型会被LocalStorage实例读取和加载。phoneList.xml文件会保存jList中的内容。你可以在完整的程序代码中找到这个和对其它格式保存的例子,程序的代码在文章结尾有下载。

 

示例19

@Action

public void loadMap() throws IOException {

    Object map = ctxt.getLocalStorage().load(file);

    listModel.setMap((LinkedHashMap<String, String>)map);

    showFileMessage("loadedFile", file);

}

 

@Action

public void saveMap() throws IOException {

    LinkedHashMap<String, String> map = listModel.getMap();

    ctxt.getLocalStorage().save(map, file);

    showFileMessage("savedFile", file);

}

 

 

Figure 9 shows the My Little Black Book application. Notice the tool tip and labels. All these resource items are in an application resource file. All the buttons and text fields have associated actions that provide labels, tips, and event handling.

 

9展示了我的小电话本程序。请注意看那工具提示(tool tip)和那些标签。所有这个资源项都是在资源文件中保存的。所有的buttontextField都有动作绑定,它提供了标签,提示以及事件处理。

 

 

9  程序使用LocalStorage保存数据。

 

 

The text field at the bottom of the application window shows the file name of the phone list. Files created by the LocalStorage class always exist in platform-specific locations on your host system. The files are, however, always stored under the application user's home directory.

 

在窗口底部的TextField显示数据的文件的文件名。用LocalStorage创建的文件总是和你所使用的系统平台相关的。但是默认情况下文件的地址却总是在保存在用户home目录下。

 

Summary

 

总结

 

The Swing Application Framework (JSR 296) provides a basic architecture and a set of commonly used services for Swing applications. Most applications must implement and manage lifecycle events, UI component event handling, threading, localizable resources, and simple persistence. The framework provides services for managing all these common needs, allowing you to concentrate on your application's unique functionality.

 

Swing应用程序框架(JSR 296)Swing应用提供了一个基础的结构和一组常用的服务。大部分程序都需要实现和管理生命周期,UI组件的事件处理,线程,资源本地化和简单的持久化。框架为这些常见需求提供了服务,这使你更加关注于你的程序的业务逻辑,而不是Swing的开发上。

 

Framework architecture includes Application and ApplicationContext classes. The ApplicationContext instance will provide help for session and local storage, task management, resource management, and most other services that the framework provides.

 

框架的结构主要包括ApplicationApplicationContext这两个类。ApplicationContext实例提供会话和数据的本地存储,任务管理,资源管理,和框架所提供的大部分其它服务。

 

Your framework application has a well-defined life cycle. Each lifecycle stage has a corresponding method that your application can override to provide its unique functionality.

 

应用程序应该有一个良好定义的生命周期。在用Swing应用程序框架时,每一个周期都有一个对应的方法,你可以覆盖它为你的程序提供特有的功能。

 

Framework applications have support for application- and class-level resources. Using the ResourceManager and ResourceMap classes, you can automatically inject those resources into your application, making them easy to localize.

 

框架支持程序级和类级两种资源。使用ResourceManagerResourceMap类,你可以将这些资源自动注入到你的程序,这使本地化大为简单。

 

The @Action annotation helps you create UI component event handlers. Using this annotation and ResourceMap objects, you can combine an action's functionality and visual elements in a single place, making the resulting Action object reusable and localizable.

 

@Action标记帮助你创建UI组件的事件处理。同时使用@ActionResourceMap对象,你可以把动作的功能和视觉元素在同一地方绑定起来,这可以让Action对象可重用化和本地化。

 

Some event handlers should run as background threads. Use the Task class to define background threads. The framework makes it easy to combine Action and Task instances to make your GUI run smoothly and reliably with long-running event handlers.

 

有一些事件处理应该放在后台线程运行。使用Task类来定义后台线程。框架可以轻松将ActionTask结合在一起,使你的UI运行流畅并且在长程事件处理中保持稳定。

 

Finally, the framework provides support for session state and local storage. Session state includes component geometry, screen location, selected tabs, column widths, and other UI state. You can save session state before the application exits, and you can restore the same state when the application starts again. Additionally, the framework gives you a simple storage API that lets you store local objects on your host platform.

 

最后,框架还提供了会话状态支持和本地存储。会话状态包括组件的几何特性,屏幕位置,标签的选种情况,列的宽度以及其它一些UI上的状态。你可以在程序退出之前保存会话,然后在程序再次开启的时候恢复会话状态。另外,框架还为你提供了简单的存储API让你把对象保存到你的本地系统上。

 

The Swing Application Framework is a work in progress. Expect some changes as the JSR 296 expert group revises both the specification and reference implementation before its final release. The expert group hopes to include the framework in a future release of the Java platform.

For More Information

 

Swing应用程序框架还处于开发中。各种各样的改动在它最终发布之前都可能会有。专家组希望Swing应用程序框架可以出现在未来的Jave平台上(JDK)。

 

Article demo project and source code

Online article Improve Application Performance With SwingWorker in Java SE 6

 

 

copyright © Sun Microsystems, Inc

flyinfeeling 翻译。

 

 

 

原创粉丝点击