Step 3 Writing a Custom Renderer


Step 2 — Swing’s Separable Model Architecture

In this next version of MainFrame, we will add a shuffled list of Garments to a JList at the left side of the window. JList is an example of what is known as Swing’s separable model architecture which is loosely based on MVC (model-view-controller) architecture. We will gain a deeper understanding of JList and how it uses its data model, ListModel. Also, just to make the program a bit more fun at this stage (and the image more discrete), we’ll have ListSelectionEvents trigger the DressingBoard to draw selected Garments on the boy.

Using a ListModel

One of the differences between AWT’s and Swing’s approaches to GUI architecture is that Swing employs a separable model architecture while the AWT does not. The difference between Swing’s JList and AWT’s List is a prime example of this.

If you look at the API for java.awt.List, you will see many methods for managing the data it contains. It provides methods for adding items, removing items and replacing items, for instance. Unfortunately, it requires that the items be Strings. As a result of the tight coupling between List and its data, if you use a List in your program, you are committed to representing your data as a list of strings and to giving the List sole ownership over it.

Let’s see what implications the use of a List would have on this chapter’s program. Our application’s data is a list of Garments. In order to use a List, however, the application would have to maintain a list of strings for the List’s benefit as well as the list of Garments. This means that at any time the list of Garments changed, the application would have to make sure that List’s list of Strings was changed accordingly, or application data and view would be out of sync. This could be a maintenance headache.

JList, on the other hand, provides no methods for managing data. It maintains view functionality but delegates data management to a separate Object, a ListModel. Hence the term separable model. ListModel is an interface that declares a bare minimum of methods to manage a read-only list of data and a set of ListDataListeners. Unlike List, it requires only that the data be comprised of Objects, thus freeing the application to maintain its data however it wishes. Any object implementing the ListModel interface can be shared with any number of JLists through JList’s getModel() and setModel() methods. Futhermore, since JList automatically registers a ListDataListener with its ListModel, it is notified whenever an invocation of the ListModel’s methods changes the data. Table 14-5 lists the methods defined by the ListModel interface. Table 14-6 lists the methods available to JList for manipulating its ListModel.

Table 14-5: ListModel Methods

Method Name and Purpose

public int getSize()

Returns the number of elements in the list model.

public Object getElementAt(int index)

Returns the element at the specified index in the list model.

public void addListDataListener(ListDataListener l)

Called automatically when this list model becomes the component’s data model.

public void removeListDataListener(ListDataListener l)

Called automatically when this list model is no longer the component’s data model.

Table 14-6: JList’s ListModel Methods

Method Name and Purpose

public ListModel getModel()

Gets the current ListModel.

public void setModel(ListModel model)

Installs the specified ListModel.

public void setListData(Object[] listData)

Installs a new ListModel containing the specified data.

public void setListData(Vector listData)

Installs a new ListModel containing the specified data.

Let’s see what implications the use of a JList would have on this chapter’s program. Our application could maintain a list of Garments wrapped in a ListModel interface and share it with as many JLists as it wanted. This means that with no further effort, all JLists would be guaranteed to be in sync with the underlying data. This is definitely a better scenario.

The MainFrame Class

In this step (gui1 package) MainFrame creates, initializes and manipulates the JList’s ListModel. In the initList method (lines 44 - 79), it creates a Vector of Garments, shuffles it and passes it to the JList, which will create a ListModel for itself containing the Garments in the shuffled order. This book has not yet formally covered the Vector class or the Collections utility class used here but if lines 70 - 77 are not self-explanatory, a quick look into the javadocs for those classes should suffice to explain them. For more information on Collections in general please refer to Chapter 17 — Collections.

MainFrame also implements ListSelectionListener and registers itself with the JList so that any change in the JList’s selected items will trigger a call to MainFrame’s valueChanged() method. The valueChanged() method translates the JList’s selection state into the ListModel’s data by setting all Garments in the ListModel to be worn if and only if they are selected in the JList. The next logical step is factored into a new method, redrawBoy(), which is not dependent on the selection state of the JList so that, if we ever need to redraw the boy for other reasons, we will have a convenient method that doesn’t require a ListSelectionEvent parameter. RedrawBoy() obtains all the garments from the ListModel and creates an array of worn garments which it passes to DressingBoard.setOrder(). The call in line 90 to order.toArray() takes advantage of a feature of Collections which will be discussed later. Simply pass in an array of the appropriate type to the toArray() method. It will return an array of that same type containing every element in the collection. Example 14.5 gives the code for the modified MainFrame class.

