Step 6 A Good Component Gets Better


Interlude — Writing a Custom Editor

Looking back over the previous two steps (steps 4 and 5), what we did could be summed up as “making an editable list cell”. The renderer gave us the image of a checkbox but it took some effort to coax life into that image. Earlier it was pointed out that JTrees, JTables and JComboBoxes use custom renderers just as JList does. What wasn’t mentioned was that all of them except for the JList are editable and offer a mechanism for customizing editors that is similar to the mechanism for customizing renderers, and that greatly simplifies editing their underlying data models. This chapter’s main program doesn’t require an editable JTable, JTree or JComboBox, so in this brief interlude, we take a quick detour to look at the process for customizing JTree and JTable editors.

JTrees use TreeCellEditors. Table 14-9 shows JTree’s editor-related methods. JTables and TableColumns use TablecellEditors. Table 14-10 shows JTable’s and tableColumn’s editor-related methods.

Table 14-9: JTree’s Editor-Related Methods

Method Name

public void setCellEditor(TreeCellEditor editor)

public TreeCellEditor getCellEditor()

Table 14-10: JTable’s and TableColumn’s Editor-Related Methods

Defining Class

Method

JTable

public void setDefaultEditor(Class columnClass, TableCellEditor editor)

JTable

public TableCellRenderer getDefaultEditor(Class columnClass)

JTable

public TableCellRenderer getCellEditor(int row, int column)

TableColumn

public void setHeaderEditor(TableCellEditor headerEditor)

TableColumn

public TableCellEditor getHeaderEditor()

TableColumn

public void setCellEditor(TableCellEditor cellEditor)

TableColumn

public TableCellRenderer getCellEditor()

Both the TreeCellEditor and TableCellEditor interfaces extend from the CellEditor interface as shown in the class hierarchy in figure 14-9. Table 14-11 shows the methods that the CellEditor interface de nes.

image from book

     javax.swing.CellEditor        javax.swing.tree.TreeCellEditor        javax.swing.table.TableCellEditor

image from book

Figure 14-9: TreeCellEditor and TableCellEditor Inheritance Hierarchy

Table 14-11: javax.swing.CellEditor Methods

Method Name and Purpose

public Object getCellEditorValue()

Returns the value that the editor component contains.

public boolean isCellEditable(EventObject anEvent)

Returns true if the cell should be edited.

public boolean shouldSelectCell(EventObject anEvent)

Returns true if the cell should be selected.

public boolean stopCellEditing()

Call this to stop the editing process and accept the value contained in the editor component. This method returns false ifthe value could not be accepted.

public void cancelCellEditing()

Call this to stop the editing process and discard the value contained in the editor component.

public void addCellEditorListener(CellEditorListener l)

Called automatically when this CellEditor becomes a component’s editor.

public void removeCellEditorListener(CellEditorListener l)

Called automatically when this CellEditor is no longer a component’s editor.

The TableCellEditor interface extends CellEditor and defines one additional method:

 public Component getTableCellEditorComponent(JTable table,                                              Object value,                                              boolean isSelected,                                              int row,                                              int column);

The TreeCellEditor interface extends CellEditor and defines the additional method:

 public Component getTreeCellEditorComponent(JTree tree,                                             Object value,                                             boolean isSelected,                                             boolean expanded,                                             boolean leaf,                                             int row);

Both these methods must return a configured component that will handle the editing of the value parameter. In both cases, the first parameter is a reference to the faux-composite component itself. The second is the object (obtained from the data model) that is represented by the implicated cell. The remaining parameters identify the state and location of the implicated cell.

As an aid to writing a CellEditor, the javax.swing.AbstractCellEditor class implements CellEditor to provide default implementations of all the methods except for getCellEditorValue. Whether you need an editor for a JTable or a JTree, my recommendation is to extend AbstractCellEditor and implement either the TreeCellEditor or TableCellEditor interface as required. You will have to implement two methods: getCellEditorValue() and the one method declared by the additional interface. You may choose to override other methods if their default behavior is not appropriate. In particular, you might want to override the two methods isCellEditable() and shouldSelectCell() to return different values depending on which element of the data model is implicated by the EventObject. Determining which element is implicated by the EventObject takes a bit of work, though, as the getValueFromEvent() method of DemoTreeOrTableHandler class illustrates (lines 90 - 108 of example 14.12).

Example 14.12: chap14.interlude.DemoTreeOrTableCellHandler.java

