Styling Java User Interfaces
Improving data display can help users perform tasks better.
by Philippe Kaplan
Posted May 29, 2003
A well-designed user interface (UI) can make an end user perform more effectively by processing more data without sacrificing usability and readability. Computer power is improving over time, enabling larger and more complex UIs without a corresponding loss of speed. This is not only a matter of hardware; making a UI more usable and more readable means improving the data display for a better quality of data rendering. However, while the design of the UI has a strong influence, it is complex and expensive to upgrade. That's why we consider styling as a solution, because it enhances the pertinence of the display while keeping the same UI design. Let's see how we can use what we know to style Java objects, and then apply one typical styling system, Cascading Style Sheets (CSS), to the Java world to enhance the informative level of a user interface.
CSS is a simple mechanism for adding style such as fonts, colors, and spacing to Web documents. This technology remains today confined to HTML and XML documents, although its principles are suitable for other domains.
The typical architecture of a Web page is shown in Figure 1. The HTML and CSS are located on a Web server. They transit through the network to the client browser that displays the page. The CSS customizes the look of the pages according to the HTML content and the rules defined in the CSS file.
Style sheet documents are made up of rules. Here is an example of a CSS rule:
H1 {
ext-align : center;
color : green;
}
The part before the opening curly brackets (H1) is called the selector. This selector matches the elements tagged H1 in the HTML document. The property settings defined between the curly brackets, called declarations, are interpreted by the browser to customize the rendering of all elements tagged H1. In the example, all H1 elements display as centered green text.
The selector can also match element attribute values, IDs, hierarchical structures, and so on. For example, the CSS rule:
A[REL='home'] {
color: green;
}
matches all HTML elements tagged A and whose attribute is named REL with the value 'home', like this:
<A REL='home' name='test'>test</A>
and sets their color to green. The important point here is that the selector provides a power close to regular expressions. (A regular expression system is a common tool in programming languages, especially shells or interpreters, to recognize patterns in a document.)
Several rules can match the same element. CSS defines a priority order for the rules based on the specificity of the selectors. The specificity of a selector is a value computed from the number and kind of patterns that constitute the selector. Roughly, the specificity value grows with the precision level of the selector. In case the specificity is the same, the rule that has been defined last gets the highest priority.
Once the rules are sorted in order of priority, a simple inheritance mechanism is defined in this way. Specific rules override general rules when their declarations are identical. Two declarations are identical if their left parts are identical. This means that a declaration defined in a low priority rule applies unless it has been redefined in a rule of higher priority, which is useful because it allows you to set default values in general rules. With the power of selectors and the inheritance, the rules remain compact.
Style sheets can be cascaded, which means that they are merged using the same priority rule. This feature allows the user to supply a custom style sheet that overrides some of the CSS rules defined by the author of the document. CSS has some limitations: it is perceived as "yet another language"; there are few good editors, especially at the inception of CSS; and there is no extension mechanism: all the patterns and declarations are hard coded.
Separating Style from Data
When styling is delegated to a CSS document, the HTML document can focus on the actual information. The CSS is usually shared within a Web site to keep a consistent look and feel across all the pages. This separation alone justifies the use of CSS. It allows you to define the look and feel in a single place and easily tailor the appearance for different categories of users (who want large fonts, are color blind, or have personal tastes) and different media (like PDAs or printers).
CSS declarations also provide powerful layout capabilities that HTML cannot handle by itself. Without CSS, browsers use proprietary HTML extensions, images, JavaScript, or other plugins to enhance presentations (although it is basically a question of rendering pure text with fancy layouts). These drifts are pushing away the accessibility of the Web, as they hide the information behind nonstandard or unreachable constructs. They do not work for every user, nor all the time (because of versioning problems).
Table rendering and navigation can also be adjusted on the client browser through CSS. One example of this is to cope with a limited device or to ease life for a disabled user.
CSS is not bound to HTML. CSS can be naturally coupled with XML documents. XML provides the structure and CSS provides the rendering customization. For instance, Scalable Vector Graphics (SVG) is an XML dialect to describe 2D graphics. XML describes the object types and structures, and CSS sets the color, size, relative positions, and so on, of the SVG objects. CSS for SVG is different from CSS for HTML, but it relies on the same basis. SVG is another recommendation of the W3C.
Outside the W3C scope, XUL is a cross-platform XML dialect for describing application UIs. The XML provides the UI structure and the CSS provides the rendering options. XUL is an essential part of the Mozilla browser, but it tackles only its proprietary graphic components.
Styling Java UI
Java UI components like Swing generally implement the Model-View-Controller (MVC) design pattern. The model contains the business data, but no style information. The View (often combined with the Controller part) displays the business data without paying special attention to style and semantics of the model content. Any additional customization must be implemented manually (see Figure 2).
Swing provides an API to customize the graphic components at a low level. To create a button and set its color to pink:
JButton button = new
JButton("cancel");
button.setBackground(Color.pink);
Note that the model in this case only consists of the string "cancel". Even in this simple case we may need styling. For example, we may need to adapt the font size to the length of the text displayed, or we may need a rule that turns all the "cancel" buttons of the application into pink. Swing does not provide any facility to implement this kind of behavior. We need to write specific Java code to handle such a requirement.
However, Swing has a notion of style mainly aimed at the Pluggable Look And Feel (PLAF) feature. The PLAF is supposed to replace the whole look and feel of the application using a few lines of code, like:
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.gtk.GTK
LookAndFeel");
SwingUtilities.updateComponentTree
UI(frame);
frame.pack();
The look and feel itself is customizable by setting properties on a Swing class that takes over the look and feel specifications. For example:
UIManager.put
("ToolTip.background",
Color.cyan);
sets the background color of all ToolTip objects to cyan.
This styling mechanism has some limitations: first, it is unrelated to the model, that is, the business data. A UI component, like a table, is destined to display business data. Today this data seems cleared of its semantics. It is merely displayed in a table as a stream of values without paying attention to the meaning of these values. Further, it is rather static, since the customization takes place at creation time only. Moreover, the setting addresses the class, not the instance; thus it is hard coded and has a coarse granularity.
The cascading ability exists but is limited to three hard-coded levels (user, L&F, and system L&F). The UIManager is not sufficient to achieve the same styling level as for Web pages, which is why it makes sense to use an external styling mechanism to style Java objects.
Styling Mechanism Requirements
The styling module represents an add-on of the application. It can add a lot of value at very low cost. More than ever, it should interfere as little as possible with the host application. The styling module implementation should be reliable, efficient, and have a small footprint.
Developers will be tempted to bypass the styling module and handle the customization of the UI manually, which may happen if the module looks complex. The module should then offer an easy access and be scalable. Scalability means that the use of the module is not reduced to a couple of rules and a small UI, in which case direct coding seems more sensible than investing in the styling module. CSS offers an easy access because of the simplicity of the language and is scalable with the power of the selectors and the inheritance mechanism.
The styling mechanism should always conform to this principle: "For this input value, in this context, set these rendering properties." The context is given at runtime, for example, when a UI element is selected. There is no problem expressing CSS rules that depend on runtime context at authoring time.
CSS for Java
Sometimes the needs to customize the Java UI components are similar to those of Web pages. Users do not want to pollute their business data with rendering information. On the other hand, users may appreciate being able to control fine customization of the UI according to the business data. For example, a table basically displays rows and columns, but it could also use different colors to highlight cells corresponding to linked data.
Styling is implemented with a CSS engine that operates on a CSS model. It defines a hierarchical structure of objects and some accessors that may be used by the selectors to match a pattern. The CSS model is generated from the business data, mostly automatically: the hierarchical structure of the CSS model is mapped to the hierarchical structure of the Swing model (for example, the JTree model keeps the same tree structure, the JTable model remains flat). The syntax of selectors is close to the true CSS. Attribute matching performs string comparisons, but it has been extended to support arithmetic comparisons as well.
Depending on the business data format, accessors may be directly available or may need some coding. For instance, when the business data are Java Beans, the accessors of the model are the getters of the Beans. Again, Swing models like a TreeModel do not provide accessors to get the values from the business data, so it is necessary to make these accessors available to the CSS engine through the CSS model.
When a rule matches a model object, the declarations of the rule are applied to the graphic component that represents this object. The declarations are beans property settings. A declaration like:
background : red;
has the effect of setting the value red to the background property of the target component. Using a bean makes things a lot easier. The bean mechanism is a well-known Java standard. The BeanInfo class contains all the exported properties with the setter methods ready to be called and even a PropertyEditor to convert the string value to an object of the class accepted by the setter.
When the property does not exist or a value cannot be converted correctly, the declaration is silently ignored. CSS for Java works in three steps:
- Style sheets are parsed once.
- Rules are matched against the CSS model objects.
- For each model object, declarations are applied to a bean that represents the object. When a new style sheet is set, steps 1, 2, and 3 are invoked again. When a model object has changed, steps 2 and 3 are invoked. When the bean representing an object has changed, only step 3 is invoked.
CSS for Java offers other services:
-
Dynamics. Any change in the model or in the style sheets updates the view to reflect the change.
-
Global options. The property settings work on any bean even if it is not related to the UI. Typically, the CSS can customize a bean that represents global options and controls the behavior of the application.
-
Creation on the fly. Under certain conditions, the CSS engine can create on the fly a bean whose class and property values are described in the style sheet itself. This is convenient for specifying a real object in the right part of a declaration instead of a string value that may be difficult to convert to the proper type (for example, an object of type java.util.Date).
-
Logging. The set of applied property settings are recorded in a data structure.
The CSS engine has an API to get/set the style sheets, match rules, and apply declarations to target beans. The stylable components wrap the Swing component and apply the CSS engine whenever needed, that is, when a new style sheet is applied or when the model has changed. Then the Swing component is updated with new rendering values. As an example, we will examine a Swing JTable enhanced with the CSS engine.
A stylable JTable is basically a Swing table. The styling controls the graphical rendering of rows, columns, and cells. The TableModel is backed by a ListModel, where the objects of the list correspond to the rows, and the properties of the objects correspond to the columns. For example, we have a table with a list of customers, and each customer has three properties: name (type: String), quantity (type: int), and updated (type: boolean).
Now, let's define the style sheet to customize the appearance of the table. The first rule sets global properties for the table: the default font and the background color. Colors can be specified as RGB values, like 12,38,240, or as symbolic names recognized by SVG (see www.w3.org/TR/SVG/types.html#ColorKeywords). There is no special relationship with SVG, but color names are more readable than RGB values. SVG defines more than 100 color names. SVG color names are converted to Java Color objects using a custom global PropertyEditor.
table {
font : "sanserif,bold,12";
background : bisque;
}
Three rules customize the three columns. The selector column:quantity matches all columns named "quantity", that is, the Quantity column. The header property sets the title of the column. The foreground and background properties control the rendering colors of the column. The property type of the "updated" column is boolean. The renderer property sets a custom CellRenderer to a class that displays a checkbox for this column instead of the text true or false. These three rules are static, as they do not depend on business data.
column:name {
header : "Customer Name";
foreground : darkslategrey;
}
column:quantity {
header : "Quantity";
background : moccasin;}
column:updated {
header : "Updated ?";
renderer : "BooleanRenderer";
background : tan ;
}
The remaining rules manage customization according to the model value. All rows with an updated value of false will have the color goldenrod in the updated column. [updated='false'] matches the false value in the model, and :updated limits the scope of the declarations to the "updated" column. If we omit :updated, then the whole row is affected by the goldenrod color.
row[updated='false']:updated {
background:goldenrod;
}
The rows with a quantity greater than or equal to 300 have a different color in the name column.
row[quantity>=300]:name {
background:darksalmon;
foreground:black;
}
The last two rules manage the font color in the "quantity" column according to the quantity value.
row[quantity>=200]:quantity {
foreground:darkturquoise;
}
row[quantity>=300]:quantity {
foreground:cadetblue;
}
Let's take this data sample:
| Name |
Quantity |
Updated |
| "Bauer" |
232 |
True |
| "Decker |
211 |
False |
| "Kowalski" |
183 |
True |
| "Fergusson" |
313 |
False |
| "Smith" |
300 |
True |
| "Reed" |
253 |
True |
|
Here is the result (see Figure 3):
All the styles are applied, in particular:
- The column headers are the ones specified in the style sheet.
- The three columns have a different color for better readability.
- Customer names with a quantity over 300 are highlighted.
- Nonupdated rows are also highlighted.
- Quantity values have a color according to their value.
- The Updated column has a custom renderer (the checkbox).
Obviously, styling makes more sense for a larger table.
Because CSS is dynamic, you can use styles for data mining purposes. Assume we want to highlight all customers with a quantity over 250 AND who are not updated. All we have to do is load a new style sheet with the first four rules plus this one:
row[quantity>250][updated=
'false'] {
background : navy;
foreground : tan;
}
We obtain this table (see Figure 4).
This table highlights only the customers matching the requested condition. Note that this is different from selection. The table displays all the rows that fulfill the conditions with the given colors. However the styling can influence the selection colors as well. By appending :selected to a selector, the rule applies only to selected rows that match the remaining conditions of the selector.
AND conditions are implemented by appending the conditions in the same selector, whereas OR conditions are implemented with one CSS rule per condition.
A word about performance: the styling overhead is very small. The style sheets are parsed once; they are matched only at the beginning and upon property changes. They are applied at will.
Styling Other Graphic Components
Most Swing components can be styled as we did with the JTable. However, the added value becomes more obvious as the components get more complex. This is why Table, Tree, and List are good candidates. An implementation of CSS for Java has been performed in a commercial product, ILOG JViews, a framework for displaying 2D graphics like charts, maps, and graphs. The 2D rendering engine reads input from XML files and is driven by CSS, which permits the display of data virtually without writing a line of Java code. To try a stylable workflow monitoring applet built on ILOG Jviews, go to www.ilog.com/products/jviews/demos/wf-monitor/index.cfm.
Benefits of CSS Styling
Styling not only makes the UI more attractive, it also provides more readable and more relevant rendering. It can even become a graphic tool to dig into data and to highlight the data that verifies simple conditions, although it is not the main purpose of styling. Sometimes users do not need a full data mining system, and this simple graphic filtering is good enough to help find particular items in a table quickly. And it comes free with the styling mechanism.
Further, the tuning of a UI—that is, finding the right colors, fonts, and other graphic attributes, is usually time-consuming. With CSS, the design loop is faster:
- Customizations of objects are gathered in a style sheet (or several style sheets).
- The syntax of CSS for Java is close to the true CSS, which speeds up the learning curve.
- Tuning the UI is simplified by the dynamicity of the implementation. No need to compile or restart the application to test a change in style, because the style can be changed while the application is running. The UI will be updated automatically.
Bug tracking: the styling system is harmless. If a bug occurs, it can only happen within the bean setter invocation. The CSS engine reports this kind of error to the developer. Of course, all the other advantages of the true CSS are preserved, like cascading, inheritance, powerful pattern matching that make efficient and compact rules, and so on.
UIs remain the favorite application domain of styling, because they provide the most spectacular result. However, the technology serves other purposes as well, since it basically applies property settings to beans. For instance, it works nicely for the configuration of objects, like setting the preferences of an application or its modules.
Styling the UI enhances the MVC pattern and separates the business data from the UI rendering. CSS have been designed for this purpose. Their success in the Web page authoring domain proves how mature they are. However, CSS technology appears to be suitable for styling Java components as well. A proprietary styling system could probably do the job, too, but reusing a standard such as CSS, even if it needs tweaking, speeds up the learning curve to master this module of customization. For the user, the style sheets applied to customize Java UIs improve the quality of the information.
About the Author
Philippe Kaplan, Ph.D., works for ILOG, where he specializes in software tools and user interfaces. He's been using Java for as long as the language has been available. You can reach him at .
|