HE Dapter Attern: Moving Data Between Lists
HE Dapter Attern: Moving Data Between Lists
In this simple program, you enter names into the top entry field and
click on Insert to move the names into the left-hand list box. Then, to move
names to the right-hand list box, you click on them, and then click on Add.
To remove a name from the right hand list box, click on it and then on
Remove. This moves the name back to the left-hand list.
This is a very simple program to write in Java 1.1. It consists of a
GUI creation constructor and an actionListener routine for the three buttons:
public void actionPerformed(ActionEvent e)
{
Button b = (Button)e.getSource();
if(b == Add)
addName();
if(b == MoveRight)
moveNameRight();
if(b == MoveLeft)
moveNameLeft();
}
The button action routines are then simply
private void addName()
{
if (txt.getText().length() > 0)
{
leftList.add(txt.getText());
txt.setText("");
}
}
//--------------------------------------------
private void moveNameRight()
{
String sel[] = leftList.getSelectedItems();
if (sel != null)
{
rightList.add(sel[0]);
leftList.remove(sel[0]);
}
}
//--------------------------------------------
public void moveNameLeft()
{
String sel[] = rightList.getSelectedItems();
if (sel != null)
{
leftList.add(sel[0]);
rightList.remove(sel[0]);
}
}
This program is called TwoList.java on your CD-ROM.
4
Both classes have quite a number of other methods and almost none
of them are closely correlated. However, since we have already written the
program once, and make use of two different list boxes, writing an adapter to
make the JList class look like the List class seems a sensible solution to our
problem.
The JList class is a window container which has an array, vector or
other ListModel class associated with it. It is this ListModel that actually
contains and manipulates the data. Further, the JList class does not contain a
scroll bar, but instead relies on being inserted in the viewport of the
JScrollPane class. Data in the JList class and its associated ListModel are not
limited to strings, but may be almost any kind of objects, as long as you
provide the cell drawing routine for them. This makes it possible to have list
boxes with pictures illustrating each choice in the list.
In our case, we are only going to create a class that emulates the List
class, and that in this simple case, needs only the three methods we showed in
the table above.
We can define the needed methods as an interface and then make sure
that the class we create implements those methods:
public interface awtList {
public void add(String s);
public void remove(String s);
public String[] getSelectedItems()
}
5
Interfaces are important in Java, because Java does not allow multiple
inheritance as C++ does. Thus, by using the implements keyword, the class
can take on methods and the appearance of being a class of either type.
}
//-----------------------------------------
public void add(String s) {
listContents.addElement(s);
}
//-----------------------------------------
public void remove(String s) {
listContents.removeElement(s);
}
//-----------------------------------------
public String[] getSelectedItems() {
Object[] obj = listWindow.getSelectedValues();
String[] s = new String[obj.length];
for (int i =0; i<obj.length; i++)
s[i] = obj[i].toString();
return s;
}
}
Note, however, that the actual data handling takes place in the
JlistData class. This class is derived from the AbstractListModel, which
defines the following methods:
addListDataListener(l) Add a listener for changes in the
data.
6
The three fire methods are the communication path between the data
stored in the ListModel and the actual displayed list data. Firing them causes
the displayed list to be updated.
In this case, the addElement, removeElement methods are all that are
needed, although you could imagine a number of other useful methods. Each
time we add data to the data vector, we call the fireIntervalAdded method to
tell the list display to refresh that area of the displayed list.
class JListData extends AbstractListModel
{
private Vector data;
//-----------------------------------------
public JListData() {
data = new Vector();
}
//-----------------------------------------
public void addElement(String s)
{
data.addElement(s);
fireIntervalAdded(this, data.size()-1,
data.size());
}
//-----------------------------------------
public void removeElement(String s) {
data.removeElement(s);
fireIntervalRemoved(this, 0, data.size());
}
}
and so forth.
The class-based adapter is much the same, except that some of the
methods now refer to the enclosing class instead of an encapsulated class:
public class JclassAwtList extends JList
implements awtList
{
private JListData listContents;
//-----------------------------------------
public JclassAwtList(int rows)
{
listContents = new JListData();
setModel(listContents);
setPrototypeCellValue("Abcdefg Hijkmnop");
}
There are some differences between the List and the adapted JList
class that are not so easy to adapt, however. The List class constructor allows
you to specify the length of the list in lines. There is no way to specify this
directly in the JList class. You can compute the preferred size of the
enclosing JScrollPane class based on the font size of the JList, but depending
on the layout manager, this may not be honored exactly.
You will find the example class JawtClassList, called by
JTwoClassList on your example CD-ROM.
There are also some differences between the class and the object
adapter approaches, although they are less significant than in C++.
• The Class adapter
• Won’t work when we want to adapt a class and all of its
subclasses, since you define the class it derives from when you
create it.
• Lets the adapter change some of the adapted class’s methods but
still allows the others to be used unchanged.
• An Object adapter
• Could allow subclasses to be adapted by simply passing them in
as part of a constructor.
8
Pluggable Adapters
A pluggable adapter is one that adapts dynamically to one of several
classes. Of course, the adapter can only adapt to classes it can recognize, and
usually the adapter decides which class it is adapting based on differing
constructors or setParameter methods.
Java has yet another way for adapters to recognize which of several
classes it must adapt to: reflection. You can use reflection to discover the
names of public methods and their parameters for any class. For example, for
any arbitrary object you can use the getClass() method to obtain its class and
the getMethods() method to obtain an array of the method names.
JList list = new JList();
Method[] methods = list.getClass().getMethods();
//print out methods
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i].getName());
//print out parameter types
Class cl[] = methods[i].getParameterTypes();
for(int j=0; j < cl.length; j++)
System.out.println(cl[j].toString());
}
A “method dump” like the one produced by the code shown above
can generate a very large list of methods, and it is easier if you know the
name of the method you are looking for and simply want to find out which
arguments that method requires. From that method signature, you can then
deduce the adapting you need to carry out.
9
Adapters in Java
In a broad sense, there are already a number of adapters built into the
Java language. In this case, the Java adapters serve to simplify an
unnecessarily complicated event interface. One of the most commonly used
of these Java adapters is the WindowAdapter class.
One of the inconveniences of Java is that windows do not close
automatically when you click on the Close button or window Exit menu item.
The general solution to this problem is to have your main Frame window
implement the WindowListener interface, leaving all of the Window events
empty except for windowClosing.
public void mainFrame extends Frame
implements WindowListener
{
public void mainFrame() {
addWindowListener(this); //frame listens
//for window events
}
setSize(new Dimension(100,100));
setVisible(true);
}
static public void main(String argv[]) {
new Closer();
}
}
//make an extended window adapter which
//closes the frame when the closing event is received
class WindAp extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
You can, however, make a much more compact, but less readable
version of the same code by using an anonymous inner class:
//create window listener for window close click
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{System.exit(0);}
});
Adapters like these are common in Java when a simple class can be
used to encapsulate a number of events. They include ComponentAdapter,
ContainerAdapter, FocusAdapter, KeyAdapter, MouseAdapter, and
MouseMotionAdapter.