|
Introduction |
Download Chapter 18| Download Chapter 22 18.5 Stocks Table: part IV - Sorting ColumnsNote: This
and the following In this section we add the ability to sort any column in ascending or descending order. The most suitable graphical element for selection of sort order are the column headers. We adopt the following model for our sorting functionality: Collections
functionality in Java 2.
Note: Class
We use the int
compare(Object o1, Object o2):Compares two objects and
returns
the
result
as an int (zero if equal,
negative value if the first is less than the second, positive
value if
the
first is more than the second).boolean
equals(Object obj):Returns
true if the given
object is equal to this Comparator.
The Code:
|
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
{
// Unchanged code from
//section 18.4
public StocksTable() {
// Unchanged code from
//section 18.4
JTableHeader header =
m_table.getTableHeader();
header.setUpdateTableInRealTime(
true);
header.addMouseListener(
m_data.new ColumnListener(
m_table));
header.setReorderingAllowed(true);
// Unchanged code from section 18.4
}
public static void main(String argv[]) {
new StocksTable();
}
}
// Unchanged code from section 18.4
class StockTableData
extends AbstractTableModel
{
// Unchanged code from
//section 18.2
protected SimpleDateFormat m_frm;
protected Vector m_vector;
protected Date m_date;
protected int m_sortCol = 0;
protected boolean m_sortAsc = true;
public StockTableData() {
m_frm = new SimpleDateFormat(
"MM/dd/yyyy");
m_vector = new Vector();
setDefaultData();
}
public void setDefaultData() {
// Unchanged code from
//section 18.4
Collections.sort(m_vector, new
StockComparator(m_sortCol,
m_sortAsc));
}
// Unchanged code from section 18.4
public String getColumnName(
int column) {
String str =
m_columns[column].m_title;
if (column==m_sortCol)
str += m_sortAsc ? " " : " ";
return str;
}
// Unchanged code from section 18.4
class ColumnListener
extends MouseAdapter
{
protected JTable m_table;
public ColumnListener(JTable table) {
m_table = table;
}
public void mouseClicked(MouseEvent e) {
TableColumnModel colModel =
m_table.getColumnModel();
int columnModelIndex =
colModel.getColumnIndexAtX(e.getX());
int modelIndex =
colModel.getColumn(
columnModelIndex).getModelIndex();
if (modelIndex < 0)
return;
if (m_sortCol==modelIndex)
m_sortAsc = !m_sortAsc;
else
m_sortCol = modelIndex;
for (int i=0; i < m_columns.length;
i++) {
tablecolumn column =
colmodel.getcolumn(i);
column.setheadervalue(
getcolumnname(
column.getmodelindex()));
}
m_table.gettableheader().repaint();
collections.sort(m_vector, new
stockcomparator(modelindex, m_sortasc));
m_table.tablechanged(
new tablemodelevent(
stocktabledata.this));
m_table.repaint();
}
}
}
class stockcomparator
implements comparator
{
protected int m_sortcol;
protected boolean m_sortasc;
public stockcomparator(int
sortcol, boolean sortasc) {
m_sortcol = sortcol;
m_sortasc = sortasc;
}
public int compare(object o1,
object o2) {
if(!(o1 instanceof stockdata) ||
!(o2 instanceof stockdata))
return 0;
stockdata s1 = (stockdata)o1;
stockdata s2 = (stockdata)o2;
int result = 0;
double d1, d2;
switch (m_sortcol) {
case 0: // symbol
string str1 =
(string)s1.m_symbol.m_data;
string str2 =
(string)s2.m_symbol.m_data;
result = str1.compareto(str2);
break;
case 1: // name
result =
s1.m_name.compareto(s2.m_name);
break;
case 2: // last
d1 = s1.m_last.doublevalue();
d2 = s2.m_last.doublevalue();
result = d1<d2 ? -1 :
(d1>d2 ? 1 : 0);
break;
case 3: // open
d1 = s1.m_open.doublevalue();
d2 = s2.m_open.doublevalue();
result = d1<d2 ? -1 : (
d1>d2 ? 1 : 0);
break;
case 4: // change
d1 = ((fraction)
s1.m_change.m_data).doublevalue();
d2 = ((fraction)
s2.m_change.m_data).doublevalue();
result = d1<d2 ?
-1 : (d1>d2 ? 1 : 0);
break;
case 5: // change %
d1 = ((double)s1.m_changepr.m_data)
.doublevalue();
d2 = ((double)s2.m_changepr.m_data)
.doublevalue();
result = d1<d2 ? -1 : (
d1>d2 ? 1 : 0);
break;
case 6: // volume
long l1 = s1.m_volume.longvalue();
long l2 = s2.m_volume.longvalue();
result = l1<l2 ? -1 :
(l1>l2 ? 1 : 0);
break;
}
if (!m_sortasc)
result = -result;
return result;
}
public boolean
equals(object obj) {
if (obj instanceof
stockcomparator) {
stockcomparator compobj =
(stockcomparator)obj;
return (
compobj.m_sortcol==m_sortcol) &&
(compobj.m_sortasc==m_sortasc);
}
return false;
}
}
|
In the StocksTable constructor we set the
updateTableInRealTime
property to show column contents while columns are dragged, and
we add
an
instance of the ColumnListener
class (see below) as a mouse listener to the table's header.
Here we declare two new instance
variables: int m_sortCol
to hold the index of the current column chosen for sorting, and
boolean
m_sortAsc, which is
true when sorting in
ascending order, and false
when sorting in descending order. These variables determine the
initial
sorting
order. To be consistent we sort our table initially by calling
the
Collections.sort() method
in our setDefaultData()
method (which is called from the StockTableData
constructor).
We also add a special marker for the sorting column's header: ' for ascending and ' for descending sorting. This changes the way we retrieve a column's name, which no longer is a constant value:
public String getColumnName(int column) {
String str = m_columns[column].m_title;
if (column==m_sortCol)
str += m_sortAsc ? " " : " ";
return str;
}
|
Because this class interacts heavily
with our table data, it is implemented as an inner class in
StockTableData. ColumnListener takes a
reference to a JTable
in its constructor and stores that reference in its
m_table
instance variable.
The mouseClicked() method is invoked when the user
clicks on a header. First it determines the index of the
TableColumn clicked based
on the coordinate of the click. If for any reason the returned
index
is
negative (such as the column cannot be determined) the method
cannot
continue
and
we return. Otherwise, we check whether this index corresponds to
the
column
which already has been selected for sorting. If so, we invert the
m_sortCol flag to reverse
the sorting order. If the index corresponds to newly selected
column
we store
the new sorting index in the m_sortCol
variable.
Next, we refresh the header names by
iterating through the TableColumns
and assinging them a name corresponding to the column they
represent
in the
TableModel. To do this we
pass each TableColumn's
modelIndex property
to our getColumnName()
method (see above). Finally our table data is re-sorted by
calling the
Collections.sort() method
and passing in a new StockComparator
object. We then refresh the table by calling
tableChanged() and
repaint().
This class implements the rule of
comparison for two objects, which in our case are
StockDatas.
Instances of the StockComparator class are
passed to the Collections.sort()
method to perform data sorting.
Two instance variables are defined:
int
m_sortCol represents the index of the column which
performs the
comparisonboolean
m_sortAsc is true
for ascending sorting and false
for descending.
The StockComparator constructor takes two parameters
and
stores
them in these instance variables.
The compare() method takes two objects to be
compared
and returns an integer value according to the rules determined in
the
Comparator interface:
Since we are dealing only with StockData objects,
first
we
cast both objects and return 0 if this cast isn't possible. The
next
issue is
to define what it means when one StockData objects
is
greater,
equal, or less than another. This
is done in a switch-case
structure, which, depending on the index of the comparison
column,
extracts
two
fields and forms an integer result of the comparison. When the
switch-case structure
finishes, we know the result of an ascending comparison. For
descending
comparison we simply need to invert the sign of the result.
The equals() method takes another
Comparator
instance
as
parameter and returns true
if that parameter represents the same Comparator. We
determine
this
by comparing Comparator instance
variables: m_sortCol
and m_sortAsc.
Figure 18.4 shows StocksTable sorted by
decreasing Change %. Click different column headers and note that
resorting
occurs as expected. Click the same column header twice and note
that
sorting
order flips from ascending to descending and vice versa. Also
note
that the
currently selected sorting column header is marked by the or
symbol.
This sorting functionality is very useful. Particularly, for
stock
market data
we can instantly determine which stocks have the highest price
fluctuations or
the most heavy trade.
Sort by header selection idiom: Introducing table sorting using the column headers is introducing another design idiom to the User Interface. This design idiom is becoming widely accepted and widely used in many applications. It is a useful and powerful technique which you can introduce when sorting table data is a requirement. The technique is not intuitive and their is little visual affordance to suggest that clicking a column header will have any effect. So consider that the introduction of this technique may require additional User training.
<< NEXT >>
Download Chapter 18| Download Chapter 22]
|
| ||||||||||||