Sun Java Solaris Communities My SDN Account Join SDN
 
Swing Introduction

Swing

 

Bookshelf Index


Introduction | Download Chapter 18| Download Chapter 22
<< NEXT >>

Running the Code

Figure 18.6 shows StocksTable with data retrieved from a database. Try loading data for different dates in your database. A sample Microsoft Access database, market.mdb, containing some real market data, can be found in the \swing\Chapter18 directory.

18.7 Stocks Table: Part VI - Column Addition and Removal

JTable allows us to dynamically add and remove TableColumns on the fly. Recall that the TableColumnModel interface provides the methods addColumn() and removeColumn() to programmatically add or remove a TableColumn respectively. In this section we add dynamic column addition and removal to our StocksTable application.


Click Figure 18.6 for full-scale image.
JTable with dynamic column addition and removal.

The Code: StocksTable.java
see \Chapter18\6



import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.text.*;
import java.sql.*;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.table.*;

public class StocksTable 
                extends JFrame 
{
  // Unchanged code from 
  //section 18.5
  
  public StocksTable() {
    // Unchanged code from section 18.5
    header.setReorderingAllowed(true);

    m_table.getColumnModel(
                 ).addColumnModelListener(
      m_data.new ColumnMovementListener());

    // Unchanged code from section 18.5
  }  

  protected JMenuBar createMenuBar() {
    // Unchanged code from section 18.5

    JMenu mView = new JMenu("View");
    mView.setMnemonic('v');
    TableColumnModel model = 
              m_table.getColumnModel();
    for (int k = 0; k < 
           stocktabledata.m_columns.length;
                                    k++) {
      jcheckboxmenuitem item = 
                   new jcheckboxmenuitem(
        stocktabledata.m_columns[k].m_title);
      item.setselected(true);
      tablecolumn column = 
                       model.getcolumn(k);
      item.addactionlistener(
                  new columnkeeper(column, 
        stocktabledata.m_columns[k]));
      mview.add(item);
    }
    menubar.add(mview);

    return menubar;
  }

  // unchanged code from section 18.5

  class columnkeeper 
           implements actionlistener
  {
    protected tablecolumn m_column;
    protected columndata  m_coldata;

    public columnkeeper(tablecolumn 
       column, columndata  coldata) {
      m_column = column;
      m_coldata = coldata;
    }

    public void 
        actionperformed(
                  actionevent e) {
      jcheckboxmenuitem item = 
         (jcheckboxmenuitem)e.getsource();
      tablecolumnmodel model = 
              m_table.getcolumnmodel();
      if (item.isselected()) {
        model.addcolumn(m_column);
      }
      else {
        model.removecolumn(m_column);
      }
      m_table.tablechanged(new 
            tablemodelevent(m_data)); 
      m_table.repaint();
    }
  }

  public static void main(string argv[]) {
    new stockstable();
  }
}

// unchanged code from section 18.5

class stocktabledata 
       extends abstracttablemodel 
{
  // unchanged code from section 18.5

  protected simpledateformat m_frm;
  protected vector m_vector;
  protected java.util.date m_date;
  protected int m_columnscount = 
                   m_columns.length;

  // unchanged code from section 18.5 

  public int getcolumncount() { 
    return m_columnscount;
  }

  // unchanged code from section 18.5

  class columnlistener 
              extends mouseadapter
  {
    // unchanged code from section 18.4

    public void mouseclicked(mouseevent e) {
      // unchanged code from section 18.4

      for (int i=0; i < 
          m_columnscount; i++) {
        tablecolumn column = 
          colmodel.getcolumn(i);
        column.setheadervalue(
         getcolumnname(column.getmodelindex()));    
      }
      m_table.gettableheader().repaint();  

      // unchanged code from section 18.4
    }
  }

  class columnmovementlistener 
      implements tablecolumnmodellistener
  {
    public void columnadded(
           tablecolumnmodelevent e) {
      m_columnscount++;
    }

    public void columnremoved(
              tablecolumnmodelevent e) {
      m_columnscount--;
    }

    public void columnmarginchanged(
                        changeevent e) {}
    public void columnmoved(
               tablecolumnmodelevent e) {}
    public void columnselectionchanged(
                  listselectionevent e) {}
  }
  // unchanged code from section 18.5
}

