This document presents a technical overview of the Swing component architecture. In particular, it covers the following areas in detail: Design GoalsThe overall goal for the Swing project was: To build a set of extensible GUI components to enable developers to more rapidly develop powerful Java front ends for commercial applications. To this end, the Swing team established a set of design goals early in the project that drove the resulting architecture. These guidelines mandated that Swing would:
Roots in MVCSwing architecture is rooted in the model-view-controller (MVC) design that dates back to SmallTalk. MVC architecture calls for a visual application to be broken up into three separate parts:
Early on, MVC was a logical choice for Swing because it provided a basis for meeting the first three of our design goals within the bounds of the latter two. The first Swing prototype followed a traditional MVC separation in which each component had a separate model object and delegated its look-and-feel implementation to separate view and controller objects. The delegateWe quickly discovered that this split didn't work well in practical terms because the view and controller parts of a component required a tight coupling (for example, it was very difficult to write a generic controller that didn't know specifics about the view). So we collapsed these two entities into a single UI (user-interface) object, as shown in this diagram: (The UI delegate object shown in this picture is sometimes called a delegate object, or UI delegate. The UI delegate used in Swing is described in more detail in the Pluggable look-and-feel section of this article, under the subheading "The UI delegate".) As the diagram illustrates, Swing architecture is loosely based -- but not strictly based -- on the traditional MVC design. In the world of Swing, this new quasi-MVC design is sometimes referred to a separable model architecture.
Swing's separable model design treats the model part of a component as a separate element, just as the MVC design does. But Swing collapses the view and controller parts of each component into a single UI (user-interface) object. To MVC or not to MVC?One noteworthy point is that as an application developer, you should think of a component's view/controller responsibilities as being handled by the generic component class (such as. For example, the code that implements double-buffered painting is in Swing's
So Swing does have a strong MVC lineage. But it's also important to reiterate that our MVC architecture serves two distinct purposes:
Although these two concepts are linked by the MVC design, they may be treated somewhat orthogonally from the developer's perspective. The remainder of this document will cover each of these mechanisms in greater detail.
Separable model architectureIt is generally considered good practice to center the architecture of an application around its data rather than around its user interface. To support this paradigm, Swing defines a separate model interface for each component that has a logical data or value abstraction. This separation provides programs with the option of plugging in their own model implementations for Swing components. The following table shows the component-to-model mapping for Swing.
GUI-state vs. application-data modelsThe models provided by Swing fall into two general categories: GUI-state models and application-data models. GUI-state modelsGUI state models are interfaces that define the visual status of a GUI control, such as whether a button is pressed or armed, or which items are selected in a list. GUI-state models typically are relevant only in the context of a graphical user interface (GUI). While it is often useful to develop programs using GUI-state model separation -- particularly if multiple GUI controls are linked to a common state (such as in a shared whiteboard program), or if manipulating one control automatically changes the value of another -- the use of GUI-state models is not required by Swing. It is possible to manipulate the state of a GUI control through top-level methods on the component, without any direct interaction with the model at all. In the preceding table, GUI-state models in Swing are highlighted in blue. Application-data modelsAn application-data model is an interface that represents some quantifiable data that has meaning primarily in the context of the application, such as the value of a cell in a table or the items displayed in a list. These data models provide a very powerful programming paradigm for Swing programs that need a clean separation between their application data/logic and their GUI. For truly data-centric Swing components, such as JTree and JTable, interaction with the data model is strongly recommended. Application-data models are highlighted in red in the table presented at the beginning of this section. Of course with some components, the model categorization falls somewhere in between GUI state models and application-data models, depending on the context in which the model is used. This is the case with the Swing's separable model API makes no specific distinctions between GUI state models and application-data models; however, we have clarified this difference here to give developers a better understanding of when and why they might wish to program with the separable models.
Shared model definitionsReferring again to the table at the beginning of this section, notice that model definitions are shared across components in cases where the data abstraction for each component is similar enough to support a single interface without over-genericizing that interface. Common models enable automatic connectability between component types. For example, because both
The separable-model APISwing components that define models support a JavaBeans bound property for the model. For example,
All Swing components have one thing in common: If you don't set your own model, a default is created and installed internally in the component. The naming convention for these default model classes is to prepend the interface name with "Default." For
If a program subsequently calls
For more complex models (such as those for For example,
Model change notificationModels must be able to notify any interested parties (such as views) when their data or value changes. Swing models use the JavaBeans Event model for the implementation of this notification. There are two approaches for this notification used in Swing:
Lightweight notificationThe following models in Swing use the lightweight notification, which is based on the
The ChangeListener interface has a single generic method:
The only state in a ChangeEvent is the event "source." Because the source is always the same across notifications, a single instance can be used for all notifications from a particular model. Models that use this mechanism support the following methods to add and remove ChangeListeners:
Therefore, to be notified when the value of a
To provide convenience for programs that don't wish to deal with separate model objects, some Swing component classes also provide the ability to register ChangeListeners directly on the component (so the component can listen for changes on the model internally and then propagates those events to any listeners registered directly on the component). The only difference between these notifications is that for the model case, the event source is the model instance, while for the component case, the source is the component. So we could simplify the preceding example to:
Stateful notificationModels that support stateful notification provide event Listener interfaces and event objects specific to their purpose. The following table shows the breakdown for those models:
The usage of these APIs is similar to the lightweight notification, except that the listener can query the event object directly to find out what has changed. For example, the following code dynamically tracks the selected item in a JList:
Automatic View UpdatesA model does not have any intrinsic knowledge of the view that represents it. (This requirement is critical to enable multiple views on the same model). Instead, a model has only a list of listeners interested in knowing when its state has changed. A Swing component takes responsibility for hooking up the appropriate model listener so that it can appropriately repaint itself as the model changes (if you find that a component is not updating automatically when the model changes, it is a bug!). This is true whether a default internal model is used or whether a program installs its own model implementation.
Ignoring models completelyAs mentioned previously, most components provide the model-defined API directly in the component class so that the component can be manipulated without interacting with the model at all. This is considered perfectly acceptable programming practice (especially for the GUI-state models). For example, following is
And so programs can simply do the following:
Separable model summarySo while it's useful to understand how Swing's model design works, it isn't necessary to use the model API for all aspects of Swing programming. You should carefully consider your application's individual needs and determine where the model API will enhance your code without introducing unnecessary complexity. In particular, we recommend the usage of the Application-Data category of models for Swing (models for Pluggable look-and-feel architectureSwing's pluggable look-and-feel architecture allows us to provide a single component API without dictating a particular look-and-feel. The Swing toolkit provides a default set of look-and-feels; however, the API is "open" -- a design that additionally allows developers to create new look-and-feel implementations by either extending an existing look-and-feel or creating one from scratch. Although the pluggable look-and-feel API is extensible, it was intentionally designed at a level below the basic component API in such a way that a developer does not need to understand its intricate details to build Swing GUIs. (But if you want to know, read on . . .) While we don't expect (or advise) the majority of developers to create new look-and-feel implementations, we realize PL&F is a very powerful feature for a subset of applications that want to create a unique identity. As it turns out, PL&F is also ideally suited for use in building GUIs that are accessible to users with disabilities, such as visually impaired users or users who cannot operate a mouse. In a nutshell, pluggable look-and-feel design simply means that the portion of a component's implementation that deals with the presentation (the look) and event-handling (the feel) is delegated to a separate UI object supplied by the currently installed look-and-feel, which can be changed at runtime.
The pluggable look-and-feel APIThe pluggable look-and-feel API includes:
The component hooksEach Swing component that has look-and-feel-specific behavior defines an abstract class in the The UI delegate is created in the component's constructor and is accessible as a JavaBeans bound property on the component. For example,
This process of creating a UI delegate and setting it as the "UI" property for a component is essentially the "installation" of a component's look-and-feel. Each component also provides a method which creates and sets a UI delegate for the "default" look-and-feel (this method is used by the constructor when doing the installation):
A look-and-feel implementation provides concrete subclasses for each abstract plaf UI class. For example, the Windows look-and-feel defines
Consequently, the hash table in the Windows look-and-feel will provide an entry that maps "ScrollBarUI" to "com.sun.java.swing.plaf.windows.WindowsScrollBarUI" Look-and-feel managementSwing defines an abstract LookAndFeel class that represents all the information central to a look-and-feel implementation, such as its name, its description, whether it's a native look-and-feel -- and in particular, a hash table (known as the "Defaults Table") for storing default values for various look-and-feel attributes, such as colors and fonts. Each look-and-feel implementation defines a subclass of The UIManager is the API through which components and programs access look-and-feel information (they should rarely, if ever, talk directly to a The 'default' look and feelThe
As a default look-and-feel, Swing initializes the cross-platform Java look and feel (formerly known as "Metal"). However, if a Swing program wants to set the default Look-and-Feel explicitly, it can do that using the
Sometimes an application may not want to specify a particular look-and-feel, but instead wants to configure a look-and-feel in such a way that it dynamically matches whatever platform it happens to be running on (for instance, the. Windows look-and-feel if it is running on Windows NT, or CDE/Motif if it running on Solaris). Or, perhaps, an application might want to lock down the look-and-feel to the cross-platform Java look and feel. The public static String getSystemLookAndFeelClassName() public static String getCrossPlatformLookAndFeelClassName() So, to ensure that a program always runs in the platform's system look-and-feel, the code might look like this:
Dynamically Changing the Default Look-and-FeelWhen a Swing application programmatically sets the look-and-feel (as described above), the ideal place to do so is before any Swing components are instantiated. This is because the Remember that components initialize their UI delegate at construct time, therefore, if the current default changes after they are constructed, they will not automatically update their UIs accordingly. It is up to the program to implement this dynamic switching by traversing the containment hierarchy and updating the components individually. (NOTE: Swing provides the The look-and-feel of a component can be updated at any time to match the current default by invoking its
For example, the implementation of
And so if a program needs to change the look-and-feel of a GUI hierarchy after it was instantiated, the code might look like the following:
Managing look-and-feel dataThe
These methods can be used to programmatically determine which look-and-feel implementations are available, which is useful when building user interfaces which allow the end-user to dynamically select a look-and-feel. The look-and-feel packagesThe UI delegate classes provided in Each look-and-feel implementation provides concrete subclasses of these abstract plaf classes. All such classes defined by a particular look-and-feel implementation are contained in a separate package under the
In implementing the various Swing look-and-feels, we soon discovered that there was a lot of commonality among them. We factored out this common code into a base look-and-feel implementation (called "basic") which extends the plaf abstract classes and from which the specific look-and-feel implementations (motif, windows, and so on.) extend. The basic look-and-feel package supports building a desktop-level look-and-feel, such as Windows or CDE/Motif. The basic look-and-feel package is just one example of how to build a pluggable look-and-feel; the architecture is flexible enough to accommodate other approaches as well. The remainder of this document will show how a look-and-feel package works at the generic level, leaving the details on the basic package for a future document. WARNING: All APIs defined below the The LookAndFeel SubclassThe LookAndFeel class defines the following abstract methods, which all subclasses must implement:
The The The A
The The Defaults TableFinally, the
The Defaults Table is represented by the UIDefaults class, a direct extension of
When the default look-and-feel is set with
The UI classes access their default information in the same way. For example, our example
The defaults are organized this way to allow developers to override them. More detail about Swing's Defaults mechanism will be published in a future article. Distinguishing between UI-set and app-set propertiesSwing allows applications to set property values (such as color and font) individually on components. So it's critical to make sure that these values don't get clobbered when a look-and-feel sets up its "default" properties for the component. This is not an issue the first time a UI delegate is installed on a component (at construct time) because all properties will be uninitialized and legally settable by the look-and-feel. The problem occurs when the application sets individual properties after component construction and then subsequently sets a new look-and-feel (that is, dynamic look-and-feel switching). This means that the look-and-feel must be able to distinguish between property values set by the application, and those set by a look-and-feel. This issue is handled by marking all values set by the look-and-feel with the
The UI delegateThe superclass of all UI Delegate classes is Many of the UI Delegate subclasses also provide additional methods specific to their own required interaction with the component; however, this document focuses primarily on the generic mechanism implemented by UI installation and deinstallationFirst off, the ComponentUI class defines these methods methods for UI delegate installation and uninstallation:
Looking at the implementation of
The UI delegate's
For example, the
Conventions for initializing component propertiesSwing defines a number of conventions for initializing component properties at install-time, including the following:
To facilitate convention No 1, the Convention No. 2 is implemented by always checking for either a The
For example, an
Defining geometryIn the AWT (and thus in Swing) a container's LayoutManager will layout the child components according to its defined algorithm; this is known as "validation" of a containment hierarchy. Typically LayoutManagers will query the child components' preferredSize property (and sometimes minimumSize and/or maximumSize as well, depending on the algorithm) in order to determine precisely how to position and size those children. Obviously, these geometry properties are something that a look-and-feel usually needs to define for a given component, so
Even though the bounding box for all components is a Rectangle, it's possible
So a UI delegate could provide non-rectangular "feel" by defining a particular implementation of PaintingFinally, the UI delegate must paint the component appropriately, hence
And once again,
Similarly to the way in which things are done in AWT, the UI delegate's Stateless vs. stateful delegatesAll the methods on This approach works well for many of the simpler GUI components. But for more complex components, we found it not to be a "win" because the inefficiency created by constant state recalculations was worse than creating extra objects (especially since the number of complex GUI components created in a given program tends to be small). The
It's the implementation of this method that determines whether the delegate is stateless or stateful. That's because the The Swing look-and-feel implementations use both types of delegates. For example, Swing's
While Swing's
Pluggable Look-and-Feel summaryThe pluggable look-and-feel feature of Swing is both powerful and complex (which you understand if you've gotten this far!). It is designed to be programmed by a small subset of developers who have a particular need to develop a new look-and-feel implementation. In general, application developers only need to understand the capabilities of this mechanism in order to decide how they wish to support look-and-feels (such as whether to lock-down the program to a single look-and-feel or support look-and-feel configuration by the user). Swing's If you're one of those developers who needs (or wants) to develop a custom look-and-feel, it's critical to understand these underpinnings before you write a single line of code. We're working on providing better documentation to help with this process -- starting with this document, and continuing with others that will follow soon. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.
|