Structuring Java code that assembles a UI
I’m dong some GWT coding today, which is a lot like writing Swing. It’s very procedural. Create a new UI element, then call methods on it to set up the state. Even when you do the right thing and split your UI into many Composite classes, you get something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | StackPanel stackPanel = new StackPanel(); stackPanel.add(new Label("Recently Eaten")); stackPanel.add(new Label("Manual Entry")); stackPanel.add(new Label("Search the database")); DockPanel panel = new DockPanel(); panel.add(stackPanel, DockPanel.CENTER); FlowPanel buttonsPanel = new FlowPanel(); buttonsPanel.add(new Button("Done")); buttonsPanel.add(new Button("Next")); panel.add(buttonsPanel, DockPanel.SOUTH); |
This is really annoying. The order of assembly is important, but it’s also very arbitrary. I’m creating my DockPanel before my FlowPanel, but you should always declare variables in the narrowest scope possible (item 45 in Effective Java). On the other hand, maybe I want to finish referring to my StackPanel, adding it to the parent, before I create my FlowPanel. Also, it’s easy to make a mistake here. I might accidentally add something to the wrong parent container, if the variable names are similar, and there isn’t a good way to test this code aside from inspecting the UI visually.
Maybe Java is just not a good language to express this UI layout, but for now I considered an under-used construct, inline instance initialization blocks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | final StackPanel stackPanel = new StackPanel() {{ add(new RecentlyEatenPanel(), "Recently Eaten"); add(new ManualEntryPanel(), "Manual Entry"); add(new DatabaseSearchPanel(), "Search the database"); }}; final FlowPanel buttonsPanel = new FlowPanel() {{ add(nextButton); add(doneButton); }}; DockPanel panel = new DockPanel() {{ add(stackPanel, DockPanel.CENTER); add(buttonsPanel, DockPanel.SOUTH); }}; |
This is kind of cool - the structure is certainly more evident in this code. The extra braces define an initializer that is copied into each constructor - see the Sun tutorial. I assume that the code in the initializer block executes after the constructor has finished its work, so we can depend on the add() method having a place to do its work.
The limitations: this creates a subclass, so you can’t use it to initialize fields in a final class. Also, as you see, any variables must be declared final to be used in the initializer block, just like with any anonymous inner class.
Links:
Here’s a clever DSL to build the GUI in a “fluent” way: http://www.pathf.com/blogs/2007/09/expressing-rich/
And a project to use Flex-style XML to configure the layout: http://code.google.com/p/gwt-ui/
No comments yet.
Leave a comment
About Me
Tweets
- attn:ladies re:why do men like to buy tools so much? That's just our way of nesting. 1 day ago
- Finally running my first MapReduce in production! I wonder what it's doing... ? 1 day ago
- Finally running my first MapReduce in production. I wonder what it's doing?? 1 day ago
- Have been informed of the location of a $13k espresso machine on campus. Time to take my coffee making skills to the next level. 3 days ago
- We need to get someone in academia to run a study comparing Qwerty and Dvorak, and do it right. The world deserves a decent unbiased answer. 4 days ago
- Today garden harvest: giant zuchinni, kohlrabi, a nice cucumber, an onion and a carrot and 3 artichokes http://yfrog.com/7gfu2dj 1 week ago
- The DC metro system is pretty messed up when you can discern there's a pattern of Red line trains that crash by subduction. #plate_tectonics 1 week ago
- That's the first time I was moved to leave a bar because the bitrate of the music was way too low. 1 week ago
- More updates...
Powered by Twitter Tools.