image from book
 1     package chap14.interlude; 2 3     import java.awt.Color; 4     import java.awt.Component; 5     import java.awt.Point; 6     import java.awt.event.ActionEvent; 7     import java.awt.event.ActionListener; 8     import java.awt.event.MouseEvent; 9     import java.util.EventObject; 10 11    import javax.swing.AbstractCellEditor; 12    import javax.swing.JTable; 13    import javax.swing.JTextField; 14    import javax.swing.JTree; 15    import javax.swing.table.TableCellEditor; 16    import javax.swing.table.TableCellRenderer; 17    import javax.swing.tree.TreeCellEditor; 18    import javax.swing.tree.TreeCellRenderer; 19    import javax.swing.tree.TreePath; 20 21    public class DemoTreeOrTableCellHandler 22      extends AbstractCellEditor 23      implements TreeCellEditor, TableCellEditor, TreeCellRenderer, TableCellRenderer { 24 25      private JTextField rendererField; 26      private JTextField editorField; 27 28      public DemoTreeOrTableCellHandler() { 29        rendererField = new JTextField(); 30        rendererField.setBorder(null); 31        rendererField.setForeground(Color.blue); 32 33        editorField = new JTextField(); 34        editorField.setBorder(null); 35        editorField.setForeground(Color.red); 36 37        editorField.addActionListener(new ActionListener() { 38          public void actionPerformed(ActionEvent e) { 39            stopCellEditing(); 40          } 41        }); 42      } 43      public Object getCellEditorValue() { 44        return editorField.getText(); 45      } 46      public Component getTreeCellEditorComponent( 47        JTree tree, 48        Object value, 49        boolean isSelected, 50        boolean expanded, 51        boolean leaf, 52        int row) { 53 54        editorField.setText(String.valueOf(value)); 55        return editorField; 56      } 57      public Component getTableCellEditorComponent( 58        JTable table, 59        Object value, 60        boolean isSelected, 61        int row, 62        int column) { 63 64        editorField.setText(String.valueOf(value)); 65        return editorField; 66      } 67      public Component getTreeCellRendererComponent( 68        JTree tree, 69        Object value, 70        boolean selected, 71        boolean expanded, 72        boolean leaf, 73        int row, 74        boolean hasFocus) { 75 76        rendererField.setText(String.valueOf(value)); 77        return rendererField; 78      } 79      public Component getTableCellRendererComponent( 80        JTable table, 81        Object value, 82        boolean isSelected, 83        boolean hasFocus, 84        int row, 85        int column) { 86 87        rendererField.setText(String.valueOf(value)); 88        return rendererField; 89      } 90      private Object getValueFromEvent(EventObject e) { 91        Object value = null; 92        if (e instanceof MouseEvent) {  93          MouseEvent event = (MouseEvent)e; 94          Point p = event.getPoint(); 95          Object src = e.getSource(); 96          if (src instanceof JTree) { 97            JTree tree = (JTree)src; 98            TreePath path = tree.getClosestPathForLocation(p.x, p.y); 99            value = path.getLastPathComponent(); 100          } else if (src instanceof JTable) { 101            JTable table = (JTable)src; 102            int row = table.rowAtPoint(p); 103            int column = table.columnAtPoint(p); 104            value = table.getValueAt(row, column); 105          } 106        } 107        return value; 108      } 109   }
image from book

Following is a demonstration application that creates a JTree and a JTable and a minimal renderer and editor for each. It is comprised of the two classes DemoTreeOrTableCellHandler and DemoFrame.

The DemoTreeOrTableCellHandler Class

The DemoTreeOrTableCellHandler class extends AbstractCellEditor. In an attempt to “be all things to all people” it implements TreeCellEditor, TableCellEditor, TreeCellRenderer and TableCellRenderer simultaneously, albeit in a minimal way. This is perhaps putting too much functionality into one class but it is useful for pointing out the overlaps between the various interfaces.

The DemoFrame Class

The DemoFrame class illustrates the basics of creating a TableModel and TreeModel as well as demonstrating the use of a custom renderer and editor in trees and tables. Notice that it creates two instances of DemoTreeOrTableHandler – one for the JTree and another for the JTable. Editors should not be shared between components lest the components’ painting processes become confused. For some mischievous fun, rewrite DemoFrame so that the JTree and JTable share the same instance of DemoTreeOrTableHandler. Then run the program, clicking and editing various cells, going back and forth between the JTree and the JTable, to see why sharing an editor isn’t a good idea.