// class stockcomparator 
//unchanged from section 18.4



Understanding the Code

Class StocksTable

The StocksTable constructor now adds an instance of StockTableData.ColumnMovementListener (see below) to our table's TableColumnModel to listen for column additions and removals.

Our createMenuBar() method now adds several checkbox menu items to a new View menu---one for each column. Each of these check box menu items recieves a ColumnKeeper instance (see below) as ActionListener.

Class StocksTable.ColumnKeeper

This inner class implements the ActionListener interface and serves to keep track of when the user removes and adds columns to the table. The constructor receives a TableColumn instance and a ColumnData object. The actionPerformed() method adds this column to the model with the addColumn() method if the corresponding menu item is checked, and removes this column from the model with removeColumn() if it is unchecked. To update the table to properly reflect these changes, we call its tableChanged() method followed by a repaint() request.

Class StockTableData

StockTableData now contains instance variable m_columnsCount to keep track of the current column count. This variable is decremented and incremented in the columnRemoved() and columnAdded() methods of inner class ColumnMovementListener. It is also used in the StockTableData.ColumnListener class's mouseClicked() method for properly setting header values for the visible columns only.

Class StockTableData.ColumnMovementListener

This class implements TableColumnModelListener to increment and decrement StockTableData's m_columnsCount variable when a column addition or removal occurs, respectively. An instance of this inner class is added to our table's TableColumnModel in the StocksTable constructor.

Running the Code

Figure 18.7 shows the new View menu with an unchecked Change % menu item, and the corresponding column hidden. Reselecting this menu item will place the column back in the table at the end position. Verify that each menu item functions similarly.

18.8 Custom Models, Editors, and Renderers

In constructing our StocksTable application we talked mostly about displaying and retrieving data in JTable. In this section we will construct a basic expense report application, and in doing so we will concentrate on table cell editing. We will also see how to implement dynamic addition and removal of table rows.

