The Multiplexing Look & Feel
Creating
and Using Auxiliary L&Fs in Swing
Swing's multiplexing look and feel is
a special look and feel (L&F) that provides an easy way to
extend the capabilities of a user interface created in Swing without
having to create a new look and feel. For example, an application
that is designed to generate output for users with special needs
can simultaneously provide and audio output and a Braille output,
along with the standard visual output that ordinary Swing applications
generate.
In this article, Swing team member Will
Walker explains how the Swing's multiplexing L&F works, and
provides some illustrations of how you might use the multiplexing
L&F in your own Swing applications.
By
Will Walker
This
article shows how the Swing tool set supports the multiplexing
look and feel: a special look and feel (L&F) that provides
an easy way to extend the capabilities of a user interface created
in Swing without having to create a new look and feel. More specifically,
the article covers these major topics:
- It describes the purpose of the multiplexing
look and feel and outlines the problems that the multiplexing
L&F can solve.
- It shows how to use the multiplexing look
and feel in Swing programs.
- It briefly demonstrates how to create
an auxiliary look and feel.
- It describes some of the features and
capabilities of the default multiplexing look and feel that comes
with Swing.
- It very briefly outlines some future plans
for supporting the multiplexing look and feel in Swing.
This article assumes that you are familiar
with Swing's concept of pluggable looks and feels (PL&Fs). If
you aren't, it might be beneficial to read the article titled "An
Overview of Swing Architecture" before
you continue reading this article.
Overview
To
simplify the task of extending the capabilities of a Swing user
interface without having to create a new look and feel, Swing supports
the concept of a multiplexing look and feel. When a component
asks for its UI instance (that is, when it invokes the getUI()
method), the multiplexing look and feel transparently creates --and
simultaneously supports -- UI instances from several different look-and-feels.
This mechanism enables Swing to make use
of the concept of more specialized look and feels -- known as auxiliary
look and feels -- that can be combined to create an environment
tailored to the needs of the end user.
For example, with the help of Swing's multiplexing
look and feel, an application that makes use of a default visual
look and feel can simultaneously and automatically load and maintain
several auxiliary look and feels as well -- such as one for audio
and another for Braille. All this can be done at the same time,
and without requiring modifications to the default visual look and
feel.
Without the multiplexing look and feel, a
developer who wanted to enhance a particular look and feel would
need to create a subclass of that look and feel. For example, to
add audio support to the Metal look and feel without using the multiplexing
look and feel, the developer would need to create a subclass of
the Metal look and feel and add audio support to that subclass.
If the developer also wanted to add audio support to other L&Fs,
such as Motif or Windows, the developers would need to create subclasses
of them as well.
This approach has at least two shortcomings:
- First, each subclass must use what is
essentially a copy of the same code, potentially creating a difficult
support situation for the developer.
- Second, and more significantly for the
end user, some application developers might force the use of a
particular look and feel to be used. When this approach is used,
the end user can't even use Swing's enhanced look and feel subclass.
Swing's multiplexing look and feel solves
both these problems simultaneously because it allows multiple look
and feels to be combined. The multiplexing L&F solves the first
problem (having to use what amounts to a second copy of the same
code) because it allows the developer to create a specialized look
and feel that can then be combined with other look and feels.
The multiplexing look and feel is also a
good solution to the second problem (having to force the use of
a particular L&F) because it allows a specialized look and feel
to be used in conjunction with whatever default look and feel the
application may have locked in place.
Using Auxiliary Look and Feels
It's
easy to use auxiliary look-and-feels is with Swing. To instruct
Swing to use the multiplexing look and feel, all an application
has to do is modify the $JDKHOME/lib/swing.properties
file to include a definition of the swing.auxiliarylaf
property. Swing treats the swing.auxiliarylaf
property as a comma-separated list of LookAndFeel
subclasses that specify what auxiliary look-and-feels should be
used in addition to the default look and feel. If at least one valid
LookAndFeel
subclass is specified in the swing.auxiliarylaf
property, Swing automatically uses the multiplexing look and feel
to load and support the default and auxiliary look and feels.
For example, let's assume that an application
makes use of a look-and-feel that supports audio feedback, and also
uses an L&F that that adds support to Swing for a mouse with
a speed-scrolling wheel on it. Let's also assume that the audio-feedback
L&F is named com.myco.AudioLookAndFeel,
and that the L&F which adds speed-scrolling support to the mouse
is named com.mouseco.WheelMouseLookAndFeel.
To tell Swing to use both these L&Fs
-- and to use a default look and feel at the same time -- your application
could simply add the following line to the $JDKHOME/lib/swing.properties
file:
swing.auxiliarylaf=com.myco.AudioLookAndFeel,
com.mouseco.WheelMouseLookAndFeel
This
statement tells Swing to obtain a component's UI from the multiplexing
look and feel automatically, instead of obtaining it directly from
the default look and feel. The resulting multiplexing UI is a small
delegate that obtains and maintains UIs from the default and auxiliary
look and feels. As a result, when a method is called in a multiplexing
UI instance, the multiplexing UI will call the same method in each
of the UIs obtained from the default and auxiliary look and feels.
Developing an auxiliary L&F
An
auxiliary look and feel is no different from any other LookAndFeel
subclass except that it doesn't have to provide all the support
of a look and feel that would be used as the default look and feel. For
example, an auxiliary look and feel that supports just audio feedback
doesn't need to provide any code for painting. As a result, developing
an auxiliary look and feel can be easier than developing a visual
look and feel and also permits the developer to concentrate solely
on providing just this specialized functionality. Note that the
primary purpose of an auxiliary look and feel is to enhance the
default look and feel. Therefore, auxiliary look and feels tend
be non visual. Since an auxiliary look and feel is a LookAndFeel
subclass, however, there is nothing to prevent the auxiliary look
and feel from rendering information on the display.
Auxiliary L&Fs do's and don'ts
The
following paragraphs provide some general recommendations for developing
auxiliary look and feels.
Use installUI()
and uninstallUI()
In most cases, auxiliary look-and-feels
are primarily interested in the installUI()
and uninstallUI()
methods of the UI. These are the methods that are called when
a component's look and feel is set. They give the UI a chance
to register and remove listeners on the component and its data
model.
Don't Extend
Visual Look and Feels
We recommended that you don't implement
UI classes of an auxiliary look and feel as subclasses of the
UI classes of a visual look and feel. Why not? Because they might
accidentally inherit code that installs listeners on a component
instance or renders the component on the display. As a result,
your auxiliary look and feel would compete with the default look
and feel rather than cooperating with it.
Instead, we recommend that the UI classes of an auxiliary look
and feel directly extend the abstract UI classes in the swing.plaf
package. By using this strategy, the developer of an auxiliary
look and feel can avoid competing with the default look and feel.
What UI
classes should override
We also recommend that each UI class of
an auxiliary look and feel override the methods of the swing.plaf
UI class it extends. The reasons for this recommendation are similar
to those given for not extending a visual look and feel (see first
item, above). For example, there is a ComponentUI
class that provides a default implementation for the update
method. This default implementation draws on the display if the
component is opaque. If a UI class from a non-visual auxiliary
look and feel did not override this method, it would cause all
opaque components to appear as blank areas on the screen!
When to extend UIDefaults
In many cases, you
might want an auxiliary look and feel to be "incomplete." That
is, your application might not want to provide the complete set
of UI classes for Swing.
For example, an auxiliary look and feel might choose not to provide
a LabelUI class, but may wish to provide a ButtonUI class. This
option is allowed, and the multiplexing look and feel gracefully
handles such situations.
By default, however, Swing issues an error message when it asks
a look and feel for a UI instance and the look and feel does not
support that UI. This message can be annoying, especially to auxiliary
look-and-feel developers who purposely don't want to support a particular
UI.
Fortunately, auxiliary L&F developers can prevent this error
message from being issued by simply creating a subclass of the UIDefaults
class and returning that from the getDefaults()
method of the LookAndFeel:
public class MyAuxLookAndFeel
extends LookAndFeel {
...
public UIDefaults getDefaults() {
UIDefaults table =
new MyAuxUIDefaults();
Object[] uiDefaults = {
"ButtonUI", "MyAuxButtonUI",
...
}
table.putDefaults(uiDefaults);
return table;
}
}
class MyAuxUIDefaults extends UIDefaults {
protected void getUIError(String msg) {
//System.err.println
// ("An
annoying message!");
}
}
In the
preceding example, an auxiliary look and feel named MyAux
creates a UIDefaults subclass
that overrides the getUIError()
method. The getUIError()
method is the method that is called when Swing cannot find a UI
instance in a look and feel. By merely doing nothing in this method,
you can avoid the error message.
Examining UI instances from other
L&Fs
In
some rare instances, a UI instance from an auxiliary look and feel
may be interested in the default UI instance used by the component. In
these cases, the UI instance from auxiliary look and feel can obtain
the UI from a component by calling its getUI()
method. If the resulting UI is an instance of one of the multiplexing
look and feel UI classes from the default multiplexing look and
feel (e.g., MultiButtonUI),
the UI instance from the auxiliary look and feel can call the getUIs
method of this object to obtain an array containing complete list
of all UIs handled by the multiplexing factory. The first element
(i.e., element[0])
is guaranteed to be the UI created from the default look and feel.
The Default Multiplexing L&F
The
multiplexing look and feel itself is meant to be transparent to
all developers and users. It should "just work" -- and
it is used only when the user tells Swing to use an auxiliary look
and feel.
There may be cases, however, where the implementation
of the default multiplexing look and feel does not meet a developer's
needs. This section describes the implementation of the default
multiplexing look and feel and also provides details for how to
replace it with a different one.
The default multiplexing look and feel, swing.plaf.multi.MultiLookAndFeel,
is stored in the multi.jar
file that is shipped with Swing. When the multiplexing look and
feel is in use, each component requesting a UI instance actually
gets an instance of a multiplexing UI instead of an instance of
a UI from the default look and feel. The default multiplexing UI
obtains and maintains UIs from the default and auxiliary look-and-feels,
and refers to these UIs in the following manner:
- The UI instance from the default look
and feel is always the first to be created. After that, a UI instance
will be created from each auxiliary look and feel in the order
they are specified in the swing.auxiliarylaf
property.
- When a method that requests information
from a UI instance is invoked, the multiplexing UI instance returns
only the results from the UI that are obtained from the default
factory. For example, when the getPreferredSize()
method is invoked on a multiplexing UI, the UI returns only the
results of getPreferredSize()
on the UI obtained from the default factory. The rest of the UIs
obtained from the auxiliary factories are ignored.
- When a method that does not request information
from the UI instance is invoked, the multiplexing UI instance
invokes that method on the UI obtained from the default factory
and all the UIs obtained from the auxiliary factories as well.
For example, invoking the installUI()
method on a multiplexing UI causes the multiplexing UI to invoke
installUI()
on UI obtained from the default factory and the UIs obtained from
the auxiliary factories.
In all cases, the UI instance obtained from
the default look and feel is acted upon first, and then the auxiliary
look and feels are acted upon in the order they are specified in
the swing.auxiliarylaf
property.
Overriding the Default L&F
While
we hope the behavior of the default multiplexing look and feel is
flexible enough not to require an alternative multiplexing look
and feel, Swing allows the user to specify the multiplexing look
and feel to use.
To do that, all the user has to do is modify
the $JDKHOME/lib/swing.properties
file to include a definition of the swing.plaf.multiplexinglaf
property. Swing then treats the swing.plaf.multiplexinglaf
property as a LookAndFeel
subclass that supports multiplexing.
For example, if a user has a multiplexing
look and feel named com.myco.SuperMultiLookAndFeel
that is a better match for their needs than the default multiplexing
look and feel provided by swing.plaf.MultiLookAndFeel,
the user could include the following line in $JDKHOME/lib/swing.properties:
swing.plaf.mulitplexinglaf
=
com.myco.SuperMultiLookAndFeel
This
statement instructs Swing to use com.myco.SuperMultiLookAndFeel
instead of swing.plaf.MultiLookAndFeel. But
if you use this kind of statement, be careful, because the suppliers
of auxiliary look-and-feels will most likely have developed and
tested against Swing's default multiplexing look and feel.
What's Next
In
the current release of Swing, the multiplexing look and feel is
meant to be relatively latent; there's just enough there to support
developers who have requested such a feature. In the future, work
on the multiplexing look and feel will include additions to the
UIManager
class that will allow procedural determination of the auxiliary
and multiplexing look and feels.
|