3

I'm creating a custom component with its own custom UI. The question How to create a custom Swing Component provided a great start in developing the component, but I haven't been able to find a good way to connect the custom component with its UI.

The code in How to Write a Custom Swing Component is good, but the implementation of JFlexiSlider.updateUI hard codes the UI to use BasicFlexiSliderUI:

public void updateUI() {
    if (UIManager.get(getUIClassID()) != null) {
        setUI((FlexiSliderUI) UIManager.getUI(this));
    } else {
        setUI(new BasicFlexiSliderUI());
    }
}

The standard Swing components implement updateUI to simply set the UI directly from UIManager.get(this). The mapping from the UIClassID to the actual implementation is in BasicLookAndFeel and subclasses.

In my case, where I don't want to create a new look and feel, where would I set the default mapping from my UIClassID to the actual implementation? It seems like it should be outside the component class, but it needs to be added to the UIDefaults object prior to the first use of the custom component.

Community
  • 1
  • 1
Rangi Keen
  • 935
  • 9
  • 29
  • 2
    For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Jul 22 '13 at 22:36
  • This is a hard problem. There is no good place to put your own UI. If you try to put it in the UIManager, your code doesn't work so well as a self contained library. If you don't, they can't use it with different looks and feels so easy. – Lee Meador Jul 22 '13 at 22:39

2 Answers2

3

The essential collaborators:

  • the custom component which must report a unique uiClassID (in getUIClassID)
  • a custom ui delegate
  • the UIManager is the place to map the custom uiClassId to the ui delegate class (which it is expected to use for the custom component) before instantiating the component

In code something like:

/**
 * The custom component
 */
public class Block extends JComponent {

    private final static String ID = "BlockUI";

    public Block(Color color) {
        // configure
        setBackground(color);
        // install ui
        updateUI();
    }


    @Override
    public void updateUI() {
        // ask the ui manager for an appropriate ui and set it
        setUI(UIManager.getUI(this));
    }

    /**
     * Implemented to return a unique component class identifier
     */
    @Override
    public String getUIClassID() {
        return ID;
    }

}

/**
 * For each supported LAF, a laf-specific implementation
 */
public class BasicBlockUI extends ComponentUI {

    public static ComponentUI createUI(JComponent c) {
        return new BasicBlockUI();
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        g.setColor(c.getBackground());
        g.fillRect(10,10, c.getWidth()- 20, c.getHeight()-20);
    }

}

// usage

//let the ui manager know of our component
// the value must be the fully qualified classname
UIManager.put("BlockUI", "org.jdesktop.swingx.plaf.BasicBlockUI");

JXFrame frame = new JXFrame("custom box", true);
Block block = new Block(Color.RED);
frame.add(block);
frame.setSize(200, 200);
frame.setVisible(true);

If you want to support several LAFs, you might have a look into a recent QA which illustrates the SwingX mechanism to plug-in custom components.

Community
  • 1
  • 1
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Where would the call to `UIManager.put("BlockUI", ...)` typically be made? If you have a large project with multiple separate libraries, you couldn't easily do this from one central location in the application. – Rangi Keen Jul 24 '13 at 13:06
  • the same place where you would set the LAF, somewhere "early" – kleopatra Jul 24 '13 at 13:09
  • @RangiKeen anyway, the easier solution is the swingx mechanism: with it in place you simply let the custom component contribute itself, no need for any manual adding :-) – kleopatra Jul 24 '13 at 13:17
  • Thanks @kleopatra. I'll look into using the swingx mechanism. I see in `JXTaskPane`, for example, there's a static initialization `LookAndFeelAddons.contribute(new TaskPaneAddon());` which seems reasonable. I also like the use of the `swingx/` prefix for the `UIClassID` value. – Rangi Keen Jul 24 '13 at 15:23
  • glad you like it :-) though: the prefix is ... (inconsistently) arbitrary and maybe even violating conventions, we could never really decide how to name those. – kleopatra Jul 25 '13 at 06:33
0

You can use setUI to set the UI for a component.

tbodt
  • 16,609
  • 6
  • 58
  • 83
  • That is true but my question is really about how to determine the mapping from the `UIClassID` to a concrete implementation appropriate for the current look and feel. This was in the content, but the title was misleading so I've update it to make this more clear. – Rangi Keen Jul 23 '13 at 12:41