Controls in Tables


Overview Basic version Model Cell Renderer Editor Column widths Tooltips Controls in cells

We often want to put a control in a table, to enable for example the row to be edited or deleted.

We need to produce the visual appearance, and the functionality, so the control does what we want, including updating the underlying model and the table's appearance

We will look at an example where we enable rows to be deleted. We want to produce:

We will consider the table model, the visual appearance, and the deletion functionality.

The model in this example just has 1 column of 'real' data, and a second column for the control, which just returns an arbitrary string

 
   class MyModel extends AbstractTableModel {

    private String[] columnNames = {"Data", "Delete"};
    private ArrayList data = new ArrayList();

    void deleteRow(int row) {
        .. described later
    }

    MyModel() {
        Random r = new Random();
        for (int i = 0; i < 30; i++) {
            data.add(r.nextInt(1000));
        }
    }

    @Override
    public int getColumnCount() {
        return 2;
    }

    public int getRowCount() {
        return data.size();
    }

    public String getColumnName(int col) {
        return columnNames[col];
    }

    public Object getValueAt(int row, int col) {
        if (col == 0) {
            return data.get(row);
        } else {
            return "Delete"; // dummy string
        }
    }

    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

    public boolean isCellEditable(int row, int col) {
        if (col == 0) {
            return true;
        } else {
            return false;
        }
    }

    public void setValueAt(Object value, int row, int col) {
        if (col == 0) {
            data.set(row, (Integer) value);
        }
        fireTableCellUpdated(row, col);
    }
}

For appearance, we can render the control column as a JLabel displaying an ImageIcon. We need to set the widths, and assign the renderer to column 1:


        MyModel data = new MyModel();
        table = new JTable(data);     
        table.setRowHeight(37); // match image height
        ..
        // fix widths
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        TableColumn column = table.getColumnModel().getColumn(1);
        column.setPreferredWidth(60);
        column = table.getColumnModel().getColumn(0);
        column.setPreferredWidth(100);
        // set renderer for control column..
        column = table.getColumnModel().getColumn(1);
        column.setCellRenderer(new MyRenderer());
        ..
        JScrollPane sp = new JScrollPane(table);
        add(sp);              

The cell renderer is


class MyRenderer extends JLabel implements TableCellRenderer {

    static ImageIcon icon;

    static { // load image
        java.net.URL imageURL = MyRenderer.class.getResource("del.gif");
        if (imageURL != null) {
            icon = new ImageIcon(imageURL); 
        } 
    }

    MyRenderer() {
        super(icon);             
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        if (isSelected) { // border selected cell in red
            setBorder(BorderFactory.createLineBorder(Color.red));
        } else {
            setBorder(null);
        }
        return this; 
    }
}

The image file is loaded by MyRenderer.class.getResource("del.gif"); This expects to find the file alongside the compiled .class file, possibly in a .jar. Depending on the IDE you are using, and how you use it, you need to ensure the gif ends up in the correct place.

To get the correct functionality, we control the user's 'selection' - the table cells they click on, and set up a listener to respond.

        // disallow multiple selection areas
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // only allow single cell selection, not rows or columns
        table.setCellSelectionEnabled(true);
        .. set a list selection listener
        ListSelectionModel tableSelectionModel = table.getSelectionModel();
        tableSelectionModel.addListSelectionListener(this);

The interface has only one method:

          @Override
    public void valueChanged(ListSelectionEvent e) {
        int column = table.getSelectedColumn();
        if (column != 1) {
            return; // process only column 1
        }
        ListSelectionModel lsm = (ListSelectionModel) e.getSource();
        boolean isAdjusting = e.getValueIsAdjusting();
        if (isAdjusting) {
            return; // ignore if not yet finished
        }
        int row = lsm.getMaxSelectionIndex();
        if (row < 0) {
            return; // ignore negative row - results after row deleted
        }
        // delete selected row
        ((MyModel) table.getModel()).deleteRow(row);
    }

Deleting the row affects the model, so the method is part of the model

    void deleteRow(int row) {
        // verification..
        int dialogButton = JOptionPane.YES_NO_OPTION; 
        int dialogResult = JOptionPane.showConfirmDialog(null, 
            "Really delete this row?", "Delete can't be undone", dialogButton);
        if (dialogResult == JOptionPane.YES_OPTION) {
            data.remove(row); // remove element from arraylist
            fireTableRowsDeleted(row, row); // re-draw
        }
    }

Comments

0 responses so far

Add a new comment

Your name

Your email (will not be published)

Your comment (no HTML)

Are you a robot? Please type the two words: