Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

What Is Swing?


Sep98: Java Q&A

Mukul is a project manager at DigitalFocus. He can be contacted at [email protected].


Swing is a collection of lightweight components built on top of the Java Abstract Windowing Toolkit (AWT). Swing components are based on the Lightweight UI Framework that became part of the Java AWT with the JDK 1.1. (With JDK 1.2, Swing becomes part of the Java Foundation Classes.) Lightweight components do not have "peers" (platform-dependent code) associated with them. They are thus unencumbered by problems brought on by peers, such as platform-dependent bugs. Swing, which creates its components using pluggable look-and-feel modules written in pure Java, doesn't make use of native code. Pluggable look-and-feel lets an application running on heterogeneous platforms have the same look-and-feel across dissimilar machines. This feature also has the ability to change the visual representation of the application dynamically at run time.

As Figure 1 illustrates, the architecture of Swing components is based on the model-view-controller (MVC) paradigm. In a Swing component, models provide information that specifies the component's value, views manage the way in which the component and its data are drawn on the screen, and controllers modify the information maintained by the model in response to user input.

In actuality, Swing uses a modified MVC pattern, in which the view and controller are combined into a single component called the "delegate object." This reduces the number of communication paths between the model, view, and controller, and the number of components to be created and managed. In short, both the appearance and behavior of a component can be managed together, using just one delegate object. The delegate object encapsulates the look-and-feel of each Swing component.

JTable and Swing

In this article, I'll focus on JTable, one of Swing's lightweight components, and explain how the MVC architecture applies to it. JTable is a view controller -- it knows how to draw itself and acts as a controller for the various editors, with which it can be fitted for the Table data model. JTable is a user-interface component that presents data in a two-dimensional table format. Tables (or grids) implemented using JTable can be easily connected to databases through JDBC. JTable provides features such as reassessable and reorderable columns, a flexible and configurable row/column selection mechanism, cells that can be displayed using any components through the use of the Renderer interface (whereby cells can be equipped with Checkbox controls, choice controls, and so on), cells that can be edited using an editor interface, and support for large data sets.

All delegates extend the ComponentUI interface and all delegates are also part of the com.sun.java.swing.plaf package. ComponentUI defines how a delegate renders a JComponent (the abstract JComponent class is the root class of all Swing components). Under Windows, for example, the default delegate for JButton is BasicButtonUI. This is loaded by default when an application runs on Windows. To change the look-and-feel to, say, a Java look-and-feel, you'd use something like Listing One. You can also define new looks-and-feels and create custom Swing components.

Swing Table Framework

The Swing Table framework is an example of MVC architecture. The table data model is represented by the TableModel interface and the view-controller part is handled by the JTable class. The TableModel interface specifies how to describe the data in the table cells. These methods are what JTable uses to interrogate the data model. The TableModel interface also specifies methods required for maintaining a TableModelListener list. TableModelListener defines the interface for an object that listens to changes in a TableModel. The method tableChanged(TableModelEvent), for instance, tells listeners the exact range of cells, rows, or columns that changed. The JTable component listens to changes in the data model and implements the TableModelListener interface.

The AbstractTableModel adapter class implements most of the methods in TableModel and maintains the listener list. This simplifies the task of creating a data model. By subclassing from this adapter, only the methods getColumnCount, getRowCount, getValueAt, and setValueAt need to be implemented. Since the setValueAt method is present as a stub in AbstractDataModel, it doesn't have to be implemented. setValueAt sets the value in the data model. The listener must also be notified about this change. This is done by invoking the fireTableCellUpdated method, defined in AbstractDataModel.

The following methods should be implemented by the data model if you extend from AbstractDataModel:

  • public abstract int getRowCount() returns the number of records managed by the data source object. JTable uses this method to determine how many rows it should create and display. This method should be quick, since it is frequently called by JTable.
  • public abstract int getColumnCount() returns the number of columns managed by the data source object. A JTable uses this to determine how many columns it should create and display upon initialization.
  • public abstract Object getValueAt(int rowIndex, int columnIndex) returns an attribute value for the cell at columnIndex and rowIndex. Listing Two, for example, sets an attribute value for the record in the cell at columnIndex and rowIndex. aValue is the new value.

