How to Write a Custom Look and Feel
Why Write a Custom Look and Feel?
There are four main reasons for writing a custom look and feel:
- The first reason, which has become very popular in the last few years, is native fidelity. Although much of this area has been covered by using native painting APIs in Windows and GTK core implementations in Mustang, there is still a large "feel" gap between Java applications and native applications. A considerable number of custom look and feels aim to address these problems: Looks' Windows and WinLAF for Windows, Quaqua for Mac, KDE, and Liquid and Lipstik for KDE. Skin can be considered a part of the last category as it's able to render KDE and GTK skins.
- The second reason is to provide a distinct look and feel for suites of products, which is usually a concern with commercial products. Most of these products are never released separately and are instead shipped as part of a product suite (such as SAP or Nexvu).
- The third reason is to enhance an existing look and feel, either by adding certain missing feature (like SmoothMetal, which adds global anti-aliasing support to all Metal delegates) or by sub-theming (such as the JDeveloper look and feel, which is built on top of Looks' Plastic with a custom theme). Synth-based look and feels can be roughly placed in this category. (Synthetica is currently the only complete example.)
- The fourth reason is to create a completely new look and feel from the scratch based on the author's (hopefully) unique design idea. An example of this kind of look and feel is Squareness, which provides a unique "square" touch to Swing applications.
Where to Begin?
First, go to the Look and Feel repository to see if your idea has already been implemented. If it has, or even if there's a look and feel that is "almost but not exactly" like yours, consider joining the effort (especially if it's an open source project and you're comfortable with its license). You may first need to establish yourself as a worthy addition to the developer team by becoming acquainted with the source code, submitting bug reports with (at least partially working) fixes, and suggesting new features and the ways to implement them. This approach may not be easy (as with any other open-source projects), therefore you may consider starting your own look and feel project.
If you decide to start you own look and feel, you have three main decisions to make: the target platform(s), the target VM, and the license. Since the latter issue doesn't belong in this article, I'll focus on the first two.
Although Swing by its nature is lightweight and cross-platform, you may decide to target a specific OS or toolkit and perhaps even use some native code. Some of the existing look and feels do this (such as WinLAF, Quaqua, and KDE), confining themselves to the particular platform. You should consider not only the advantages of this approach (easier path to native fidelity) but also the deployment and maintainability issues (the need to install native code somewhere in the path, what happens when the native API changes and you want to support multiple OS versions, and so on). On the other hand, you will need to test your code only on the platform of your choice.
The target VM issue is complicated as well. Most of the currently active look and feels require Java 1.4.2. (A noteworthy exception is Simple, which requires 1.1.6.) Some look and feels require 5.0 as a minimum JRE, as Tiger was officially released almost two years ago and is in reasonably common use. The blog entry Porting small library from Java 5.0 to Java 1.4 - could it be any harder? describes some of the reasons why Substance has chosen 5.0 as the minimum requirement. One of the more compelling reasons is the many bug fixes since 1.4.2, along with the more flexible API provided for the painting extensions. A major drawback of deciding to go with 5.0 is cutting off users who have to use 1.4.2 (or earlier) VMs.
Another equally important decision is whether your look and feel will be image-based or Java2D-based. While some look and feels (such as Quaqua, Liquid, and obviously Synthetica) use bundled images to paint the controls, this approach may prove to be a disadvantage if you're planning to support theming and large controls as that will cause image scaling artifacts to be visible. However, it may be very difficult to achieve the same level of image-based design precision in Java2D.
What You Should Know
Writing a look and feel is neither simple nor quick. There are forty-four different UI delegates, and you would extend most of them in a complete custom look and feel. The codebase for Basic delegates weighs in at 1.9 MB, and at one point or another you will have to become well-versed in almost all of it. The codebase for Metal delegates weighs in at an additional 680 KB, although if you're planning to provide a complete custom look and feel that doesn't use any painting or listening logic from Metal delegates, you don't need to extend Metal (as Alloy, Liquid, Napkin, Skin, and Substance do). In addition to writing your delegates, sometimes you'll be forced to fix bugs in the core delegates, especially if this action is requested by your users.
Ideally, you should be very well-versed in the following topics:
- Java2D painting, including composites, gradients, clipping, shapes, colors, and efficient image processing.
- Working with the Event Dispatcher Thread (EDT), multi-threaded environment (for example, for animation effects), timers, and avoiding deadlocks and synchronization issues.
- Knowing all existing Swing components and the different ways to use them, including existing component properties, events, models, and renderers.
- Swing listener approach, including proper listener registration/unregistration, and running listener code on EDT when appropriate.
- Swing models, including associated listeners, events, and custom/dynamic models.
- Internationalization support, especially if you're planning on introducing custom UI elements (for example, system menu elements). One of the more complicated topics in this area is right-to-left (RTL) support.
- Thorough knowledge of
UIDefaults, and the marker
UIResourceinterface. You can gain this knowledge by reading and debugging the code for the core delegates.
- Custom layout managers, which are employed very heavily throughout look and feel (for example, for custom title panes, spinners, and scroll bars).
Note that some of these areas can be learned while you're writing your look and feel. However, you shouldn't embark on writing a custom look and feel as your first Swing project; most probably you will be overwhelmed by the technical details. However, if you do persist in writing your own look and feel, you will learn a lot of invaluable information about how Swing works and how to use it correctly. Doing it all the way will surely make you an outstanding Swing expert.
Think about the developers who will be using your L&F.
- Clearly separate the supported API from internal implementation. Clever users who cast to one of your internal delegates and accesses an internal component (such as a closing button on a decorated title pane) should know that they are doing it at their own risk. A common approach is to provide the API in one class (maybe either your main LookAndFeel class or a separate Options class) and/or a list of supported client properties.
- Provide ample Javadoc comments and documentation on the officially supported API, client properties, and VM flags.
- Provide a complete test application that covers all available core components in different states. When you fix a defect that couldn't be reproduced in the test application, add the relevant scenario there. This way you will: provide sample code of correct usage of your API / client properties / VM flags; and you will have the complete coverage of all your code.
- Avoid lock-in as much as possible. Provide sensible default settings and allow an easy path to "upgrade" the application look and feel by using API/client properties/VM flags. When the application switches off your look and feel at runtime, be sure to not leave any visual trace of your look and feel behind.
- Preserve the API compatibility as much as possible, if you want to have a faithful user base. Unlike other libraries, a look and feel is expected to work correctly when a new version is dropped in the classpath instead of an old version (with no recompilation at all). If you break the API, provide a clear and complete migration guide.
Think about the users who will be using your L&F.
- Always be aware of memory leaks/memory retention. Constantly run your test applications in a profiler and see the memory/object count when you switch to a core look and feel. Beware of keeping pointers to application controls/models/renderers. Uninstall all listeners/controls that you add in the installation phase. Don't leave stray helper threads behind, especially when the last application window is disposed.
- The CPU is for the applications. You are not the application. If you have CPU-intensive painting, cache the results. (Use soft cache if necessary and clean your caches when your look and feel is switched out.) The goal is to have no more than 3-5 percent CPU when the application is idle and the user just moves the mouse around the screen.
- Listen to your users. Their requests are not whims. If a developer requests something (like an option to add custom buttons on decorated title panes), probably it's a real requirement in the application. Stay tuned to Internet Swing forums such as java.sun.com or JavaRanch forums. See what people are asking and consider including these features in your API.
- Treat user requests as top priority—higher than the features that you are planning yourself. If the feature implementation takes days instead of weeks, you will have gained a devoted tester and a longtime user. If you feel that the user request is not generic enough or requires too much work, explain the reasons why you have decided not to implement that feature.
- Constantly check that your look and feel is interoperable with core Swing look and feels (Metal/Ocean, Windows, Motif, GTK) and other third-party look and feels. By way of comparison, Joel on Software's Strategy Letter III: Let Me Go Back! describes how Microsoft eventually was able to make customers switch from Lotus to Excel, thanks to interoperability.
- Don't forget about RTL mode. Four RTL languages are supported in Java: Hebrew, Arabic, Farsi, and Urdu. See my blog entries Aligning menu items in Swing applications - welcome to the real world and RTL support in Swing - part II for a glimpse of RTL-related issues.
- Use lazy evaluation for creating expensive elements. For example, an application that doesn't use sliders shouldn't pay for creating the slider icons at the look and feel initialization time.
- Provide an option for theming your look and feel. Setting a theme should result in consistent painting of all UI elements. Provide an option for creating custom (third-party) themes.
Follow sensible programming practices.
- Study the code of other open-source look and feels. Compare the support for various corner cases in yours and other look and feels. Open defects if necessary and be polite. We are all living in the same community. A defect in other products doesn't necessarily mean that your product is better.
- Have a well-defined, short development cycle. Having releases every three to four months forces you to invest thought into putting in new features and will create a healthy feedback loop from the community.
- Test your look and feel on big Swing applications, such as NetBeans, JDeveloper, IntelliJ IDEA, JEdit, SwingSet2, Columba, and demos from other look and feels. Test your look and feel on all target OSs. Test your look and feel on multiple monitors and on different resolutions. Test your look and feel under different locales. Test your look and feel under different JVMs.
- Although visual compatibility is less important (especially if you change gradients/colors to achieve more pleasing visual design), be sure not to break the layout compatibility. Use extreme caution when changing settings for borders, insets, and margins. The same goes for changing implementation of internal layout managers.
- Reuse existing code. If you see something interesting that some other person wrote (even if it's under a different license), politely ask them to share or contribute their code; in most cases they will agree. Don't forget to thank them and put the contribution notes in the source code and on the project page. This goes not only for snippets found on people's blogs, but also for the code of other look and feels. The laf-plugin project provides generic plugin framework for look and feels and defines the interface of a common kind of plugin: the component plugin. The laf-widget project provides support for and a base set of additional behavior and widgets in look and feels.
Know the L&F-specific development issues.
- If you're planning to provide a complete look and feel, do not extend
Metal. This may seem an easier option at the beginning, but in the long run it incurs unnecessary overhead at startup and switch. If necessary, make you look and feel extend
Metal, but have your UI delegates extend
Basic. When you're done with all UI delegates, simply switch to
- For the most part, the
Basicdelegates provide excellent extension points for custom painting logic. If you find that you need to copy and paste sizeable chunks of code to override basic behavior, remember that you inherit all unknown or unsolved bugs.
- Follow the general
uninstallcycle of the core UI delegates. If you need to add a listener to the control, do it in
installListeners, keep a reference to it in your delegate, and unregister it in
- Never make assumptions on how the applications use controls, models, or renderers. A tab in the tabbed pane can be null, only to be filled when the user first selects that tab. Combobox mode (editable/non editable) can be switched based on the selected item. Table renderer can provide custom paint logic and ignore the opacity settings. A progress bar can be set to visible and have zero width.
- Stay updated on the latest OS trends (Windows, Mac, Linux / XGL) and the latest desktop applications (Firefox/extensions, IE, Shiira, OmniWeb, Opera, Thunderbird, Office). Constantly evaluate new widgets worthy of inclusion in your look and feel.
UIResourceis the single most important class for you. All entries in
UIManagerand all UI controls that you add should be instances of this interface.
- Beware of giving your listener members the same name as in basic delegates. This will leave the basic listener registered forever, resulting in memory leaks and unwanted control behavior.
Writing a look and feel provides many benefits. You will learn Swing thoroughly since you will be exposed to both its internals and how people use it (sometimes very inventively) in their applications. Unlike the average Swing programmer who is exposed only to the public API, you will be able to interact with the Swing API at a much lower level, having more powerful tools at your disposal. You will be able to give your in-house applications a distinct UI that goes far beyond simple theming.
This article describes many of the questions, issues, and problems that you will face during a look and feel development cycle.
- Official Java look and feel design guidelines
- Repository of existing look and feels
- Creating a Custom Look and Feel - article on creating the SAP look and feel
- Using Swing's Pluggable Look and Feel
|width="1" height="1" border="0" alt=" " />|