I'm writing a custom swing component (something completely new, but think JTree or JList). I'm trying to follow the general design of JTree, JTable, JList etc for consistency (I've also seen various poor 3rd party components abandon the separable model and/or renderer approach).
So, I have a model full of nodes, the component itself and a renderer. At some point the node has to be turned into text and displayed by a renderer. I'm not clear on the best way to do this:
- Pass the node itself (as Object) to the renderer, and let the renderer decide how to display it.
- This is how JList does it.
- Requires a customised renderer just to change the text.
- Allows great flexibility in how to display the node (doesn't even have to be text).
- Pass the node itself (as Object) to the renderer, but have a convertValueToText() method in the component class.
- This is how JTree does it.
- Renderers can be just as flexibile as before - don't have to use this method.
- Have to override component to change the text transformation.
- As above, but delegate convertValueTotext() to the model.
- This is how JXTable does it.
- The model is probably the best place for this method - and it's easier to override there.
I don't want to have to customise the renderer just to change the text, but I'd like to be able to customise the renderer to do more than display a model-displayed string (else why bother with renderers). I really don't like the fact that JXTable uses reflection to look for convertValueToText() in the model - this smells of bad magic to me.
Can anyone shed any light on this oft-neglected part of Swing?
SOLUTION
What I ended up doing was this:
- Add a method to the model which returns a string for the given node. Importantly, this can be null to indicate that the renderer should know what to do or that we simply can't provide anything useful.
- The component has the same method, and passes the call on to the model. This is important for view-model separation. The renderer calls this method, so it doesn't talk to the model directly.
- The default renderer calls the above method and if it's not null, it uses it, otherwise it can call toString on the value, or provide a default, or whatever.
This leaves developers a choice when they want to override the displayed value - Override the method with a non-null return value knowing that the default renderer will display this text. - Provide a custom renderer which is passed the actual node object so it can do "clever" things if it needs to.
I'm quite happy with it - it "feels" right, it works, and it's easy to use.
Thanks for your perspectives!