The Source for Java Technology Collaboration
User: Password:



Scott Violet's Blog

December 2006 Archives


Extreme List View

Posted by zixle on December 18, 2006 at 08:39 AM | Permalink | Comments (16)

In my last blog we finally released the source for this years Extreme GUI Makeover talk; hooray! There are a number of aspects of the app that are worth exploring. For this blog, I want to explore how the extreme list view was done. What I'm calling the extreme list view can be seen in the following screen shot:

As you may be able to surmise from the screen shot, and the name, the extreme list view is nothing more than a JList with a custom ListCellRenderer. The ListCellRenderer is implemented as a JPanel, that uses GroupLayout to achieve the desired effect.

If you look at the layout of each cell, you'll notice the layout is nearly a grid, but not quite. The first row has two columns, the second row one column, and the third row two columns. The reason this doesn't fit well with a grid is that the second columns of the first and last row shouldn't have the same size. Most grid based layout managers enforce all columns to have the same size. I've no doubt there is some grid based layout managers that does this, but not GridBagLayout. Of course you could always implement this as nested panels, but that should be avoided when possible!

GroupLayout does not enforce the components be in a grid, although grid based layouts are certainly possible with GroupLayout. As such, GroupLayout is particularly well suited for this layout, and it gave me another chance to play with GroupLayout. Here's how this layout is implemented in terms of GroupLayout. For those wishing to learn more about GroupLayout before getting into the nitty gritty, we've just updated the Swing Tutorial to include coverage of GroupLayout, which can be found here.

GroupLayout treats each axis independently. Looking at the horizontal axis, visually you can see space needs to be provided for the image, followed by three rows of text. This translates to:

(IP + rows)
Which equates to a sequential group with the image component, followed by the rows. As the rows are positioned in the same space, horizontally, the components in each row need to be placed in a parallel group:
(I + [R1 R2 R2])
I'm using parens to denote sequential groups, and brackets to denote parallel groups. The first and third row consist of two components each for the differing labels. As the labels in each are placed one after another, a sequential group is used:
(I + [(S + C0) C1 (C2 + F)])
The key for this layout is that all extra space should go to the subject and content text. This is achieved by explicitly specifing the subject and context text have a minimum and preferred size of 1, and a maximum size of Integer.MAX_VALUE:
  addComponent(component, 1, 1, Integer.MAX_VALUE);
All other components are forced to use their preferred size for the min/pref/max. As JLabels already do this, you can use the single argument addComponent, or explicitly request this behavior with the following code:
  addComponent(component, PREFERRED_SIZE, PREFERRED_SIZE, PREFERRED_SIZE);
Viewed graphically, this looks like:

Here's the complete code for creating the horizontal grouping:

  GroupLayout.SequentialGroup hg = layout.createSequentialGroup();
  layout.setHorizontalGroup(hg);
  // Add the image panel with a fixed size
  hg.addComponent(imagePanel, IS, IS, IS).
    // Create parallel group that will contain the rows
    addGroup(layout.createParallelGroup().
      // First row contains the subject, and date labels
      // Notice the subject is infinitely resizable
      addGroup(layout.createSequentialGroup().
        addComponent(subjectLabel, 1, 1, Integer.MAX_VALUE).
        addComponent(dateLabel)).
      // Second row is a single label that is infinitely resizable.
      addComponent(labels[0], 1, 1, Integer.MAX_VALUE).
      // Third row contains two labels: text and from. The label
      // is infinitely resizable, where as the from label is fixed
      // at its preferred size.
      addGroup(layout.createSequentialGroup().
        addComponent(labels[1], 1, 1, Integer.MAX_VALUE).
        addComponent(fromLabel)));
The only other trick to mention when using a JList like this is be absolutely sure to set the prototype cell value. If you don't, JList is going to query the renderer for the preferred size for each and every value in the list. As you can imagine, a layout like this is NOT cheap! Additionally each cell should have the same size, so specifying the protoype value is the way to go!

I'm not going to go through the code for the vertical axis, hopefully it's not that hard to grok given the walk through of the horizontal axis.

Here's the complete source for the app.

Enjoy!

    -Scott



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds