For example, the following code adds a
JButton enterButton = new JButton("Enter");
layeredPane.add(enterButton,
JLayeredPane.Default_Layer, 0);
You can achieve the same effect by calling the LayeredPane.moveToFont
method within a layer or using the LayeredPane.setLayer method
to move to a different layer.
JContentPane
The getContentPane()).setLayout(new BoxLayout())Or you can replace the default ContentPane with your own
ContentPane, such as a JPanel, like this:
JPanel pane= new JPanel(); pane.setLayout(new BoxLayout()); setContentPane(pane); GlassPane
The
One way to use a
MyGlassPane glassPane = new MyGlassPane();
setGlassPane(glassPane);
setGlassPane.setVisible(true); //before worker thread
..
setGlassPane.setVisible(false); //after worker thread
private class MyGlassPane extends JPanel {
public MyGlassPane() {
addKeyListener(new KeyAdapter() { });
addMouseListener(new MouseAdapter() { });
super.setCursor(
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
}
Data Models
Numerous model layers are combined to form the tables of the
The
The
At a higher level, and representing an intermediate layer between data and its
graphical representation, is the
For example, freezing the left-most columns in the
Higher still lie the various renderers, editors, and header components whose
combination define the look and organization of the
The creation of the inner classes
Finally, the various component user interfaces are responsible for the ultimate
appearance of the Table Model
The
Object[][] data = new Object[][]{ {"row 1 col1",
"Row 1 col2" },
{"row 2 col 1",
"row 2 col 2"}
};
Object[] headers = new Object[] {"first header",
"second header"};
DefaultTableModel model = new DefaultTableModel(data,
headers);
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
Creating a custom table model is nearly as easy as using
DefaultTableModel, and requires little additional coding.
You can implement a table model by implementing a method to return the number
of entries in the model, and a method to retrieve an element at a specific
position in that model. For example, the JTable model can be
implemented from javax.swing.table.AbstractTableModel by
implementing the methods getColumnCount, getRowCount
and getValueAt as shown here:
final Object[][] data = new Object[][]{ {
"row 1 col1",
"row 1 col2" },
{"row 2 col 1",
"row 2 col 2"} };
final Object[] headers = new Object[] {
"first header",
"second header"};
TableModel model = new AbstractTableModel(){
public int getColumnCount() {
return data[0].length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return (String)headers[col];
}
public Object getValueAt(int row,int col) {
return data[row][col];
}
};
table = new JTable(model);
table.setAutoResizeMode(
JTable.AUTO_RESIZE_OFF);
This table is read-only and its data values are already known. In fact,
the data is even declared final so it can be retrieved
by the inner TableModel class. This is not normally the situation
when working with live data.
You can create an editable table by by adding the
public void setValueAt (Object value,
int row, int col) {
data[row][col] = value;
fireTableCellUpdated (row, col);
}
public boolean isCellEditable(int row,
int col) {
return true;
}
More Table ModelsA common requirement for the display of tabular data is the inclusion of a non-scrolling column. This column provides a set of anchor data that remains stationary and visible while its neighboring columns are scrolled horizontally (and often out of view). This is particularly important in cases where row data can be identified by a unique value in the fixed column, such as a name or identification number. The next code example uses a fixed table column to display a list of the auction items.
The base table model in this example implements the
package auction;
import javax.swing.table.AbstractTableModel;
import javax.swing.event.TableModelEvent;
import java.text.NumberFormat;
import java.util.*;
import java.awt.*;
public class ResultsModel extends AbstractTableModel{
String[] columnNames={};
Vector rows = new Vector();
public String getColumnName(int column) {
if (columnNames[column] != null) {
return columnNames[column];
} else {
return "";
}
}
public boolean isCellEditable(int row, int column){
return false;
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return rows.size();
}
public Object getValueAt(int row, int column){
Vector tmprow = (Vector)rows.elementAt(row);
return tmprow.elementAt(column);
}
public void update(Enumeration enum) {
try {
columnNames = new String[5];
columnNames[0]=new String("Auction Id #");
columnNames[1]=new String("Description");
columnNames[2]=new String("High Bid");
columnNames[3]=new String("# of bids");
columnNames[4]=new String("End Date");
while((enum !=null) &&
(enum.hasMoreElements())) {
while(enum.hasMoreElements()) {
AuctionItem auctionItem=(
AuctionItem)enum.nextElement();
Vector items=new Vector();
items.addElement(new Integer(
auctionItem.getId()));
items.addElement(
auctionItem.getSummary());
int bidcount= auctionItem.getBidCount();
if(bidcount >0) {
items.addElement(
NumberFormat.getCurrencyInstance().
format(auctionItem.getHighBid()));
} else {
items.addElement("-");
}
items.addElement(new Integer(bidcount));
items.addElement(auctionItem.getEndDate());
rows.addElement(items);
}
}
fireTableStructureChanged();
} catch (Exception e) {
System.out.println("Exception e"+e);
}
}
}
The table is created from the ResultsModel model. Then,
the first table column is removed from that table and added to a new
table. Because there are now two tables, the only way the selections
can be kept in sync is to use a ListSelectionModel object
to set the selection on the table row in the other tables that were not
selected by calling the setRowSelectionInterval method.
The full example can be found in the AuctionClient.java source file:
private void listAllItems() throws IOException{
ResultsModel rm=new ResultsModel();
if (!standaloneMode) {
try {
BidderHome bhome=(BidderHome)
ctx.lookup("bidder");
Bidder bid=bhome.create();
Enumeration enum=
(Enumeration)bid.getItemList();
if (enum != null) {
rm.update(enum);
}
} catch (Exception e) {
System.out.println(
"AuctionServlet <list>:"+e);
}
} else {
TestData td= new TestData();
rm.update(td.results());
}
scrollTable=new JTable(rm);
adjustColumnWidth(scrollTable.getColumn(
"End Date"), 150);
adjustColumnWidth(scrollTable.getColumn(
"Description"), 120);
scrollColumnModel = scrollTable.getColumnModel();
fixedColumnModel = new DefaultTableColumnModel();
TableColumn col = scrollColumnModel.getColumn(0);
scrollColumnModel.removeColumn(col);
fixedColumnModel.addColumn(col);
fixedTable = new JTable(rm,fixedColumnModel);
fixedTable.setRowHeight(scrollTable.getRowHeight());
headers = new JViewport();
ListSelectionModel fixedSelection =
fixedTable.getSelectionModel();
fixedSelection.addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm = (
ListSelectionModel)e.getSource();
if (!lsm.isSelectionEmpty()) {
setScrollableRow();
}
}
});
ListSelectionModel scrollSelection =
scrollTable.getSelectionModel();
scrollSelection.addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm =
(ListSelectionModel)e.getSource();
if (!lsm.isSelectionEmpty()) {
setFixedRow();
}
}
});
CustomRenderer custom = new CustomRenderer();
custom.setHorizontalAlignment(JLabel.CENTER);
scrollColumnModel.getColumn(2).setCellRenderer(
custom);
scrollColumnModel.getColumn(3).setCellRenderer(
new CustomButtonRenderer());
CustomButtonEditor customEdit=new
CustomButtonEditor(frame);
scrollColumnModel.getColumn(3).setCellEditor(
customEdit);
headers.add(scrollTable.getTableHeader());
JPanel topPanel = new JPanel();
topPanel.setLayout(new BoxLayout(topPanel,
BoxLayout.X_AXIS));
adjustColumnWidth(
fixedColumnModel.getColumn(0), 100);
JTableHeader fixedHeader=
fixedTable.getTableHeader();
fixedHeader.setAlignmentY(Component.TOP_ALIGNMENT);
topPanel.add(fixedHeader);
topPanel.add(Box.createRigidArea(
new Dimension(2, 0)));
topPanel.setPreferredSize(new Dimension(400, 40));
JPanel headerPanel = new JPanel();
headerPanel.setAlignmentY(Component.TOP_ALIGNMENT);
headerPanel.setLayout(new BorderLayout());
JScrollPane scrollpane = new JScrollPane();
scrollBar = scrollpane.getHorizontalScrollBar();
headerPanel.add(headers, "North");
headerPanel.add(scrollBar, "South");
topPanel.add(headerPanel);
scrollTable.setPreferredScrollableViewportSize(
new Dimension(300,180));
fixedTable.setPreferredScrollableViewportSize(
new Dimension(100,180));
fixedTable.setPreferredSize(
new Dimension(100,180));
innerPort = new JViewport();
innerPort.setView(scrollTable);
scrollpane.setViewport(innerPort);
scrollBar.getModel().addChangeListener(
new ChangeListener() {
public void stateChanged(ChangeEvent e) {
Point q = headers.getViewPosition();
Point p = innerPort.getViewPosition();
int val = scrollBar.getModel().getValue();
p.x = val;
q.x = val;
headers.setViewPosition(p);
headers.repaint(headers.getViewRect());
innerPort.setViewPosition(p);
innerPort.repaint(innerPort.getViewRect());
}
});
scrollTable.getTableHeader(
).setUpdateTableInRealTime(
false);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(
bottomPanel, BoxLayout.X_AXIS));
fixedTable.setAlignmentY(Component.TOP_ALIGNMENT);
bottomPanel.add(fixedTable);
bottomPanel.add(Box.createRigidArea(
new Dimension(2, 0)));
innerPort.setAlignmentY(Component.TOP_ALIGNMENT);
bottomPanel.add(innerPort);
bottomPanel.add(Box.createRigidArea(
new Dimension(2, 0)));
scrollPane= new JScrollPane(bottomPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JViewport outerPort = new JViewport();
outerPort.add(bottomPanel);
scrollPane.setColumnHeaderView(topPanel);
scrollPane.setViewport(outerPort);
scrollTable.setAutoResizeMode(
JTable.AUTO_RESIZE_OFF);
frame.getContentPane().add(scrollPane);
scrollTable.validate();
frame.setSize(450,200);
}
void setFixedRow() {
int index=scrollTable.getSelectedRow();
fixedTable.setRowSelectionInterval(index, index);
}
void setScrollableRow() {
int index=fixedTable.getSelectedRow();
scrollTable.setRowSelectionInterval(index, index);
}
void adjustColumnWidth(TableColumn c, int size) {
c.setPreferredWidth(size);
c.setMaxWidth(size);
c.setMinWidth(size);
}
JList Model
The
Default implementations of the
Three selection modes are available to
public SimpleList() {
JList list;
DefaultListModel deflist;
deflist= new DefaultListModel();
deflist.addElement("element 1");
deflist.addElement("element 2");
list = new JList(deflist);
JScrollPane scroll = new JScrollPane(list);
getContentPane().add(scroll, BorderLayout.CENTER);
}
JTree Model
The
Like the
While
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
public class SimpleTree extends JFrame {
public SimpleTree() {
String[] treelabels = {
"All Auctions",
"Closed Auction",
"Open Auctions"};
Integer[] closedItems = { new Integer(500144),
new Integer(500146),
new Integer(500147)};
Integer[] openItems = { new Integer(500148),
new Integer(500149)};
DefaultMutableTreeNode[] nodes = new
DefaultMutableTreeNode[treelabels.length];
DefaultMutableTreeNode[] closednodes = new
DefaultMutableTreeNode[closedItems.length];
DefaultMutableTreeNode[] opennodes = new
DefaultMutableTreeNode[openItems.length];
for (int i=0; i < treelabels.length; i++) {
nodes[i] = new
DefaultMutableTreeNode(treelabels[i]);
}
nodes[0].add(nodes[1]);
nodes[0].add(nodes[2]);
for (int i=0; i < closedItems.length; i++) {
closednodes[i] = new
DefaultMutableTreeNode(closedItems[i]);
nodes[1].add(closednodes[i]);
}
for (int i=0; i < openItems.length; i++) {
opennodes[i] = new
DefaultMutableTreeNode(openItems[i]);
nodes[2].add(opennodes[i]);
}
DefaultTreeModel model=new
DefaultTreeModel(nodes[0]);
JTree tree = new JTree(model);
JScrollPane scroll = new JScrollPane(tree);
getContentPane().add(scroll, BorderLayout.CENTER);
}
public static void main(String[] args) {
SimpleTree frame = new SimpleTree();
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit(0);
}
});
frame.setVisible(true);
frame.pack();
frame.setSize(150,150);
}
}
The toString method is used to retrieve the value for the
Integer objects in the tree. And although the
DefaultTreeModel is used to maintain the data
in the tree and to add or remove nodes, the DefaultMutableTreeNode
class defines the methods used to traverse through the nodes in the tree.
A primitive search of the nodes in a
The following code expands the parent node if it contains a child node that
matches the search field entered. It uses a call to
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
public class SimpleSearchTree extends JFrame {
JPanel findPanel;
JTextField findField;
JTree tree;
JButton findButton;
DefaultMutableTreeNode[] nodes;
public SimpleSearchTree() {
String[] treelabels = { "All Auctions",
"Closed Auction",
"Open Auctions" };
Integer[] closedItems = { new Integer(500144),
new Integer(500146),
new Integer(500147) };
Integer[] openItems ={ new Integer(500148),
new Integer(500149)};
nodes = new
DefaultMutableTreeNode[treelabels.length];
DefaultMutableTreeNode[] closednodes = new
DefaultMutableTreeNode[closedItems.length];
DefaultMutableTreeNode[] opennodes = new
DefaultMutableTreeNode[openItems.length];
for (int i=0; i < treelabels.length; i++) {
nodes[i] = new
DefaultMutableTreeNode(treelabels[i]);
}
nodes[0].add(nodes[1]);
nodes[0].add(nodes[2]);
for (int i=0; i < closedItems.length; i++) {
closednodes[i] = new
DefaultMutableTreeNode(closedItems[i]);
nodes[1].add(closednodes[i]);
}
for (int i=0; i < openItems.length; i++) {
opennodes[i] = new DefaultMutableTreeNode(
openItems[i]);
nodes[2].add(opennodes[i]);
}
DefaultTreeModel model=new
DefaultTreeModel(nodes[0]);
tree = new JTree(model);
JScrollPane scroll = new JScrollPane(tree);
getContentPane().add(scroll, BorderLayout.CENTER);
findPanel= new JPanel();
findField= new JTextField(10);
findButton= new JButton("find");
findButton.addActionListener (new ActionListener() {
public void actionPerformed (ActionEvent e) {
String field=findField.getText();
if (field != null) {
findNode(findField.getText());
} else {
return;
}
}
});
findPanel.add(findField);
findPanel.add(findButton);
getContentPane().add(findPanel, BorderLayout.SOUTH);
}
public void findNode(String field) {
Enumeration e = nodes[0].depthFirstEnumeration();
Object currNode;
while (e.hasMoreElements()) {
currNode = e.nextElement();
if (currNode.toString().equals(field)) {
TreePath path=new TreePath(((
DefaultMutableTreeNode)currNode).getPath());
tree.makeVisible(path);
tree.setSelectionRow(tree.getRowForPath(path));
return;
}
}
}
public static void main(String[] args) {
SimpleSearchTree frame = new SimpleSearchTree();
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit(0);
}
});
frame.setVisible(true);
frame.pack();
frame.setSize(300,150);
}
}
JTree, JTable and JList are
probably the most common models you will want to customize. But you
can use models such as SingleSelectionModel for general
data manipulation. The SingleSelectionModel class lets you
specify how data is selected in a component.
Custom Cell Rendering
As you learned above, many components have a default cell renderer
to paint each element in a table, tree or list. The default
cell renderer is usually a
A simple custom cell renderer can extend the For example, the following renderer sets the background color of the component if the auction item has received a high number of bids:
class CustomRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(
JTable table,Object value,
boolean isSelected,
boolean hasFocus,
int row, int column) {
Component comp =
super.getTableCellRendererComponent(
table,value,isSelected,hasFocus,
row,column);
JLabel label = (JLabel)comp;
if(((Integer)value).intValue() >= 30) {
label.setIcon(new ImageIcon("Hot.gif"));
} else {
label.setIcon(new ImageIcon("Normal.gif"));
}
return label;
}
}
The renderer is set on a column like this:
CustomRenderer custom = new CustomRenderer();
custom.setHorizontalAlignment(JLabel.CENTER);
scrollColumnModel.getColumn(2).setCellRenderer(
custom);
If the component being displayed inside the JTable column requires
more functionality than is available using a JLabel, you can
create your own TableCellRenderer. This next code
example uses a JButton as the renderer cell.
class CustomButtonRenderer extends JButton
implements TableCellRenderer {
public CustomButtonRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(
JTable table, Object value,
boolean isSelected,
boolean hasFocus, int row,
int column) {
if (isSelected) {
((JButton)value).setForeground(
table.getSelectionForeground());
((JButton)value).setBackground(
table.getSelectionBackground());
} else {
((JButton)value).setForeground(table.getForeground());
((JButton)value).setBackground(table.getBackground());
}
return (JButton)value;
}
}
Like the default JLabel cell renderer, this class relies on
an underlying component (in this case JButton) to do the painting.
Selection of the cell toggles the button colors. As before, the cell renderer
is secured to the appropriate column of the auction table with the
setCellRenderer method:
scrollColumnModel.getColumn(3).setCellRenderer( new CustomButtonRenderer());Alternately, all JButton components can be configured to
use the CustomButtonRenderer in the table with a call to
setDefaultRenderer as follows:
table.setDefaultRenderer( JButton.class, new CustomButtonRenderer()); Custom Cell Editing
In the same way that you can configure how a cell is painted in a
While separate renderers exist for
This next example uses a custom button editor that displays the number
of days left in the auction when the button is double clicked. The
double click to trigger the action is specified by setting the value
class CustomButtonEditor extends DefaultCellEditor {
final JButton mybutton;
JFrame frame;
CustomButtonEditor(JFrame frame) {
super(new JCheckBox());
mybutton = new JButton();
this.editorComponent = mybutton;
this.clickCountToStart = 2;
this.frame=frame;
mybutton.setOpaque(true);
mybutton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}
protected void fireEditingStopped() {
super.fireEditingStopped();
}
public Object getCellEditorValue() {
JDialog jd= new JDialog(frame, "Time left");
Calendar today=Calendar.getInstance();
Calendar end=Calendar.getInstance();
SimpleDateFormat in=new SimpleDateFormat("yyyy-MM-dd");
try {
end.setTime(in.parse(mybutton.getText()));
} catch (Exception e){
System.out.println("Error in date"+mybutton.getText()+e);
}
int days = 0;
while(today.before(end)) {
today.roll(Calendar.DATE,true);
days++;
}
jd.setSize(200,100);
if (today.after(end)) {
jd.getContentPane().add(new JLabel("Auction completed"));
} else {
jd.getContentPane().add(new JLabel("Days left="+days));
}
jd.setVisible(true);
return new String(mybutton.getText());
}
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected,
int row, int column) {
((JButton) editorComponent).setText(((
JButton)value).getText());
if (isSelected) {
((JButton) editorComponent).setForeground(
table.getSelectionForeground());
((JButton) editorComponent).setBackground(
table.getSelectionBackground());
} else {
((JButton) editorComponent).setForeground(
table.getForeground());
((JButton) editorComponent).setBackground(
table.getBackground());
}
return editorComponent;
}
}
Specialized Event Handling
Project Swing uses the event handling classes available
in the AWT API since JDK 1.1. However, some new APIs are available in
the
These methods are often used to request focus on a component after
another event has occurred that might affect the component focus. You
can return the focus by calling the
JButton button =new JButton();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
button.requestFocus();
}
});
Project Swing Directions
While the basic architecture of Project Swing has stayed true
to its original design, many optimizations and improvements
have been made to components like However, as seen in the Analyze a Program section in the Performance chapter, a simple 700x300 table requires nearly half a megabyte of memory when double buffered. The creation of ten tables would probably require swapping memory to disk, severly affecting performance on low-end machines. [TOP]
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||