A Swing Architecture Overview
|
The Inside Story on JFC Component Design
By Amy Fowler
Most Swing developers know by now that Swing components have a separable model-and-view design. And many Swing users have run across articles saying that Swing is based on something called a "modified MVC (model-view-controller) architecture."
But accurate explanations of how Swing components are designed, and how their parts all fit together, have been hard to come by -- until now.
The silence ends with the publication of this article, a major white paper on Swing component design. It provides a comprehensive technical overview of Swing's modified MVC structure and demystifies many other facets of Swing component architecture as well.
![]() |
This document presents a technical overview of the Swing component architecture. In particular, it covers the following areas in detail:
![]() |
Design Goals
The 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:
- Be implemented entirely in Java to promote cross-platform consistency and easier maintenance.
- Provide a single API capable of supporting multiple look-and-feels so that developers and end-users would not be locked into a single look-and-feel.
- Enable the power of model-driven programming without requiring it in the highest-level API.
- Adhere to JavaBeans design principles to ensure that components behave well in IDEs and builder tools.
- Provide compatibility with AWT APIs where there is overlapping, to leverage the AWT knowledge base and ease porting.
![]() |
Roots in MVC
Swing 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:
- A model that represents the data for the application.
- The view that is the visual representation of that data.
- A controller that takes user input on the view and translates that to changes in the model.
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 delegate
We 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.JButton
, JTree
, and so on). The component class then delegates the look-and-feel-specific aspects of those responsibilities to the UI object that is provided by the currently installed look-and-feel.
For example, the code that implements double-buffered painting is in Swing's JComponent
class (the "mother" of most Swing component classes), while the code that renders a JButton's
label is in the button's UI delegate class. The preceding diagram illustrates this subtle (and often confusing) point:
So Swing does have a strong MVC lineage. But it's also important to reiterate that our MVC architecture serves two distinct purposes:
- First, separating the model definition from a component facilitates model-driven programming in Swing.
- Second, the ability to delegate some of a component's view/controller responsibilities to separate look-and-feel objects provides the basis for Swing's pluggable look-and-feel architecture.
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 architecture
It 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.
JButton |
ButtonModel |
GUI |
JToggleButton |
ButtonModel |
GUI/data |
JCheckBox |
ButtonModel |
GUI/data |
JRadioButton |
ButtonModel |
GUI/data |
JMenu |
ButtonModel |
GUI |
JMenuItem |
ButtonModel |
GUI |
JCheckBoxMenuItem |
ButtonModel |
GUI/data |
JRadioButtonMenuItem |
ButtonModel |
GUI/data |
JComboBox |
ComboBoxModel |
data |
JProgressBar |
BoundedRangeModel |
GUI/data |
JScrollBar |
BoundedRangeModel |
GUI/data |
JSlider |
BoundedRangeModel |
GUI/data |
JTabbedPane |
SingleSelectionModel |
GUI |
JList |
ListModel |
data |
JList |
ListSelectionModel |
GUI |
JTable |
TableModel |
data |
JTable |
TableColumnModel |
GUI |
JTree |
TreeModel |
data |
JTree |
TreeSelectionModel |
GUI |
JEditorPane |
Document |
data |
JTextPane |
Document |
data |
JTextArea |
Document |
data |
JTextField |
Document |
data |
JPasswordField |
Document |
data |
![]() |
GUI-state vs. application-data models
The models provided by Swing fall into two general categories: GUI-state models and application-data models.
GUI-state models
GUI 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 models
An 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 BoundedRangeModel
on JSlider
or JProgressBar
. These models are highlighted in purple in the preceding table.
Swing's separable model API makes no specific distinctions between GUI state models and application-data models; ho wever, 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 definitions
Referring again to the table at the beginning of this section, notice that model definitions are shared across compone nts 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 JSlider
and JScrollbar
use the BoundedRangeModel
interface, a single BoundedRangeModel
instance could be plugged into both a JScrollbar
and a JSlider
and their visual state would always remain in sync.
![]() |
The separable-model API
Swing components that define models support a JavaBeans bound property for the model. For example, JSlider
uses the BoundedRangeModel
interface for its model definition. Consequently, it includes the following methods:
public BoundedRangeModel getModel()
public void setModel(BoundedRangeModel model)
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 JSlider
, aDefaultBoundedRangeModel
object is instantiated in its constructor:
|
If a program subsequently calls setModel()
, this default model is replaced, as in the following example:
|
For more complex models (such as those for JTable
and JList
), an abstract model implementation is also provided to enable developers to create their own models without starting from scratch. These classes are prepended with "Abstract".
For example, JList's
model interface is ListModel
, which provides both DefaultListModel
and AbstractListModel
classes to help the developer in building a list model.
![]() |
Model change notification
Models 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:
- Send a lightweight notification that the state has "changed" and require the listener to respond by sending a query back to the model to find out what has changed. The advantage of this approach is that a single event instance can be used for all notifications from a particular model -- which is highly desirable when the notifications tend to be high in frequency (such as when a
JScrollBar
is dragged). - Send a stateful notification that describes more precisely how the model has changed. This alternative requires a new event instance for each notification. It is desirable when a generic notification doesn't provide the listener with enough information to determine efficiently what has changed by querying the model (such as when a column of cells change value in a
JTable
).
![]() |
Lightweight notification
The following models in Swing use the lightweight notification, which is based on theChangeListener/ChangeEvent
API:
BoundedRangeModel | ChangeListener | ChangeEvent |
ButtonModel | ChangeListener | ChangeEvent |
SingleSelectionModel | ChangeListener | ChangeEvent |
The ChangeListener interface has a single generic method:
public void stateChanged(ChangeEvent e)
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:
public void addChangeListener(ChangeListener l)
public void removeChangeListener(ChangeListener l)
Therefore, to be notified when the value of a JSlider
has changed, the code might look like this:
|
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 notification
Models 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 Updates
A 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 completely
As 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 JSlider
's implementation of getValue()
, which internally delegates the method call to its model:
public int getValue() {
return getModel().getValue();
}
And so programs can simply do the following:
JSlider slider = new JSlider();
int value = slider.getValue();
//what's a "model," anyway?
![]() |
Separable model summary
So 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 JTable, JTree
, and the like) because they can greatly enhance the scalability and modularity of your application over the long run.
![]() |
Pluggable look-and-feel architecture
Swing'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 API
The pluggable look-and-feel API includes:
- Some small hooks in the Swing component classes.
- Some top-level API for look-and-feel management.
- A more complex API that actually implements look-and-feels in separate packages.
The component hooks
Each Swing component that has look-and-feel-specific behavior defines an abstract class in theswing.plaf
package to represent its UI delegate. The naming convention for these classes is to take the class name for the component, remove the "J" prefix, and append "UI." For example,JButton
defines its UI delegate with the plaf class ButtonUI
.
The UI delegate is created in the component's constructor and is accessible as a JavaBeans bound property on the component. For example, JScrollBar
provides the following methods to access its UI delegate:
public ScrollBarUI getUI()
public void setUI(ScrollBarUI ui)
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):
public void updateUI()
A look-and-feel implementation provides concrete subclasses for each abstract plaf UI class. For example, the Windows look-and-feel defines WindowsButtonUI
, a WindowsScrollBarUI
, and so on. When a component installs its UI delegate, it must have a way to look up the appropriate concrete class name for the current default look-and-feel dynamically. This operation is performed using a hash table in which the key is defined programmatically by the getUIClassID()
method in the component. The convention is to use the plaf abstract class name for these keys. For example, JScrollbar
provides:
public String getUIClassID() {
return "ScrollBarUI";
}
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 management
Swing 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 LookAndFeel
(for example,swing.plaf.motif.MotifLookAndFeel
) to provide Swing with the necessary information to manage the look-and-feel.
The UIManager is the API through which components and programs access look-and-feel information (they should rarely, if ever, talk directly to a LookAndFeel
instance). UIManager
is responsible for keeping track of which LookAndFeel
classes are available, which are installed, and which is currently the default. The UIManager
also manages access to the Defaults Table for the current look-and-feel.
The 'default' look and feel
The UIManager
also provides methods for getting and setting the current default LookAndFeel
:
|
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 UIManager.setLookAndFeel()
method. For example, the following code sample will set the default Look-and-Feel to be CDE/Motif:
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.motif.MotifLookAndFeel");
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 UIManager
provides the following static methods to programmatically obtain the appropriateLookAndFeel
class names for each of these cases:
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:
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
Dynamically Changing the Default Look-and-Feel
When 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 theUIManager.setLookAndFeel()
method makes a particular LookAndFeel
the current default by loading and initializing that LookAndFeel
instance, but it does not automatically cause any existing components to change their look-and-feel.
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 theSwingUtilities.updateComponentTreeUI()
method to assist with this process).
The look-and-feel of a component can be updated at any time to match the current default by invoking its updateUI()
method, which uses the following static method on UIManager
to get the appropriate UI delegate:
public static ComponentUI getUI(JComponent c)
For example, the implementation of updateUI()
for the JScrollBar looks like the following:
public void updateUI() {
setUI((ScrollBarUI) UIManager.getUI(this));
}
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 data
The UIManager
defines a static class, named UIManager.LookAndFeelInfo
, for storing the high-level name (such as. "Metal") and particular class name (such as "com.sun.java.swing.plaf.MetalLookAndFeel") for a LookAndFeel
. It uses these classes internally to manage the known LookAndFeel
objects. This information can be accessed from theUIManager
via the following static methods:
|
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 packages
The UI delegate classes provided in swing.plaf
( ButtonUI, ScrollBarUI
, and so on) define the precise API that a component can use to interact with the UI delegate instance. ( NOTE: Interfaces were originally used here, but they were replaced with abstract classes because we felt the API was not mature enough to withstand the concrete casting of an interface.) These plaf APIs are the root of all look-and-feel implementations.
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 swing.plaf
package (for example,. swing.plaf.motif, swing.plaf.metal
, and so on). A look-and-feel package contains the following:
- The
LookAndFeel
subclass (for instance,MetalLookAndFeel
). - All look-and-feel's UI delegate classes (for example,
MetalButtonUI, MetalTreeUI,
and the like). - Any look-and-feel utility classes (
MetalGraphicsUtils, MetalIconFactory
, and so on). - Other resources associated with the look-and-feel, such as image files.
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 swing.plaf
package are not frozen in the 1.0.X version of Swing. We are currently cleaning up those APIs for the version of Swing that will ship with JDK1.2beta4, at which time they will become frozen. So if you are developing your own look-and-feel implementation using the 1.0.1 API, this is likely to affect you.
The LookAndFeel Subclass
The LookAndFeel class defines the following abstract methods, which all subclasses must implement:
public String getName();
public String getID();
public String getDescription();
public boolean isNativeLookAndFeel();
public boolean isSupportedLookAndFeel();
The getName()
, getID()
, and getDescription()
methods provide generic information about the look-and-feel.
The isNativeLookAndFeel()
method returns true
if the look-and-feel is native to the current platform. For example, MotifLookAndFeel
returns true
if it is currently running on the Solaris platform, and returns false
otherwise.
The isSupportedLookAndFeel()
method returns whether or not this look-and-feel is authorized to run on the current platform. For example, WindowsLookAndFeel
returns true
only if it is running on a Windows 95, Windows 98, or Windows NT machine.
A LookAndFeel
class also provides methods for initialization and uninitialization:
public void initialize()
public void uninitialize()
The initialize()
method is invoked by the UIManager
when the LookAndFeel
is made the "default" using the UIManager.setLookAndFeel()
method. uninitialize()
is invoked by theUIManager
when the LookAndFeel
is about to be replaced as the default.
The Defaults Table
Finally, the LookAndFeel
class provides a method to return the look-and-feel's implementation of the Defaults Table:
public UIDefaults getDefaults()
The Defaults Table is represented by the UIDefaults class, a direct extension ofjava.util.Hashtable
, which adds methods for accessing specific types of information about a look-and-feel. This table must include all the UIClassID-to-classname
mapping information, as well as any default values for presentation-related properties (such as color, font, border, and icon) for each UI delegate. For example, following is a sample of what a fragment of getDefaults()
might look like for a hypothetical look-and-feel in a package called " mine
":
|
When the default look-and-feel is set with UIManager.setLookAndFeel()
, the UIManager
callsgetDefaults()
on the new LookAndFeel
instance and stores the hash table it returns. Subsequent calls to the UIManager's
lookup methods will be applied to this table. For example, after making "mine" the default Look-and-Feel:
UIManager.get("ButtonUI") => "mine.MyButtonUI"
The UI classes access their default information in the same way. For example, our exampleButtonUI
class would initialize the JButton
's "background" property like this:
button.setBackground(
UIManager.getColor("Button.background");
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 properties
Swing 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 plaf.UIResource
interface. The plaf
package provides a set of "marked" classes for representing these values,ColorUIResource, FontUIResource
, and BorderUIResource
. The preceding code example shows the usage of these classes to mark the default property values for the hypotheticalMyButtonUI
class.
![]() |
The UI delegate
The superclass of all UI Delegate classes is swing.plaf.ComponentUI
. This class contains the primary "machinery" for making the pluggable look-and-feel work. Its methods deal with UI installation and uninstallation, and with delegation of a component's geometry-handling and painting.
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 ComponentUI
.
UI installation and deinstallation
First off, the ComponentUI class defines these methods methods for UI delegate installation and uninstallation:
public void installUI(JComponent c)
public void uninstallUI(JComponent c)
Looking at the implementation of JComponent.setUI()
(which is always invoked from the setUI
method on JComponent subclasses), we can clearly see how UI delegate installation/de-installation works:
|
![]() |
UI installation illustrated
This article comes with a giant poster-size chart that illustrates the process installing a UI delegate. It can provide you with a valuable overview of the delegate-installation process.
To fold out the chart, just follow this link
![]() |
The UI delegate's installUI()
method is responsible for the following:
- Set default font, color, border, and opacity properties on the component.
- Install an appropriate layout manager on the component.
- Add any appropriate child subcomponents to the component
- Register any required event listeners on the component.
- Register any look-and-feel-specific keyboard actions (mnemonics, etc.)for the component.
- Register appropriate model listeners to be notified when to repaint.
- Initialize any appropriate instance data.
For example, the installUI()
method for an extension of ButtonUI might look like this:
|
Conventions for initializing component properties
Swing defines a number of conventions for initializing component properties at install-time, including the following:
- All values used for setting colors, font, and border properties should be obtained from the Defaults table (as described in the subsection on the LookAndFeel subclass).
- Color, font and border properties should be set if -- and only if -- the application has not already set them.
To facilitate convention No 1, the UIManager
class provides a number of static methods to extract property values of a particular type (for instance, the static methods UIManager.getColor(), UIManager.getFont(),
and so on).
Convention No. 2 is implemented by always checking for either a null
value or an instance ofUIResource
before setting the property.
The ComponentUI
's uninstall()
method must carefully undo everything that was done in theinstallUI()
method so that the component is left in a pristine state for the next UI delegate. Theuninstall()
method is responsible for:
- Clearing the border property if it has been set by installUI() .
- Remove the layout manager if it had been set by installUI() .
- Remove any subcomponents added by installUI() .
- Remove any event/model listeners that were added by installUI() .
- Remove any look-and-feel-specific keyboard actions that were installed by installUI() .
- Nullify any initialized instance data (to allow GC to clean up).
For example, an uninstall()
method to undo what we did in the above example installation might look like this:
|
Defining geometry
In 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 ComponentUI
provides the following methods for this purpose:
|
JComponent
's parallel methods (which are invoked by the LayoutManager during validation) then simply delegate to the UI object's geometry methods if the geometry property was not explicitly set by the program. Below is the implementation of JComponent.getPreferredSize()
which shows this delegation:
|
Even though the bounding box for all components is a Rectangle, it's possible
to simulate a non-rectangular component by overriding the implementation of the contains()
method from java.awt.Component.
(This method is used for the hit-testing of mouse events). But, like the other geometry properties in Swing, the UI delegate defines its own version of thecontains()
method, which is also delegated to by JComponent.contains()
:
public boolean contains(JComponent c, int x, int y) {
return (ui != null) ?
ui.contains(this, x, y)
:
super.contains(x, y);
}
So a UI delegate could provide non-rectangular "feel" by defining a particular implementation ofcontains()
(for example, if we wanted our MyButtonUI
class to implement a button with rounded corners).
Painting
Finally, the UI delegate must paint the component appropriately, hence ComponentUI
has the following methods:
public void paint(Graphics g, JComponent c)
public void update(Graphics g, JComponent c)
And once again, JComponent.paintComponent()
takes care to delegate the painting:
|
Similarly to the way in which things are done in AWT, the UI delegate's update()
method clears the background (if opaque) and then invokes its paint()
method, which is ultimately responsible for rendering the contents of the component.
Stateless vs. stateful delegates
All the methods on ComponentUI
take a JComponent
object as a parameter. This convention enables a stateless implementation of a UI delegate (because the delegate can always query back to the specified component instance for state information). Stateless UI delegate implementations allow a single UI delegate instance to be used for all instances of that component class, which can significantly reduce the number of objects instantiated.
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 ComponentUI
class defines a static method for returning a delegate instance:
public static ComponentUI
createUI(JComponent c)
It's the implementation of this method that determines whether the delegate is stateless or stateful. That's because the UIManager.getUI()
method invoked by the component to create the UI delegate internally invokes this createUI
method on the delegate class to get the instance.
The Swing look-and-feel implementations use both types of delegates. For example, Swing'sBasicButtonUI
class implements a stateless delegate:
|
While Swing's BasicTabbedPaneUI
uses the stateful approach:
public static ComponentUI createUI(JComponent c)
return new BasicTabbedPaneUI();
}
![]() |
Pluggable Look-and-Feel summary
The 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 UIManager
provides the API for applications to manage the look-and-feel at this level.
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
相关推荐
A Swing Architecture Overview(By Amy Fowler)
- **参考资源**:Sun的Java Tutorial中关于Swing GUI创建的部分,以及Amy Fowler的文章《ASwing Architecture Overview--The Inside Story on JFC Component Design》都是宝贵的学习资料,可帮助深入理解Swing架构。...
Swing interop support included with the JavaFX UI toolkit, including SwingNode (for embedding Swing inside a JavaFX application) and JFXPanel (for embedding JavaFX inside a Swing application). ...
Java Platform Debugger Architecture (JPDA) Java Debug Interface (JDI) Java Debug Wire Protocol (JDWP) JVMTM Tool Interface (JVM TI) (replaces JVMPI and JVMDI) -----------------------------...
内容概要:本文详细探讨了在主从博弈框架下,共享储能与综合能源微网的优化运行及其仿真复现。通过MATLAB和CPLEX的联合使用,展示了微网运营商和用户聚合商之间的动态博弈过程。上层模型关注微网运营商的定价策略,旨在最大化利润,考虑售电收益、储能运维成本等因素。下层模型则聚焦于用户聚合商的响应,根据电价调整电热负荷并参与共享储能调度。文中还介绍了电热耦合约束、充放电互斥约束等关键技术细节,并通过迭代博弈实现了策略更新。最终仿真结果显示,在引入电制热设备后,用户侧热负荷弹性提升,博弈收敛速度加快,达到双赢效果。 适合人群:从事能源系统优化、博弈论应用、MATLAB编程的研究人员和技术人员。 使用场景及目标:适用于希望深入了解主从博弈在综合能源系统中应用的学者和工程师。目标是掌握如何通过数学建模和编程实现复杂的能源系统优化,理解电热耦合机制和共享储能的作用。 其他说明:文章提供了详细的代码片段和仿真结果,帮助读者更好地理解和复现实验。此外,还讨论了一些常见的调试问题和解决方案,如约束冲突等。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
内容概要:深度学习在多个领域有着广泛应用。在计算机视觉方面,涵盖图像分类、目标检测、图像分割等任务,应用于自动驾驶、医疗影像分析等领域;在自然语言处理上,包括机器翻译、文本分类、文本生成等功能,服务于信息检索、内容创作等;语音识别与合成领域,实现了语音到文本的转换以及文本到语音的生成,推动了智能交互的发展;医疗领域,深度学习助力医学影像分析、疾病预测、个性化治疗及健康监测;金融领域,深度学习用于信用风险评估、欺诈检测、高频交易等,保障金融安全并优化投资策略;自动驾驶方面,环境感知与决策控制系统确保车辆安全行驶;娱乐与媒体领域,个性化推荐和内容生成提升了用户体验;工业与制造业中,质量检测和预测性维护提高了生产效率和产品质量。 适合人群:对深度学习及其应用感兴趣的初学者、研究人员以及相关领域的从业者。 使用场景及目标:帮助读者全面了解深度学习在不同行业的具体应用场景,明确各领域中深度学习解决的实际问题,为后续深入研究或项目实施提供方向指引。 其他说明:随着深度学习技术的持续进步,其应用范围也在不断扩大,文中提及的应用实例仅为当前主要成果展示,未来还有更多潜力待挖掘。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
周梁伟-大模型在融合通信中的应用实践
内容概要:本文详细介绍了利用西门子S7-200 PLC和组态王软件构建的一个花式喷泉控制系统的设计与实现。首先阐述了系统的硬件组成,包括三个环形喷泉组、七彩LED灯带以及功放喇叭等组件,并给出了详细的IO分配表。接着深入解析了关键的梯形图程序逻辑,如自动模式循环、灯光控制、喷泉舞步等部分的具体实现方法。此外,还分享了一些实际调试过程中遇到的问题及其解决方案,例如电源隔离、电磁干扰处理等。最后展示了组态王界面上生动有趣的动画效果设计思路。 适合人群:对PLC编程和工业自动化感兴趣的工程师和技术爱好者。 使用场景及目标:适用于需要设计类似互动娱乐设施的专业人士,旨在帮助他们掌握从硬件选型、程序编写到界面美化的完整流程,从而能够独立完成类似的工程项目。 其他说明:文中不仅提供了理论知识讲解,还包括了许多实践经验教训,对于初学者来说非常有价值。同时,作者还在系统中加入了一些趣味性的元素,如隐藏模式等,增加了项目的吸引力。
内容概要:本文详细介绍了利用COMSOL进行电弧熔池多物理场耦合仿真的方法和技术要点。首先解释了电弧熔池的本质及其复杂性,然后依次讲解了几何建模、材料属性设置、求解器配置以及后处理等方面的具体步骤和注意事项。文中提供了大量实用的MATLAB、Java和Python代码片段,帮助用户更好地理解和应用相关技术。此外,作者分享了许多实践经验,如分阶段激活物理场、使用光滑过渡函数处理相变、优化网格划分等,强调了参数选择和边界条件设定的重要性。 适合人群:从事电弧熔池仿真研究的专业人士,尤其是有一定COMSOL使用经验的研究人员。 使用场景及目标:适用于需要精确模拟电弧熔池行为的研究项目,旨在提高仿真精度并减少计算时间。主要目标是掌握多物理场耦合仿真的关键技术,解决实际工程中遇到的问题。 其他说明:文章不仅提供了详细的理论指导,还包括许多实用的操作技巧和常见错误的解决方案,有助于读者快速上手并深入理解电弧熔池仿真的难点和重点。
9f148310e17f2960fea3ff60af384a37_098bb292f553b9f4ff9c67367379fafd
# 【spring-ai-hanadb-store-1.0.0-M7.jar中文-英文对照文档.zip】 中包含: 中文-英文对照文档:【spring-ai-hanadb-store-1.0.0-M7-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【spring-ai-hanadb-store-1.0.0-M7.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【spring-ai-hanadb-store-1.0.0-M7.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【spring-ai-hanadb-store-1.0.0-M7.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【spring-ai-hanadb-store-1.0.0-M7-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: spring-ai-hanadb-store-1.0.0-M7.jar中文-英文对照文档.zip,java,spring-ai-hanadb-store-1.0.0-M7.jar,org.springframework.ai,spring-ai-hanadb-store,1.0.0-M7,org.springframework.ai.vectorstore.hanadb,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,springframework,spring,ai,hanadb,store,中文-英文对照API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【spring-ai-hanadb-store-1.0.0-M7.jar中文-英文
# 压缩文件中包含: 中文文档 jar包下载地址 Maven依赖 Gradle依赖 源代码下载地址 # 本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: ·本文档为人性化翻译,精心制作,请放心使用。 ·只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; ·不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;
3dmax插件
内容概要:本文详细介绍了单相全桥PWM整流器采用双闭环控制策略的具体实现方法和技术要点。电压环采用PI控制器来稳定直流侧电压,电流环则使用PR控制器确保交流电流与电压同相位并实现单位功率因数。文中提供了详细的MATLAB、C和Python代码片段,解释了各个控制器的设计思路和参数整定方法。此外,文章还讨论了突加负载测试、电压前馈补偿、PWM生成以及硬件选型等方面的内容,强调了系统的稳定性和快速响应能力。 适合人群:从事电力电子、自动控制领域的工程师和技术人员,尤其是对PWM整流器和双闭环控制感兴趣的读者。 使用场景及目标:适用于需要精确控制直流电压和交流电流的应用场景,如工业电源、新能源发电等领域。目标是提高系统的电能质量和动态响应速度,确保在负载变化时仍能保持稳定的输出。 其他说明:文章不仅提供了理论分析,还包括了大量的实际测试数据和波形图,帮助读者更好地理解和掌握双闭环控制的实际效果。同时,文中提到的一些调试技巧和注意事项对于实际工程应用非常有价值。
easyocr安装包和模型
AC_DIMMER交流调光灯stm32.7z
仲量联行-负责任的房地产:实现社会价值,赋能建筑环境,创造积极的环境和社会影响