Question

I'm writing a custom JComponent, that should look differently for different look and feels. I intend to have different ComponentUi classes at least for the WindowsLookAndFeel, the MetalLookAndFeel and for the MotifLookAndFeel.

Now, while this task seems easy enough, I couldn't find out how to easily extend an existing look and feel with my custom ComponentUi classes.

How would I go about registering the correct ComponentUi for the different look and feels? Is this possible at all? If not, what is the preferred way to write a custom component for different look and feels?


To be a bit more specific, at the moment I'm overriding JComponent#updateUI in my custom component to set the different ComponentUi instances:

@Override
public void updateUI() {
    LookAndFeel feel = UIManager.getLookAndFeel();
    if (feel instanceof WindowsLookAndFeel) {
        setUI(MyWindowsCustomUi.createUI(this));
    } else if (feel instanceof MotifLookAndFeel) {
        setUI(MyMotivCustomUi.createUI(this));
    } else if (feel instanceof MetalLookAndFeel) {
        setUI(MyMetalCustomUi.createUI(this));
    } else{
        setUI(MyBasicCustomUi.createUI(this));
    }
}

But this approach seems to entirely defeat the purpose and usefulness of look and feels. I'd like to be able to change this to the following:

public void updateUI() {
    setUI((MyCustomUi)UIManager.getUI(this));
}

And this should set the correct subclass of MyCustomUi for the current look and feel.

I know, that I could easily achieve this by creating custom subclasses of each supported LookAndFeel, that register the corresponding ComponentUi during e.g. BasicLookAndFeel#initComponentDefaults(UIDefaults) - but this is not what I want to do.

Was it helpful?

Solution

If you want it or not - you have to register your custom UIs somehow with the UIManager, how else could it know about them ;-)

What you don't need, though, is a custom subclass of the supported LAFs: you can register them manually (and update the registration when the LAF is changed, that is you'll need a propertyChangeListener on the UIManager to be notified in such a case).

Assuming a JCustom with a classID of "CustomUI" and ui implementations following the usual conventions (that is BasicCustomUI, WindowsCustomUI ... ) the registration would be something like:

 String prefix = UIManager.getLookAndFeel().getID();
 UIManager.getLookAndFeelDefaults().put("CustomUI", myUIPackage + "." + prefix + CustomUI);

Note that the custom ui needs a static createUI which returns an instance of the ui:

 public static ComponentUI createUI(JComponent comp) {
     return new BasicCustomUI(); 
 }

and the component needs to publish its uiClassID, lookup and set its ui:

@Override
public String getUIClassID() {
    return "CustomUI";
}

@Override
public void updateUI() {
    setUI(UIManager.getUI(this));
}

The benefit of using SwingX is to provide the infrastructure to automagically register custom components. You'll need an additional class - CustomAddon - which provides the per-LAF configuration and the custom component has to contribute that addon:

// in JCustom

static {
    LookAndFeelAddons.contribute(new CustomAddon());
}

// in CustomAddon

@Override
protected void addBasicDefaults(LookAndFeelAddons addon, DefaultsList defaults) {
    super.addBasicDefaults(addon, defaults);
    defaults.add("CustomUI",
            "mypackage.BasicCustomUI");
}

@Override
protected void addMacDefaults(LookAndFeelAddons addon, DefaultsList defaults) {
    super.addMacDefaults(addon, defaults);
    defaults.add("CustomUI",
            "mypackage.MacCustomUI");
}

//... similar methods for all supported LAFs
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top