|
Bookshelf Index
Chapter 18, Tables
By Matthew Robinson and Pavel Vorobiev
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:
| Name | Type | Description |
| Symbol | String | Stock's
symbol
(NYSE or
NASDAQ) |
| Name |
String | Company name |
| Last |
double |
Price at
the
end of the
trade day |
| Open
|
double |
Price at
the
beginning
of
the trade day |
| Change |
double | Absolute
change
in
price
with respect to previous closing |
| Change |
%double |
Percent change in price
with respect to previous closing |
| Volume |
long | Day'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]
|