Changing UI Default Settings in Java
Changing UI Default Settings in Java
By Daniel Horn
Abstract:
This paper discusses simple methods for changing default
settings for UI elements in Swing. These methods can simplify
testing of UI layouts that otherwise might be dependent on the "Look and
Feel" (LAF) of an underlying platform. They can also be used to
fix some of the problems (for example, with fonts or foreground and background
colors) that may occur.
The source code for a sample Java application demonstrates techniques for changing default properties
of visual components and gives its user
a simple way of getting information on default UI settings for LAFs; in
particular, changing a default UI setting is easy once you know the "name" of the
setting.
The purpose of this paper is to introduce the Java UIManager and UIDefaults
classes, to provide some tips on how the inspector and the visual designer in
JBuilder interact with LAFs, and to discuss how these affect the appearance of your
applications and applets.
Introduction:
When using the designer in JBuilder to create a GUI component such as a panel, frame,
or dialog,
one should always be careful that your layout does not depend solely on the
current look and feel (LAF). At design time, JBuilder makes it easy to
test against different LAFs; simply right click on the
designer and choose "Metal", "CDE/Motif", or
"Windows" from the "Look and Feel" submenu.
However, design time testing is not enough. If you develop primarily on one platform, say Windows, and only test your
Java app rarely on other platforms (e.g., Linux), you will encounter some
unpleasant surprises.
As different operating systems and JDK versions will affect a LAF, runtime
testing of your application or applet is also necessary. The layout and
readability of a panel depends on such properties as the preferred size of
contained components, while, for example, the component's size may depend on the
font selected during design time. In particular, one will find that
default fonts for visual components will change depending on the underlying
platform, and so one's layout may appear quite different than expected.
Because components have default fonts that
are not necessarily the same on different platforms, you might find that fields and labels that look fine on one
platform will be truncated on another.
As you switch between different LAFs, you might observe different appearances
and behaviors that you want to change. For example, fonts as well
as foreground and background colors may change. A developer who doesn't take
these changes into account may come up with, for example, a panel that looks
fine on Windows but is surprisingly different on Linux. Some specific
examples are:
- The font in a textarea (JTextArea) may differ from that of a JLabel,
JTextPane, JTextField, or JEditorPane..
- The foreground and background colors in a textarea may
differ from those of a JLabel, JTextPane, or JEditorPane.
- The selection colors in a textarea or textfield (JTextField) may differ from those of
a JTextPane, or JEditorPane.
- The text in a label (JLabel) is bold in the Metal LAF but not bold in
every other LAF.
You may wish all text components of this type to look alike, regardless of
the LAF, (examples 1, 2, and 3), or you may simply not like the default behavior
and wish to change it (as in 4). In our sample code, you will see that we
use a JTextArea to display information as if it were a multi-line label, so we
want we change its colors and font to match those of a JLabel.
This paper discusses simple methods for changing default
settings for UI elements in Swing. These methods can be applied with advantage
not only to simplifying
testing of UI layouts that need to be deployed to platforms with different
default LAFs but also to fixing some of the problems that will
occur.
The Sample Program:
The source code (http://codecentral.borland.com/codecentral/ccWeb.exe/listing?id=19851) for a sample Java application, the UI
Defaults Browser, demonstrates simple techniques for changing default properties
of visual components. In addition, as its name suggests, it gives the user
a simple way of getting information on default UI settings for LAFs. As
you will see, changing a default UI setting is easy once you know the key or
"name" of the setting; the UI Defaults Browser gives you an easy way
to find the names you may be interested in.
Using the Program:
When the program starts, the default LAF of the underlying system is
loaded. The main window contains a table of all the UI Default settings
for the current LAF. A row in the table consists of the key (the setting
name), its value, the type of the value (such as Font, String, Icon, Integer,
Color, Method, or Class); the fourth column may contain information that is
simply a more readable or abbreviated version of information that appears in
another column.
You may sort the table by clicking on the different column headers.
Clicking a header a second time will reverse the sort order.

Double-click on a table row to open a modeless dialog to view that row's
information in a slightly more readable fashion. This gives a convenient
method for comparing information from different rows in the table or from
similar rows in different versions of table data (e.g., when the LAF is
changed).

Press the "Show Test Window" button to bring up a simple modeless
window that shows various text components (a JTextField, a JTextArea, a
JEditorPane, a JTextPane, and a JLabel) using the current UI defaults.
Change the LAF, using the "Look and Feel" menu, and press the
"Show Test Window" button again to see how the appearance (in
particular, the default fonts and
colors) of these components changes.



Changing the LAF with the "Look and Feel" menu items results in the
table data being refreshed with the settings for the currently selected LAF.
Press the "Do Text Area Fix" button to make some changes to the
default appearance of a TextArea. Press the "Show Test Window"
button again to see how its appearance changes; you will notice that its font
now matches the default font for a TextPane, its background color matches that
of a Label, and its foreground color matches that of a TextPane. Press the
"Refresh Table" button to see these changes appear in the table.

After pressing the "Do Text Area Fix" button, change the LAF, and
then press the "Show Test Window" button again. You will note
that the changes via the "Text Area Fix" for the previous LAF still
apply to the new LAF. While this is arguably a bug, the real lesson here
is that, if your application allows runtime changing of the LAF, then the LAF
"fixes" need to be reapplied after each LAF change.
You can force "Text Area Fix" settings for the application's
initial LAF by adding "/doFix" to
the command line switch for launching the Java application.
The sample program also demonstrates how to force your application to use a
specific LAF when it starts up. See the information in the "About
Box" for the command line arguments to use to indicate a desired LAF.
If you are launching the application from within the JBuilder environment,
see the "UIDefaults Application Debug" runtime configuration to
automatically add the /doFix and a /laf: command line switch. (Choose Run
| Configurations... from the main menu to view or edit the configuration).
How It Works: First Example: The About Box
Let's start with the simplest case of changing default attributes in a visual
component.
Take a look at the application's "About" box (choose Help | About
from the main menu). While this about box is an example of window that is
not particularly
interesting nor well laid out, if you change between the different LAFs and open
the about box for each one, you will see how much its appearance changes.
Using a text area to display information, we are able to permit scrolling (by
adding the area to a JScrollPane), to
disable editing, and to allow (clipboard) selection and copy operations.
The main reason we chose to use a JTextArea, however, is that we really want
something like a multi-line label (something which does not appear to be available in the
standard Swing components). The problem with using a JTextArea is that it
looks quite a bit different from a JLabel. We fix that at runtime by
adding the following lines to the Frame1_AboutBox.jbInit() method:
// Here's the manual way to change the settings of a text area to
// look like a multi-line label.
jTextArea1.setBackground(label1.getBackground());
jTextArea1.setForeground(label1.getForeground());
jTextArea1.setFont(label1.getFont());
In other words, we pick a label on the same panel and set the textarea to
use the same font and the same foreground and background colors as the label.
(Make sure that this group of lines follows any custom settings made for
"label1" in jbInit(). You must do this, say, if you change the
default background color of label1 and you still want jTextArea1 to have a
matching color.)
You may now be wondering why we do not use the Inspector in JBuilder's visual
designer to accomplish this. There are a couple of problems with using the Inspector. If you simply change between the different LAFs
available in the designer, you will note the information listed in the following
table:
| Component |
Look and Feel |
Default
Foreground Color |
Default
Background Color |
Default Font |
| JTextArea |
Metal |
Black |
White |
Dialog, Size=12 |
| CDE/Motif |
Black |
R=174, G=178, B=195 |
Monospaced, Size=12 |
| Windows |
Black |
White |
Monospaced, Size=12 |
| JLabel |
Metal |
Black |
R=204, G=204, B=204 |
Dialog, Size=12, Bold |
| CDE/Motif |
Black |
R=174, G=178, B=195 |
Dialog, Size=12 |
| Windows |
Black |
R=236, G=233, B=216 |
MS Sans Serif, Size=11 |
| JTextPane |
Metal |
Black |
White |
Dialog, Size=12 |
| CDE/Motif |
Black |
White |
Serif, Size=12 |
| Windows |
Black |
White |
MS Sans Serif, Size=11 |
(For all of the specific examples described here and below, JBuilder 8 on
Windows XP was used; by now you should not be surprised to see differences if
you use a different JDK version or different underlying OS. Also, note
that the "Dialog" font seems to indicate the default font for a
specific platform; for example, the "Dialog" font for Linux may be a
different font family and/or size from those for Windows).
The first problem arises because a change in the visual Inspector in JBuilder
does not always result in a corresponding line being added to the jbInit()
method. Suppose that you wanted a JTextArea to always have a white
background, regardless of the LAF, but that you make this selection in the
designer while the LAF is Windows or Metal. You then go and run your app
with a LAF of Motif to find that a JTextArea has a grayish background. The
problem here is that when the setting being made in the JB designer is the
default value with respect to the LAF currently selected in the designer
then no line is added to jbInit().
A much better way to use the Inspector in the case of colors is to make your
selection one of the descriptive choices in the dropdown list. Select a
JTextArea object; then, in the Inspector, choose the "background"
property. Click on the value cell to display a dropdown list (if you click
to far to the right, you display a dialog box which is not what we want
here). Do not pick one of the concrete values such as "White" or
"Red" from the list but instead a descriptive value such as "Label.background".
This will result in a line being added to jbInit() that looks like:
jTextArea1.setBackground(UIManager.getColor("Label.background"));
The earlier example showed how to use the same background color as a specific
label. This example says to set the same background to the default
background color for labels; this will often be a better choice to make.
The problem with making this choice through the Inspector is not a problem at all with the code
but with confusion that may arise from relying too heavily on the visual designer. If you make a color
choice such as "Label.background" and then change the LAF in the
designer, the color value displayed in the Inspector may not match what you
think it does. If you switch to the source view and look at the code in
the jbInit() method, you will see that the "setBackground" line of
code is exactly what you want it to be, even if the Inspector fails to report
the value you expect. The lesson in this case is: if in doubt, check the
actual code in the jbInit() method; that's what the compiler will see and use.
Fonts present a slightly different problem in that the designer does not let
us choose UI defaults from a drop down list as we could with colors. Also,
you will see that some components have "Dialog" as their default font
in some LAFs. "Dialog" seems to mean use the default font for
the current LAF and can change with different underlying platforms.
As you will see, you may prefer to set a font based on UI default value for a
type rather than using the font of a specific object.
The UIManager Class
As you've seen from the above example, the UIManager class is important to
understand when dealing with LAFs and UI default settings.
With UIManager, it is possible to get a list information on the LAFs
installed with your JDK:
UIManager.LookAndFeelInfo[] info =
UIManager.getInstalledLookAndFeels();
for (int i = 0; i < info.length; i++)
{
System.out.println(info[i].toString());
}
A LookAndFeelInfo object is really just a pair of strings, the name of a LAF
and the name of an associated class that supports it.
Given the classname of a LAF, you can set it as the current LAF by:
UIManager.setLookAndFeel(strLAF);
You should make a call like this before creating any application UI elements
such as your main window. Typically you make the call before creating your
application class in the application class's main() method (see virtually any
Java application class created using JBuilder). If you don't make this
call, the default LAF, Metal, is used. Passing in an invalid string to
setLookandFeel() or naming a LAF that is unsupported in your JDK (such as
"Mac" on a Windows platform) will result in a ClassNotFoundException;
if the exception is caught, the default LAF would be used in this case.
UIDefaultsApplication.java provides example code for all of this. In
addition, one can see how to specify the application's LAF via a command line
argument. You may deem this functionality desirable for all users or for
testing purposes only.
Changing the LAF after an application has already started (i.e., after the
application's main window has been created) is slightly more complicated. One
needs to invalidate and repaint every window to display the newly selected look:
try
{
UIManager.setLookAndFeel(strLAF);
SwingUtilities.updateComponentTreeUI(this);
}
catch (Exception exc)
{
}
An example of this appears in Frame1.SetLookAndFeel(). Note that you
will need to apply updateComponentTreeUI() to each top level window in your
application. Again, this may not be general functionality that you wish to
expose to all users for all applications; still, it may be handy to know during
testing.
The UIDefaults Class
As we saw earlier, one can get the default background Color object for all
labels by making a call:
UIManager.getColor("Label.background");
"Label.background" is not the only property we can make use
of. Here's how we get a list of all of the UI default property names and
values associated with them:
UIDefaults uiDefaults = UIManager.getDefaults();
Enumeration enum = uiDefaults.keys();
while (enum.hasMoreElements())
{
Object key = enum.nextElement();
Object val = uiDefaults.get(key);
System.out.println("[" + key.toString() + "]:[" +
(null != val ? val.toString() : "(null)") +
"]");
}
The TableModel used to supply the information in the main window's table gets
its data in a similar manner; see UIDefaultsTableModel.java for details.
The important thing to note is that once we have the key for a UI default
setting (say, by scanning the table in the UI Defaults Browser main window), we
can then retrieve the associated default UI object to copy for use
elsewhere. (It is probably not a good idea to change the object directly
as it may be referred to by more than one key).
For example, the developer of UIDefaultsApplication may have decided that he
does not like the default appearance of JTextArea objects. Maybe JTextArea
just seems too different from the other text components. He may decide
that he wants to only use these objects as multi-line labels, and that by
default, he wants their appearance, regardless of LAF, to follow the following
guidelines:
- the default font used for a JTextArea should match that of a JTextPane;
- the default background color of a JTextArea should match that of a JLabel; and
- the default foreground color should match that of a JTextPane.
Now, the developer could go through his app and change each JTextArea
object to match these settings. This can be tedious, take a lot of time,
and be error-prone; if he has many of these objects scattered throughout his
app, he can easily miss one or forget to make the change if he adds a new
JTextArea object later on. A better solution is to make these changes
once, especially as in this case, it is the default appearance of these
objects he wants to change. This is what happens with the addition of the
following code to the app:
UIDefaults uiDefaults = UIManager.getDefaults();
uiDefaults.put("TextArea.font", uiDefaults.get("TextPane.font"));
uiDefaults.put("TextArea.background", uiDefaults.get("Label.background"));
uiDefaults.put("TextArea.foreground",
uiDefaults.get("TextPane.foreground"));
(In the above example, one would probably also want JLabel objects to have a
matching font or at least a non-bold font (as it does with the Metal LAF).
We leave it as an exercise to the reader to figure out the single line of code
that needs to be added to the app).
(If desired, one could use other methods from UIDefaults, such as getColor()
and getFont(), which provide a bit more type safety as they return null if the
returned object is not of the expected type).
Look at UIDefaultsApplication.DoTextAreaFix() to see this in the sample
code. It is called whenever the user presses the "Do Text Area
Fix" button or if the user starts the application with the "/doFix"
command line switch. Note that this type of fix needs to be reapplied each
time, if any, the LAF changes at runtime; in practice, it would probably be best
to place this call between the calls to setLookAndFeel() and
updateComponentTreeUI().
Finally, just in case there's anybody left who would doubt why you any of
this should be useful, take a look at the following dialog from an application
you're all familiar with:
It demonstrates a visual incongruity that appears in a great deal
of Java code. Now you know how to make a simple change to avoid it in your
own code.
Whats next?
The sample code was intended to help you get started learning about LAFs, the
UIManager and UIDefaults classes, and how they affect the appearance of your
applications and applets. There are many ways it can be expanded to
further your explorations.
One possible future direction would be to turn this functionality into a component that you can include in debug builds of your own
app to allow runtime testing of windows change with different LAFs. One
might wish to have this component apply such changes to all windows in an app or
only a specified few.
Another possible improvement to the UI Defaults Browser would be to display
visual examples for values such as images (e.g., icons), colors, and fonts.
Another possibility would be to allow the user to change UI defaults on the fly by copying one value entry from
the table to another that is of a compatible type; that is, a font (or color)
value from one row could be copied to another row's value if it was also a font
(or color, respectively).
One could use the ideas presented above to create one's own custom LAF by
copying values from pre-existing LAFs and resetting some values to one's own
objects.
Questions, comments, and suggestions may be sent to the
author at dan@nerds.com ; use the title of the article or the words BDN
Article in the email subject.
P.S. If you didn't spot the incongruity I mentioned in the previous section,
compare the font in the Description text area with the text that appears
elsewhere in the dialog.
Connect with Us