Example 14.5: chap14.gui1.MainFrame.java

image from book
 1     package chap14.gui1; 2 3     import java.awt.BorderLayout; 4     import java.awt.Container; 5     import java.awt.Image; 6     import java.awt.Point; 7     import java.util.Collections; 8     import java.util.Vector; 9 10    import javax.swing.BorderFactory; 11    import javax.swing.JFrame; 12    import javax.swing.JList; 13    import javax.swing.ListModel; 14    import javax.swing.border.BevelBorder; 15    import javax.swing.event.ListSelectionEvent; 16    import javax.swing.event.ListSelectionListener; 17 18    import utils.ResourceUtils; 19    import chap14.gui0.DressingBoard; 20    import chap14.gui0.Garment; 21 22    public class MainFrame extends JFrame implements ListSelectionListener { 23 24      private DressingBoard dressingBoard; 25      private JList garmentList; 26 27      public MainFrame() { 28        setTitle(getClass().getName()); 29        setSize(600, 400); 30        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 31 32        Container contentPane = getContentPane(); 33        contentPane.setLayout(new BorderLayout()); 34        dressingBoard = new DressingBoard(); 35        contentPane.add("Center", dressingBoard); 36 37        garmentList = new JList(); 38        garmentList.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); 39        garmentList.addListSelectionListener(this); 40        contentPane.add("West", garmentList); 41 42        initList(); 43      } 44      private void initList() { 45        String[] names = 46          { 47            "T-Shirt", 48            "Briefs", 49            "Left Sock", 50            "Right Sock", 51            "Shirt", 52            "Pants", 53            "Belt", 54            "Tie", 55            "Left Shoe", 56            "Right Shoe" }; 57        Point[] points = 58          { 59            new Point(75, 125), 60            new Point(86, 197), 61            new Point(127, 256), 62            new Point(45, 260), 63            new Point(69, 118), 64            new Point(82, 199), 65            new Point(88, 203), 66            new Point(84, 124), 67            new Point(129, 258), 68            new Point(40, 268)}; 69 70        Vector garments = new Vector(names.length); 71        for (int i = 0; i < names.length; ++i) { 72          Image image = 73            ResourceUtils.loadImage("chap14/images/" + names[i] + ".gif", this); 74          Garment garment = new Garment(image, points[i].x, points[i].y, names[i]); 75          garments.add(garment); 76        }  77        Collections.shuffle(garments); 78        garmentList.setListData(garments); 79      } 80      private void redrawBoy() { 81        ListModel lm = garmentList.getModel(); 82        int stop = lm.getSize(); 83        Vector order = new Vector(); 84        for (int i = 0; i < stop; ++i) { 85          Garment garment = (Garment)lm.getElementAt(i); 86          if (garment.isWorn()) { 87            order.add(garment); 88          } 89        } 90        dressingBoard.setOrder((Garment[])order.toArray(new Garment[0])); 91      } 92      public static void main(String[] arg) { 93        new MainFrame().setVisible(true); 94      } 95      public void valueChanged(ListSelectionEvent e) { 96        ListModel lm = garmentList.getModel(); 97        for (int i = lm.getSize() - 1; i >= 0; --i) { 98          Garment garment = (Garment)lm.getElementAt(i); 99          garment.setWorn(false); 100        } 101 102        Object[] selectedGarments = garmentList.getSelectedValues(); 103        for (int i = 0; i < selectedGarments.length; ++i) { 104          Garment selectedGarment = (Garment)selectedGarments[i]; 105          selectedGarment.setWorn(true); 106        } 107        redrawBoy(); 108      } 109     }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

      javac –d classes -sourcepath src src/chap14/gui1/MainFrame.java      java –cp classes chap14.gui1.MainFrame

Run the program and take time to play with it. Make single and multiple selections from the list and notice the effect each has on the drawing of the boy. Now comment out the toString() method of Garment, recompile and run the program again to observe the effect this has on the program. By default, JList uses a JLabel to render each list item and it sets the text of the JLabel to whatever toString() returns. We’ll discuss renderers in the next section.

Quick Review

Swing’s separable model architecture creates a loose coupling between a Component and the data it represents by defining interfaces with which the Component interacts, thus freeing an application to manage its data as it wishes. As long as the data is wrapped in the appropriate interface, the Component is automatically notified of changes in the data without any special effort on the part of the programmer.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net