Example 14.13: chap14.interlude.DemoFrame.java

image from book
 1    package chap14.interlude; 2 3    import java.awt.BorderLayout; 4    import java.awt.GridLayout; 5 6    import javax.swing.BorderFactory; 7    import javax.swing.JFrame; 8    import javax.swing.JPanel; 9    import javax.swing.JScrollPane; 10   import javax.swing.JTable; 11   import javax.swing.JTree; 12   import javax.swing.table.DefaultTableModel; 13   import javax.swing.tree.DefaultMutableTreeNode; 14   import javax.swing.tree.DefaultTreeModel; 15 16   public class DemoFrame extends JFrame { 17 18     public DemoFrame() { 19       super("Tree and Table Editor Demo"); 20 21       DemoTreeOrTableCellHandler handler1 = new DemoTreeOrTableCellHandler(); 22 23       JTable table = createTable(); 24       table.setDefaultRenderer(Object.class, handler1); 25       table.setDefaultEditor(Object.class, handler1); 26 27       DemoTreeOrTableCellHandler handler2 = new DemoTreeOrTableCellHandler(); 28 29       JTree tree = createTree(); 30       tree.setCellRenderer(handler2); 31       tree.setCellEditor(handler2); 32 33       //uncomment to see what happens. 34       //tree.setCellRenderer(handler1); 35       //tree.setCellEditor(handler1); 36 37       JPanel contentPane = new JPanel(); 38       setContentPane(contentPane); 39       contentPane.setLayout(new GridLayout(1, 2)); 40       JPanel treePanel = new JPanel(new BorderLayout()); 41       JPanel tablePanel = new JPanel(new BorderLayout()); 42       tablePanel.add(BorderLayout.CENTER, new JScrollPane(table)); 43       tablePanel.setBorder(BorderFactory.createTitledBorder("Table Demo")); 44       treePanel.add(BorderLayout.CENTER, new JScrollPane(tree)); 45       treePanel.setBorder(BorderFactory.createTitledBorder("Tree Demo")); 46       contentPane.add(treePanel); 47       contentPane.add(tablePanel); 48 49       setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 50       pack(); 51       setVisible(true); 52 53     } 54     private JTable createTable() { 55       Object[][] data = { { "Mickey", "NMI", "Mouse", "mousem" }, { 56           "Donald", "NMI", "Duck", "duckd" }, { 57           "Felix", "the", "Cat", "catf" }, { 58           "Winnie", "ther", "Pooh", "poohw" } 59       }; 60       Object[] columns = { "First", "Middle", "Last", "User Name" }; 61 62       DefaultTableModel model = new DefaultTableModel(data, columns); 63       JTable table = new JTable(model); 64 65       return table; 66     } 67     private JTree createTree() { 68       DefaultMutableTreeNode food = new DefaultMutableTreeNode("Food"); 69       DefaultMutableTreeNode fruits = new DefaultMutableTreeNode("Fruits"); 70       DefaultMutableTreeNode vegetables = 71         new DefaultMutableTreeNode("Vegatables"); 72       DefaultMutableTreeNode apples = new DefaultMutableTreeNode("Apples"); 73       DefaultMutableTreeNode pears = new DefaultMutableTreeNode("Pears"); 74       DefaultMutableTreeNode cucumbers = new DefaultMutableTreeNode("Cucumbers"); 75       DefaultMutableTreeNode tomatoes = new DefaultMutableTreeNode("Tomatoes"); 76       food.add(fruits); 77       food.add(vegetables); 78       fruits.add(apples); 79       fruits.add(pears); 80       vegetables.add(cucumbers); 81       vegetables.add(tomatoes); 82 83       DefaultTreeModel model = new DefaultTreeModel(food); 84       JTree tree = new JTree(model); 85       tree.setEditable(true); 86 87       return tree; 88     } 89     public static void main(String[] arg) { 90       new DemoFrame().setVisible(true); 91     } 92    }
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/interlude/DemoFrame.java    java –cp classes chap14/interlude/DemoFrame

Quick Review

JTables, JTrees and JComboBoxes are able to edit their underlying data models, and they provide methods for plugging in custom editors through a mechanism that is similar to plugging in custom renderers. To create a custom editor for a JTree or JTable, it is easiest to extend AbstractCellEditor and implement either the TreeCellEditor or the TableCellEditor interface as required.




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