Sun Java Solaris Communities My SDN Account Join SDN
 
Swing Introduction

Swing

 

Bookshelf Index


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

18.1.18 Column Width and Resizing

When a column's width increases, JTable must decide how other columns will react. One or more columns must shrink. Similarly, when a column's width decreases, JTable must decide how other columns will react to the newly available amount of space. JTable's autoResizeMode property can take on any of five different values that handle these cases differently:

  • JTable.AUTO_RESIZE_ALL_COLUMNS: All columns gain or lose an equal amount of space corresponding the width lost or gained by the resizing column.


  • JTable.AUTO_RESIZE_LAST_COLUMN: The right-most column shrinks or grows in direct correspondence with the amount of width lost or gained from the column being resized. All other colmns are not affected.


  • JTable.AUTO_RESIZE_NEXT_COLUMN: The column to the immediate right of the column being resized shrinks or grows in direct correspondence with the amount of width lost or gained from the resizing column. All other colmns are not affected.


  • JTable.AUTO_RESIZE_OFF: resizing only affects the column being sized. All columns to the right of the column being resized are shifted right or left accordingly while maintaining their current sizes. Columns to the left are not affected.


  • JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS: All columns to the right of the column being resized gain or lose an equal amount of space corresponding the width lost or gained by the resizing column. Columns to the left are not affected.
  • TableColumn's width defaults to 75. Its minimum width defaults to 15 and its maximum width defaults to Integer.MAX_VALUE. When a JTable is first displayed it attempts to size each TableColumn according to its width property. If that table's autoResizeMode property is set to AUTO_RESIZE_OFF this will occur successfully. Otherwise, TableColumns are adjusted according to the current autoResizeMode property.

    A TableColumn will never be sized larger than its maximum width or smaller than its minimum. For this reason it is possible that a JTable will occupy a larger or smaller area than that available (i.e. visible in the parent JScrollPane's main viewport), which may result in part of the table being clipped from view. If a table is contained in a JScrollPane and it occupies more than the available visible width, a horizontal scrollbar will be presented.

    At any time we can call TableColumn's sizeWidthToFit() method to resize a column to occupy a width corresponding to the preferred width of its table header renderer. This is often used in assigning minimum widths for each TableColumn. JTable's sizeColumnsToFit() method takes an int parameter specifying the index of the TableColumn to act as the source of a resize in an attempt to make all columns fit within the available visible space. Also note that TableColumnModel's getTotalColumnWidth() method will return the sum of the current width of all TableColumns including all margin space.

    We can specify the amount of empty space between rows with JTable's setRowMargin() method, and we can assign all rows a specific height with setRowHeight(). JTable's setIntercellSpacing() method takes a Dimension instance and uses it to assign a new width and height to use as margin space between cells (this method will redisplay the table it is invoked on after all sizes have been changed).

    18.1.19 JTable Appearance

    We can change the background and foreground colors used to highlight selected cells by setting the selectedBackground and setSelectedForeground properties. The default colors used for each TableColumn's table header renderer are determined from the current JTableHeader's background and foreground colors (recall that JTableHeader extends JComponent).

    We can turn on and off horizontal and vertical grid lines (which are always have a thickness of 1 pixel) by changing the showHorizontalLines and showVerticalLines. properties. The showGrid property will overpower these properties when set with setShowGrid() becauuse this method reasigns them to the specified value. So setShowGrid() turns on/off both vertical and horizontal lines as specified. The gridColor property specifies the Color to use for both vertical and horizontal grid lines. setGridColor() will assign the specified value to this property and then repaint the whole table.

    Visual Noise: Grid Lines add visual noise to the display of a table. Removing some of them can aid the reading of the table data. If you intend the reader to read rows across then switch off the vertical grid lines. If you have columns of figures for example, then you might prefer to switch off the horizontal grid lines, making the columns easier to read.

    When switching off the horizontal grid lines on the table, you may wish to use the column cell renderer to change the background color of alternate rows on the table to ease the reading of rows. Using this combination of visual techniques, grid lines to distinguish columns and colour to distinguish rows, helps guide the reader to interpret data as required.

    18.1.20 JTable scrolling

    JTable implements the Scrollable interface (see 7.1.4) and is intended to be placed in a JScrollPane. JTableHeaders will not be displayed otherwise, disabling any column resize capabilities. Among the required Scrollable methods, JTable implements getScrollableTracksViewportWidth() to return true, which forces JTable to attempt to size itself horizontally to fit within the current scroll pane viewport width. getScrollableTracksViewportHeight(), however, returns false as it is most common for tables to be vertically scrolled but not horizontally scrolled. Horizontal scrolling is often awkward and we advise avoiding it whenever possible.

    JTable's vertical block increment is the number of visible rows less one, and its vertical unit increment is the current row height. The horizontal block inrement is the width of the viewport, and the horizontal unit increment defaults to 100.

    Small grids, no column headers If you have a requirement to show two or three pieces of data, grouped and aligned together, consider using a JTable without JScrollPane. This gives you a small grid which is already aligned and neat and tidy for display without column headers.

    18.2Stocks Table: Part I - Basic JTable example

    This basic example shows how to construct a JTable to display information about stock market data for a given day. Despite of its simplicity, it demonstrates the most fundamental features of JTable, and serves as a good basis for the more advanced examples that follow.

    Stocks and stock trading is characterized by many attributes. The following are selected for display in our example:

    NameTypeDescription
    SymbolStringStock's symbol (NYSE or NASDAQ)
    Name StringCompany name
    Last double Price at the end of the trade day
    Open double Price at the beginning of the trade day
    Change doubleAbsolute change in price with respect to previous closing
    Change %double Percent change in price with respect to previous closing
    Volume longDay's volume of trade (in $) for this stock

    Each stock attribute represents a column in our table, and each row represents a specific company's stock information.


    Click Figure 18.1
    for full-scale image. JTable in a JScrollPane with 7 TableColumns and 16 rows of data.

    The Code: StocksTable.java
    see \Chapter18\1

    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import java.io.*;
    import java.text.*;
    
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import javax.swing.table.*;
    
    public class StocksTable 
                    extends JFrame 
    {
      protected JTable m_table;
      protected StockTableData m_data;
      protected JLabel m_title;
    
      public StocksTable() {
        super("Stocks Table");
        setSize(600, 340);
    
        m_data = new StockTableData();
    
        m_title = new JLabel(m_data.getTitle(), 
          new ImageIcon("money.gif"), 
                          SwingConstants.LEFT);
        m_title.setFont(new Font(
                   "TimesRoman",Font.BOLD,24));
        m_title.setForeground(Color.black);
        getContentPane().add(
                  m_title, BorderLayout.NORTH);
    
        m_table = new JTable();
        m_table.setAutoCreateColumnsFromModel(
                                        false);
        m_table.setModel(m_data); 
            
        for (int k = 0; k < 
                 StockTableData.m_columns.length;
                                           k++) {
          DefaultTableCellRenderer renderer = new 
            DefaultTableCellRenderer();
          renderer.setHorizontalAlignment(
            StockTableData.m_columns[
                                  k].m_alignment);
          TableColumn column = new TableColumn(k, 
            StockTableData.m_columns[k].m_width,
                                   renderer, null);
          m_table.addColumn(column);   
        }
    
        JTableHeader header = 
                         m_table.getTableHeader();
        header.setUpdateTableInRealTime(false);
    
        JScrollPane ps = new JScrollPane();
        ps.getViewport().add(m_table);
        getContentPane().add(ps, 
                              BorderLayout.CENTER);
    
        WindowListener wndCloser = 
                              new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            System.exit(0);
          }
        };
        addWindowListener(wndCloser);
        setVisible(true);
      }
    
      public static void main(String argv[]) {
        new StocksTable();
      }
    }
    
    class StockData
    {
      public String m_symbol;
      public String m_name;
      public Double m_last;
      public Double m_open;
      public Double m_change;
      public Double m_changePr;
      public Long m_volume;
    
      public StockData(String symbol, 
               String name, double last, 
               double open, double change, 
                          double changePr, 
                            long volume) {
        m_symbol = symbol;
        m_name = name;
        m_last = new Double(last);
        m_open = new Double(open);
        m_change = new Double(change);
        m_changePr = new Double(changePr);
        m_volume = new Long(volume);
      }
    }
    
    class ColumnData
    {
      public String  m_title;
      public int     m_width;
      public int     m_alignment;
    
      public ColumnData(
           String title, int width, 
                        int alignment) {
        m_title = title;
        m_width = width;
        m_alignment = alignment;
      }
    }
    
    class StockTableData 
               extends AbstractTableModel 
    {
      static final public 
              ColumnData m_columns[] = {
        new ColumnData( 
            "Symbol", 100, JLabel.LEFT ),
        new ColumnData( 
              "Name", 150, JLabel.LEFT ),
        new ColumnData( 
             "Last", 100, JLabel.RIGHT ),
        new ColumnData( 
             "Open", 100, JLabel.RIGHT ),
        new ColumnData( 
           "Change", 100, JLabel.RIGHT ),
        new ColumnData( 
          "Change %", 100, JLabel.RIGHT ),
        new ColumnData( 
            "Volume", 100, JLabel.RIGHT )
      };
    
      protected SimpleDateFormat m_frm;
      protected Vector m_vector;
      protected Date   m_date;
    
      public StockTableData() {
        m_frm = new SimpleDateFormat("MM/dd/yyyy");
        m_vector = new Vector();
        setDefaultData();
      }
    
      public void setDefaultData() {
        try { 
          m_date = m_frm.parse("04/06/1999"); 
        }
        catch (java.text.ParseException ex) { 
          m_date = null; 
        }
    
        m_vector.removeAllElements();
        m_vector.addElement(new StockData(
                      "ORCL", "Oracle Corp.", 
                        23.6875, 25.375, 
                   -1.6875, -6.42, 24976600));
        m_vector.addElement(new StockData(
                          "EGGS", "Egghead.com", 
          17.25, 17.4375, -0.1875, 
                          -1.43, 2146400));
        m_vector.addElement(
                      new StockData("T", "AT&T", 
          65.1875, 66, -0.8125, -0.10, 554000));
        m_vector.addElement(new 
             StockData("LU", "Lucent Technology", 
          64.625, 59.9375, 4.6875, 
                               9.65, 29856300));
        m_vector.addElement(new StockData(
                                "FON", "Sprint", 
                     104.5625, 106.375, -1.8125, 
                              -1.82, 1135100));
        m_vector.addElement(new StockData(
                        "ENML", "Enamelon Inc.", 
          4.875, 5, -0.125, 0, 35900)); 
        m_vector.addElement(new StockData(
                         "CPQ", "Compaq Computers", 
          30.875, 31.25, -0.375, -2.18, 11853900));
        m_vector.addElement(new StockData(
                          "MSFT", "Microsoft Corp.", 
                              94.0625, 95.1875,
                           -1.125, -0.92, 19836900));
        m_vector.addElement(new StockData(
                           "DELL", "Dell Computers", 
          46.1875, 44.5, 1.6875, 6.24, 47310000));
        m_vector.addElement(new StockData(
                          "SUNW", "Sun Microsystems", 
          140.625, 130.9375, 10, 10.625, 17734600));
        m_vector.addElement(new StockData(
                        "IBM", "Intl. Bus. Machines", 
          183, 183.125, -0.125, -0.51, 4371400));
        m_vector.addElement(new StockData(
                           "HWP", "Hewlett-Packard", 
          70, 71.0625, -1.4375, -2.01, 2410700));
        m_vector.addElement(new StockData(
                             "UIS", "Unisys Corp.", 
          28.25, 29, -0.75, -2.59, 2576200));
        m_vector.addElement(new StockData(
                               "SNE", "Sony Corp.", 
          96.1875, 95.625, 1.125, 1.18, 330600));
        m_vector.addElement(new StockData(
                             "NOVL", "Novell Inc.", 
                               24.0625, 24.375, 
                         -0.3125, -3.02, 6047900));
        m_vector.addElement(new StockData(
                            "HIT", "Hitachi, Ltd.", 
          78.5, 77.625, 0.875, 1.12, 49400));
      }
    
      public int getRowCount() {
        return m_vector==null ? 0 : 
                              m_vector.size(); 
      }
    
      public int getColumnCount() { 
        return m_columns.length; 
      } 
    
      public String getColumnName(int column) { 
        return m_columns[column].m_title; 
      }
     
      public boolean isCellEditable(int 
                              nRow, int nCol) {
        return false;
      }
    
      public Object getValueAt(int 
                             nRow, int nCol) {
        if (nRow < 0 || nRow 
                          >= getRowCount())
          return "";
        StockData row = 
           (StockData)m_vector.elementAt(nRow);
        switch (nCol) {
          case 0: return row.m_symbol;
          case 1: return row.m_name;
          case 2: return row.m_last;
          case 3: return row.m_open;
          case 4: return row.m_change;
          case 5: return row.m_changePr;
          case 6: return row.m_volume;
        }
        return "";
      }
    
      public String getTitle() {
        if (m_date==null)
          return "Stock Quotes";
        return "Stock Quotes at 
                  "+m_frm.format(m_date);
      }
    }
    

    Understanding the Code

    Class StocksTable

    This class extends JFrame to implement the frame container for our table. Three instance variables are declared (to be used extensively in more complex examples that follow):

  • JTable m_table: table component to display stock data.


  • StockTableData m_data: TableModel implementation to manage stock data (see below).


  • JLabel m_title: used to display stocks table title (date which stock prices are referenced).
  • The StocksTable constructor first initializes the parent frame object and builds an instance of StockTableData. Method getTitle()is invoked to set the text for the title label which is added to the northern region of the content pane. Then a JTable is created by passing the StockTableData instance to the constructor. Note the the autoCreateColumnsFromModel method is set to false because we plan on creating our own TableColumns.

    As we will see below, the static array m_columns of the StockTableData class describes all columns of our table. It is used here to create each TableColumn instance and set their text alignment and width.

    Method setHorizontalAlignment() (inherited by DefaultTableCellRenderer from JLabel) is used to set the proper alignment for each TableColumn's cell renderer. The TableColumn constructor takes a column index, width, and renderer as parameters. Note that TableCellEditor is set to null since we don't want to allow editing of stock data. Finally, columns are added to the table's TableColumnModel (which JTable created by default because we didn't specify one) with the addColumn() method.

    In the next step, an instance of JTableHeader is created for this table, and the updateTableInRealTime property is set to false (this is done to demonstrate the effect this has on column dragging -- only a column's table header is displayed during a drag).

    Lastly a JScrollPane instance is used to provide scrolling capabilities, and our table is added to its JViewport. This JScrollPane is then added to the center of our frame's content pane.

    Class StockData

    This class encapsulates a unit of stock data as described in the table above. The instance variables defined in this class have the following meaning:

  • String m_symbol: stock's symbol (NYSE or NASDAQ)


  • String m_name: company name


  • Double m_last: the price of the last trade


  • Double m_open: price at the beginning of the trade day


  • Double m_change: absolute change in price with respect to previous closing


  • Double m_changePr: percent change in price with respect to previous closing


  • Long m_volume: day's volume of trade (in $) for this stock
  • Note: all numerical data are encapsulated in Object-derived classes. This design decision simplifies data exchange with the table (as we will see below). The only constructor provided assigns each of these variables from the data passed as parameters.

    Note: We use public instance variables in this and several other classes in this chapter to avoid overcomplication. In most professional apps these would either be protected or private.

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

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