Model/View Applications in Swing
Supplementary readingJava tutorial (http://www.cs.auckland.ac.nz/JavaCache/tutorial)
Trail: “Learning the Java Language”Lesson: “Swing features and concepts”
The JFC Swing Tutorial, Walrath & CampioneCopy available in University library, call no. 005.133 Java W22
Model / View with Swing COMPSCI 230 Software Design & Construction 2
A typical model/view application
JTable component instance
Model
JCombobox component instance
Stats view
Graphical viewChange request
Update notification
Many desktop applications provide multiple views of some data model.
Invariant : all views should offer a mutually consistent representation of the model.
Model / View with Swing COMPSCI 230 Software Design & Construction 3
Model/view GUIs with Swing
Contemporary GUI frameworks, like Swing, are based on a separable model architecture
Unlike the AWT, in Swing all components (JComponents) have separate models
COMPSCI 230 Software Design & Construction 3
JTable << interface >>TableModel
JComboBox << interface >>ComboBoxModel
JTree << interface >>TreeModel
JList << interface >>ListModel
JTable component instance
TableModelinstance
setValueAt( row, col )
tableChanged( event )
Model / View with Swing COMPSCI 230 Software Design & Construction 4
A multiview text editor
COMPSCI 230 Software Design & Construction 4
JFrame
TextEditor
JTextArea<< interface >>Document
JMenuBar JMenu JMenuItem
<< interface >>ActionListener
PlainDocument
TextEditor instance
JTextArea instance
JMenuItem is a special kind of button.
PlainDocument is an implementation of the Document (model) interface.
JTextArea instance
view 1
view 2 PlainDocument instance
1 2
Model / View with Swing COMPSCI 230 Software Design & Construction 5
The basis for a model
COMPSCI 230 Software Design & Construction 5
Course
addStudentResult( r : StudentResult ) : voidgetResultAt( index : int ) : StudentResultiterator( ) : Iterator< StudentResult > size( ) : intsetAssessmentPolicy( p : AssesmentPolicy ) : void
<< interface >>AssessmentPolicy
calculate( r : StudentResult ) : Percentage
StudentResult
studentIDsurnameforenameexamMarktestMarkassignmentMarkoverallMark
calculateOverallMark( p : AssessmentPolicy ) : void
Courseinstance
StudentResultinstance
SeventyTenTwentyPolicyinstance
StudentResultinstance
resultspolicy
Implemented by several classes, each implementing a different policy.E.g. class SeventyTenTwentyPolicy calculates a students overall mark as
exam * 0.7 + test * 0.1 + assignment * 0.2
1: setAssessmentPolicy( … )
2: calculateOverallMark( policy )
3: calculate( this )
4: calculateOverallMark( policy )
5: calculate( this )
Model / View with Swing COMPSCI 230 Software Design & Construction 6COMPSCI 230 Software Design & Construction 6
<< interface >>TableModel
addTableModelListener( l : TableModelListener ) : voidgetColumnCount( ) : intgetRowCount( ) : intgetValueAt( row : int, col : int ) : ObjectisCellEditable( row : int, col : int ) : booleanremoveTableModelListener( l : TableModelListener ) : voidsetValueAt( value : Object, row : int, col : int ) : void
JTable
Course
addStudentResult( r : StudentResult ) : voidgetResultAt( index : int ) : StudentResultiterator( ) : Iterator< StudentResult > size( ) : intsetAssessmentPolicy( p : AssesmentPolicy ) : void
<< interface >>AssessmentPolicy
calculate( r : StudentResult ) : Percentage
StudentResult
studentIDsurnameforenameexamMarktestMarkassignmentMarkoverallMark
calculateOverallMark( p : AssessmentPolicy ) : void
So, we can plug in any kind of TableModel object into a JTable and the JTable will provide a visual representation of the TableModel instance.
But unfortunately class Course isn’t a TableModel implementation. What can we do?
Model / View with Swing COMPSCI 230 Software Design & Construction 7COMPSCI 230 Software Design & Construction 7
<< interface >>TableModel
addTableModelListener( l : TableModelListener ) : voidgetColumnCount( ) : intgetRowCount( ) : intgetValueAt( row : int, col : int ) : ObjectisCellEditable( row : int, col : int ) : booleanremoveTableModelListener( l : TableModelListener ) : voidsetValueAt( value : Object, row : int, col : int ) : void
JTable
Course
addStudentResult( r : StudentResult ) : voidgetResultAt( index : int ) : StudentResultiterator( ) : Iterator< StudentResult > size( ) : intsetAssessmentPolicy( p : AssesmentPolicy ) : void
<< interface >>AssessmentPolicy
calculate( r : StudentResult ) : Percentage
CourseAdapterCourse instance
CourseAdapter instance
JTable instance
TableModel query message
Course query message
StudentResult
studentIDsurnameforenameexamMarktestMarkassignmentMarkoverallMark
calculateOverallMark( p : AssessmentPolicy ) : void
Model / View with Swing COMPSCI 230 Software Design & Construction 8
<< interface >>TableModel
addTableModelListener( l : TableModelListener ) : voidgetColumnCount( ) : intgetRowCount( ) : intgetValueAt( row : int, col : int ) : ObjectisCellEditable( row : int, col : int ) : booleanremoveTableModelListener( l : TableModelListener ) : voidsetValueAt( value : Object, row : int, col : int ) : void
AbstractTableModel
fireTableCellUpdated( row : int, col : int ) : voidfireTableDataChanged( ) : voidfireTableRowsDeleted( startRow : int, endRow : int ) : voidfireTableRowsInserted( startRow : int, endRow : int ) : voidfireTableRowsUpdated( startRow : int, endRow : int ) : void
Default TableModel implementation
AbstractTableModel provides a partial implementation of the TableModel interface.
Code reuse methods (add/removeTableModelListener( )) are implemented.
Code reuse methods (fireXX( )) are implemented and provide a convenient facility for subclasses to fire events to listeners of a TableModel.
Concept reuse methods that must be implemented in order for a JTable to extract sufficient information to build the visual representation.
Concept reuse methods that should be implemented in order for the model to be edited by a JTable component.
Model / View with Swing COMPSCI 230 Software Design & Construction 9
public class CourseAdapter extends AbstractTableModel { private Course adaptee; // Implement composition relationship.
public CourseAdapter( Course course ) { adaptee = course; }
public int getColumnCount() { return 7; // Columns are: Student ID, surname, forename, exam, // test, assignment, overall. }
public int getRowCount() { return adaptee.size(); // Number of rows to display equates to the // number of students in the Course object. }
public Object getValueAt( int row, int col ) { // row identifies a particular student held within the Course instance. // col identifies a student attribute, e.g. surname, exam mark etc. StudentResult result = ( StudentResult )adaptee.getResultAt( row ); Object value = null;
// Get the StudentResult attribute at the specified column. switch( col ) { case 0: // Student ID. value = result.studentID; break; case 1: // Surname. value = result.studentSurname; break; … case 6: // Overall. … } return value; }}
Course instance
CourseAdapter instance
1: getValueAt( 4, 3 )
2: getResultAt( 4 )
JTable instance
3: getExamMark( )
Dave Bell
StudentResultinstance
67
Model / View with Swing COMPSCI 230 Software Design & Construction 10
TableModelListener<< interface >>TableModel
addTableModelListener( l : TableModelListener ) : voidgetColumnCount( ) : intgetRowCount( ) : intgetValueAt( row : int, col : int ) : ObjectisCellEditable( row : int, col : int ) : booleanremoveTableModelListener( l : TableModelListener ) : voidsetValueAt( value : Object, row : int, col : int ) : void
AbstractTableModel
fireTableCellUpdated( row : int, col : int ) : voidfireTableDataChanged( ) : voidfireTableRowsDeleted( startRow : int, endRow : int ) : voidfireTableRowsInserted( startRow : int, endRow : int ) : voidfireTableRowsUpdated( startRow : int, endRow : int ) : void
JTable
<< interface >>TableModelListener
tableChanged( event : TableModelEvent ) : void
Any party that is interested in changes to a TableModel implements the TableModelListener interface.
When a TableModel changes its state, it creates a TableModelEvent, which describes how the TableModel has changed, and sends a tableChanged message to each registered listener.
JTable offers a visual representation of a TableModel and thus implements the TableModelListener interface.
Model / View with Swing COMPSCI 230 Software Design & Construction 11
public class CourseAdapter extends AbstractTableModel {
// As before plus …
public boolean isCellEditable( int row, int col ) { boolean editable = false;
if( col > 2 && col < 6 ) // Allow editing of exam, test and assignment columns. editable = true; return editable; }
public void setValueAt( Object value, int row, int col ) { StudentResult result = adaptee.getResultAt(row);
switch( col ) { case 3: // Exam. adaptee.updateStudentResult(result, … ); break; case 4: // Test. … break; case 5: // Assignment. … break; } fireTableRowsUpdated( row, row ); }
public void setAssessmentPolicy(AssessmentPolicy policy) { adaptee.setAssessmentPolicy(policy); fireTableDataChanged(); }}
1: setAssessmentPolicy( … )
2: setAssessmentPolicy( … )
Course instance
CourseAdapter instance
JTable instance
3: fireTableDataChanged( )
4: create
TableModelEvent instance (evt)
5: tableChanged( evt )
For each student, recalculate overall mark (see slide 5)
All rows changed
Model / View with Swing COMPSCI 230 Software Design & Construction 12
JTable
<< interface >>TableModelListener
<< interface >>TableModel
AbstractTableModel
CourseAdapter
CourseDistributionPanel
JPanel
StatisticsPanel
DistributionPanelAdapter StatisticsPanelAdapter
StudentResult
Application classes
Adapter classes
Framework classes
Model / View with Swing COMPSCI 230 Software Design & Construction 13
Key instances & messages
JTable instanceCourseAdapter instance
Course instance
DistributionPanelAdapter instance
StatisticsPanelAdapter instance
DistributionPanel instance
StatisticsPanel instance
model listenersadaptee
adapteeadaptee
1: setValueAt 2: getResultAt
TableModelEvent instance (evt)
3: create
4: tableChanged( evt )
5: tableChanged( evt )
6: repaint 8: repaint
7: tableChanged( evt )
Model / View with Swing COMPSCI 230 Software Design & Construction 14
Integrity / enforcing design intent All registered listeners (views) of a model are guaranteed to be
notified when the model changes its state => Invariant of model/view applications is satisfied
Reuse A model/view framework promotes reuse
GUI component classes implement application independent behaviour for viewing / editing an application model
Model interfaces are intended to be implemented by adapter classes that allow application classes to be reused without modification
Maintainability It’s very easy to add new views
All that’s required is to write a new implementation of the listener interface Existing model and view classes need not be changed
Flexibility Views can easily be added to (and removed from) a model at run-time
What can we say to sum up the benefits of the model/view approach?