The editing of data generally follows this scheme:

  • Create an instance of the TableCellEditor interface. We can use the DefaultCellEditor class or implement our own. The DefaultCellEditor class takes a GUI component as a parameter to its constructor: JTextField, JCheckBox or JComboBox. This component will be used for editing.
  • If we are developing a custom editor, we need to implement the getTableCellEditorComponent() method which will be called each time a cell is about to be edited.
  • In our table model we need to implement the setValueAt(Object value, int nRow, int nCol) method which will be called to change a value in the table when an edit ends. This is where we can perform any necessary data processing and validation.
  • The data model for this example is designed as follows (where each row represents a column in our JTable):

    Name Type Description
    Date String Date of expense
    Amount Double Amount of expense
    Category Integer Category from pre-defined list
    Approved Boolean Sign of approval for this expense.
    Description String Brief description


    Click Figure 18.7 for full-scale image.
    An expense report app illustrating custom cell editing, rendering, and row addition/removal.

    Note: Since the only math that is done with our Amount values is addition, using Doubles is fine. However, in more professional implementations we may need to use rounding techniques or a custom renderer to remove unneccessary fractional amounts.

    The Code: ExpenseReport.java

    
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import java.io.*;
    import java.text.SimpleDateFormat;
    
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import javax.swing.table.*;
    
    public class ExpenseReport extends JFrame 
    {
      protected JTable m_table;
      protected ExpenseReportData m_data;
      protected JLabel m_title;
    
      public ExpenseReport() {
        super("Expense Report");
        setSize(570, 200);
    
        m_data = new ExpenseReportData(this);
    
        m_table = new JTable();
        m_table.setAutoCreateColumnsFromModel(
                        false);
        m_table.setModel(m_data); 
        m_table.setSelectionMode(
                    ListSelectionModel.
                            SINGLE_SELECTION);
            
        for (int k = 0; k < 
                   ExpenseReportData.
                     m_columns.length; k++) {
          TableCellRenderer renderer;
          if (k==ExpenseReportData.COL_APPROVED)
            renderer = new CheckCellRenderer();
          else {
            DefaultTableCellRenderer textRenderer = 
              new DefaultTableCellRenderer();
            textRenderer.setHorizontalAlignment(
              ExpenseReportData.m_columns[
                        k].m_alignment);
            renderer = textRenderer;
          }
    
          TableCellEditor editor;
    
          if (k==ExpenseReportData.COL_CATEGORY)
            editor = new DefaultCellEditor(
                                  new JComboBox(
              ExpenseReportData.CATEGORIES));
          else if (k==ExpenseReportData.
                             COL_APPROVED)
            editor = new DefaultCellEditor(     
                              new JCheckBox());
          else
            editor = new DefaultCellEditor(
                              new JTextField());
    
          TableColumn column = new TableColumn(k, 
            ExpenseReportData.m_columns[k].m_width, 
              renderer, editor);
            m_table.addColumn(column);   
        }
    
        JTableHeader header = 
                      m_table.getTableHeader();
        header.setUpdateTableInRealTime(false);
    
        JScrollPane ps = new JScrollPane();
        ps.setSize(550, 150);
        ps.getViewport().add(m_table);
        getContentPane().add(ps, 
                      BorderLayout.CENTER);
    
        JPanel p = new JPanel();
        p.setLayout(new BoxLayout(p, 
                       BoxLayout.X_AXIS));
    
        ImageIcon penny = 
                new ImageIcon("penny.gif");
        m_title = new JLabel("Total: $", 
          penny, JButton.LEFT);
        m_title.setForeground(Color.black);
        m_title.setAlignmentY(0.5f);
        p.add(m_title);
        p.add(Box.createHorizontalGlue());
    
        JButton bt = new JButton(
                     "Insert before");
        bt.setMnemonic('b');
        bt.setAlignmentY(0.5f);
        ActionListener lst = 
                      new ActionListener() { 
          public void actionPerformed(
                             ActionEvent e) { 
            int row = m_table.getSelectedRow();
            m_data.insert(row);
            m_table.tableChanged(
                          new TableModelEvent(
              m_data, row, row, 
                    TableModelEvent.ALL_COLUMNS, 
              TableModelEvent.INSERT)); 
            m_table.repaint();
          }
        };
        bt.addActionListener(lst);
        p.add(bt);
    
        bt = new JButton("Insert after");
        bt.setMnemonic('a');
        bt.setAlignmentY(0.5f);
        lst = new ActionListener() { 
          public void actionPerformed(
                         ActionEvent e) { 
            int row = m_table.getSelectedRow();
            m_data.insert(row+1);
            m_table.tableChanged(new 
                          TableModelEvent(
              m_data, row+1, row+1, 
                   TableModelEvent.ALL_COLUMNS,
              TableModelEvent.INSERT)); 
            m_table.repaint();
          }
        };
        bt.addActionListener(lst);
        p.add(bt);
    
        bt = new JButton("Delete row");
        bt.setMnemonic('d');
        bt.setAlignmentY(0.5f);
        lst = new ActionListener() { 
          public void actionPerformed(
                        ActionEvent e) { 
            int row = m_table.getSelectedRow();
            if (m_data.delete(row)) {
              m_table.tableChanged(new 
                         TableModelEvent(
                m_data, row, row, 
                   TableModelEvent.ALL_COLUMNS,
                TableModelEvent.INSERT)); 
              m_table.repaint();
              calcTotal();
            }
          }
        };
        bt.addActionListener(lst);
        p.add(bt);
          
        getContentPane().add(p, 
              BorderLayout.SOUTH);
    
        calcTotal();
    
        WindowListener wndCloser = 
                   new WindowAdapter() {
          public void windowClosing(
                         WindowEvent e) {
            System.exit(0);
          }
        };
        addWindowListener(wndCloser);
        
        setVisible(true);
      }
    
      public void calcTotal() {
        double total = 0;
        for (int k=0; k= 
                      m_vector.size())
          return false;
        m_vector.remove(row);
          return true;
      }
    }
    

    Page 1 2 3 4 5 6 7 8 9 10 11

    << NEXT >>
    Download Chapter 18| Download Chapter 22]