JTable can be configured with cell renderers and editors, either default or specialized, for various columns. The values get displayed and edited using the particular renderer and editor for that column. For example, a column could be configured with a combobox editor having a list of colors, and the renderer could display that value in the background color based on the selected value.

The renderer does not get added as a component; for example, there is no method JTable.add(renderer). The table has built-in renderer components, and these get set to whatever renderer(s) you supply, through the method JTable.setCellRenderer(MyRenderer). The renderer's only purpose is to supply the methods needed to draw the values given to it by the JTable object.

A renderer can either implement the TableCellRenderer interface or use a default implementation by using the DefaultCellRenderer and passing it the JComponent to be used for drawing; for instance, cellRenderer=new DefaultCellRenderer(new JLabel());. Listing Three is the only method defined by the TableCellRenderer interface. The table invokes this method for each cell, then calls paint() on the returned Component object. If the application needs a renderer (such as a colored button), it could implement it using Listing Four.

The table can use editors such as textfield, combobox, and the like, for editing individual cells, and the concept is similar to the concept for renderers: The editor has to implement the TableCellEditor interface; otherwise, a default editor can be used (DefaultCellEditor) and the appropriate JComponent (JTextField, JComboBox, and so on) passed to it.

A JTable Example

The JTable example I offer here presents automobile information in a tabular format. To run the example, unzip the classes (available electronically; see "Resource Center," page 3) and put them in your classpath. Then type java TableExample.AppMain to start the application. All the classes are in the package TableExample. AppMain creates the application controller class, AppController. Figure 2 shows the classes and their relationships. (This example was written using Swing 0.7. The code will not run on previous releases of Swing, as the JTable API has undergone changes in Version 0.7. The most recent Swing release, information, and documentation can be found at http:// www.javasoft.com/.)

AppController instantiates the data model, which is represented by the AutomobileDataModel class. It then instantiates the view (which is the class AppView) passing it the argument TableUI class, which creates the table and its renderers and editor.

The controller is responsible for all user input event handling, which consists of mouse click events. However, since the JTable is a view-controller component, it automatically handles all these events. The default renderer for String type data is JLabel, and the default editor is JTextField. Consequently, if the data model consists of String type data and allows cell editing (returns True from isCellEditable(int r,int c)), then, by default, the table would allow textfield editing of the cells. No renderers or editors would need to be set on the table. Another default editor type for the table is JCheckBox, which gets set if the data type in a column is Boolean. It is rendered as a checkbox.

The constructor for the AppController class calls the init method, which instantiates the AutomobileDataModel object. This is the data model for the table. The table queries the data model by invoking its getValueAt(int row,int column) method; this is how the view part of the table gets the value to render. If there is a specific renderer set for that column, then the renderer interprets and draws the value. If the model implements the isCellEditable method (Listing Five), then the table cells can be edited using whatever editors are set up for table columns.

The AutomobileDataModel extends the AbstractTableModel class, which provides default implementations for most of the methods in the TableModel interface. It takes care of the management of listeners and provides conveniences for generating TableModelEvents and dispatching them to the listeners. You override the isCellEditable method to enable cell editing.

The TableUI class instantiates the JTable and creates renderers for the Year, Automobile Make, and Details columns. It also creates a combobox editor for the Year column. The data model instance is passed to TableUI, which creates the table through the call JTable table=new JTable(dataModel).

JTable has an attribute autoCreateColumnsFromModel that is set to True by default. The JTable table=new JTable(dataModel) call has the effect that the table queries the data model to build the default set of columns. You can check this by uncommenting the System.out.println in setValueAt method. The setModel method (in JTable) could also be used, as in table=new JTable(); table.setModel(dataModel);.

These two approaches trigger the method fireTableChanged(null), which generates a TableModelEvent that is caught by the JTable (as it implements a TableModelListener). The table then creates the columns and fills in the data from the data model that is passed to it in the TableModelEvent.

An alternative approach is to call new JTable(), invoking setAutoCreateColumnsFromModel(false), and then creating the columns using table.addColumn method.

The TableUI creates a renderer for Automobile Make, which displays the value in a certain font based on that value. It extends from JLabel so you can override label's setText method and plug in your implementation. You have implemented the renderer as an inner class as its part of the table user interface.

The renderer is associated with the column by invoking TableColumn.setCellRenderer(rendererForColumn). For the Automobile Make column, I used a DefaultCellRenderer passing it the label object.

In most cases, you could use a DefaultCellRenderer, but for situations in which you need to override the paint method (such as to toggle display of the component on and off based on value), it would be necessary to provide your implementation of the TableCellRenderer and override whatever methods to get the desired effect. Figure 3 shows the table (note the background colors in the Year and Details column as a result of using renderers).

The TableUI also uses a combobox editor, something that's simple to create. The editor is associated with the column by invoking TableColumn.setCellEditor(editorForColumn).

Whenever any cell in the Year column is clicked, a combobox with a selectable list drops down. When a selection is made from this box, the table calls the setValueAt method to set that value in the data model. This is possible since JTable implements the CellEditorListener interface, and it knows the results of editing.

The Details column uses our implementation of a TableCellRenderer. It basically simulates checkbox behavior. When the cell value is No and the user clicks on the checkbox, the mouse handler calls setValueAt, passing it a value Yes, and setValueAt calls the fireTableCellUpdate method. This method causes the table to invoke getValueAt for that cell and then to pass that value to the renderer. The renderer, which extends JCheckBox, calls setSelected(true) if the value passed is Yes; otherwise, it calls setSelected(false).

The mouse handler is implemented in AppController and is set up to listen for mouse click events only (extends from MouseAdapter). It is passed a reference to the table in the constructor, from which it gets the data model. On receiving a mouse click event, it gets the value for the cell that was clicked by invoking the code in Listing Six. Based on the value (Yes or No), it then calls setValueAt passing it the appropriate value.

Conclusion

The example I've presented illustrates the concept of a table as a view-controller component and brings to light the various mechanisms that the table uses to achieve its rich functionality. With its wide capabilities and feature set, JTable offers a good solution to developers, IDE builders, and toolset vendors.

DDJ

Listing One

try {    UIManager.setLookAndFeel (
        "com.sun.java.swing.jlf.JLFLookAndFeel");
} catch( java.lang.ClassNotFoundException e) {
    // look and feel class not found, can't change l&f
}

Back to Article

Listing Two

public abstract void setValueAt(Object aValue, int rowIndex, int columnIndex)

Back to Article

Listing Three

Component getTableCellRendererComponent(JTable table,   
                 Object valueToBeDisplayed, boolean whetherCellSelected,
                 int rowNumber, int columnNumber) 

Back to Article

Listing Four

class ButtonRenderer extends JButton implements TableCellRenderer {
           Component   getTableCellRendererComponent(JTable table, 
               Object valueToBeDisplayed, boolean whetherCellSelected,
               int rowNumber, int columnNumber) {
if(valueToBeDisplayed == "red") {
    this.setBackground = Color.red;
} else {
     this.setBackground = Color.white;
}
                    return this;
            }
    }

Back to Article

Listing Five

  public boolean isCellEditable(int row, int col) {
     return true;
}

Back to Article

Listing Six

        rowIndex = table.rowAtPoint(mouseEvent.getPoint());
        colIndex = table.columnAtPoint(mouseEvent.getPoint());
        dataModel.getValueAt(rowIndex, colIndex); ALGORITHM ALLEY

Back to Article


Copyright © 1998, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.