140 likes | 155 Views
Model/View Applications in Swing. Supplementary reading Java tutorial ( http://www.cs.auckland.ac.nz/JavaCache/tutorial ) Trail: “Learning the Java Language” Lesson: “Swing features and concepts” The JFC Swing Tutorial, Walrath & Campione
E N D
Model/View Applications in Swing Supplementary reading Java tutorial (http://www.cs.auckland.ac.nz/JavaCache/tutorial)Trail: “Learning the Java Language”Lesson:“Swing features and concepts”The JFC Swing Tutorial, Walrath & Campione Copy available in University library, call no. 005.133 Java W22
Stats view Model Graphical view A typical model/view application Many desktop applications provide multiple views of some data model. Invariant : all views should offer a mutually consistent representation of the model. Change request JTable component instance Update notification JCombobox component instance COMPSCI 230 Software Design & Construction
Model/view GUIs with Swing JTable << interface >> TableModel • Contemporary GUI frameworks, like Swing, are based on a separable model architecture • Unlike the AWT, in Swing all components (JComponents) have separate models JComboBox << interface >> ComboBoxModel setValueAt( row, col ) JTree << interface >> TreeModel JTable component instance TableModel instance JList << interface >> ListModel tableChanged( event ) COMPSCI 230 Software Design & Construction COMPSCI 230 Software Design & Construction 3
A multiview text editor JFrame JMenuItem is a special kind of button. << interface >> ActionListener << interface >> Document 1 2 TextEditor JMenuBar PlainDocument is an implementation of the Document (model) interface. JMenu JTextArea view 1 TextEditor instance JTextArea instance PlainDocument JMenuItem view 2 PlainDocument instance JTextArea instance COMPSCI 230 Software Design & Construction COMPSCI 230 Software Design & Construction 4
1: setAssessmentPolicy( … ) 2: calculateOverallMark( policy ) StudentResult instance results policy 4: calculateOverallMark( policy ) Course instance StudentResult instance 5: calculate( this ) SeventyTenTwentyPolicy instance 3: calculate( this ) The basis for a model StudentResult studentID surname forename examMark testMark assignmentMark overallMark calculateOverallMark( p : AssessmentPolicy ) : void Course addStudentResult( r : StudentResult ) : void getResultAt( index : int ) : StudentResult iterator( ) : Iterator< StudentResult > size( ) : int setAssessmentPolicy( p : AssesmentPolicy ) : void << interface >> AssessmentPolicy calculate( r : StudentResult ) : Percentage 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 COMPSCI 230 Software Design & Construction COMPSCI 230 Software Design & Construction 5
JTable << interface >> TableModel addTableModelListener( l : TableModelListener ) : void getColumnCount( ) : int getRowCount( ) : int getValueAt( row : int, col : int ) : Object isCellEditable( row : int, col : int ) : boolean removeTableModelListener( l : TableModelListener ) : void setValueAt( value : Object, row : int, col : int ) : 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? StudentResult studentID surname forename examMark testMark assignmentMark overallMark calculateOverallMark( p : AssessmentPolicy ) : void Course addStudentResult( r : StudentResult ) : void getResultAt( index : int ) : StudentResult iterator( ) : Iterator< StudentResult > size( ) : int setAssessmentPolicy( p : AssesmentPolicy ) : void << interface >> AssessmentPolicy calculate( r : StudentResult ) : Percentage COMPSCI 230 Software Design & Construction COMPSCI 230 Software Design & Construction 6
JTable << interface >> TableModel addTableModelListener( l : TableModelListener ) : void getColumnCount( ) : int getRowCount( ) : int getValueAt( row : int, col : int ) : Object isCellEditable( row : int, col : int ) : boolean removeTableModelListener( l : TableModelListener ) : void setValueAt( value : Object, row : int, col : int ) : void JTable instance TableModel query message CourseAdapter instance Course query message Course instance CourseAdapter StudentResult studentID surname forename examMark testMark assignmentMark overallMark calculateOverallMark( p : AssessmentPolicy ) : void Course addStudentResult( r : StudentResult ) : void getResultAt( index : int ) : StudentResult iterator( ) : Iterator< StudentResult > size( ) : int setAssessmentPolicy( p : AssesmentPolicy ) : void << interface >> AssessmentPolicy calculate( r : StudentResult ) : Percentage COMPSCI 230 Software Design & Construction COMPSCI 230 Software Design & Construction 7
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 methodsthat must be implemented in order for a JTable to extract sufficient information to build the visual representation. Concept reuse methodsthat should be implemented in order for the model to be edited by a JTable component. << interface >> TableModel addTableModelListener( l : TableModelListener ) : void getColumnCount( ) : int getRowCount( ) : int getValueAt( row : int, col : int ) : Object isCellEditable( row : int, col : int ) : boolean removeTableModelListener( l : TableModelListener ) : void setValueAt( value : Object, row : int, col : int ) : void AbstractTableModel fireTableCellUpdated( row : int, col : int ) : void fireTableDataChanged( ) : void fireTableRowsDeleted( startRow : int, endRow : int ) : void fireTableRowsInserted( startRow : int, endRow : int ) : void fireTableRowsUpdated( startRow : int, endRow : int ) : void COMPSCI 230 Software Design & Construction
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; } } JTable instance 1: getValueAt( 4, 3 ) CourseAdapter instance 67 2: getResultAt( 4 ) Course instance 3: getExamMark( ) StudentResult instance Dave Bell COMPSCI 230 Software Design & Construction
TableModelListener << interface >> TableModelListener tableChanged( event : TableModelEvent ) : void << interface >> TableModel addTableModelListener( l : TableModelListener ) : void getColumnCount( ) : int getRowCount( ) : int getValueAt( row : int, col : int ) : Object isCellEditable( row : int, col : int ) : boolean removeTableModelListener( l : TableModelListener ) : void setValueAt( value : Object, row : int, col : int ) : void JTable 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. AbstractTableModel fireTableCellUpdated( row : int, col : int ) : void fireTableDataChanged( ) : void fireTableRowsDeleted( startRow : int, endRow : int ) : void fireTableRowsInserted( startRow : int, endRow : int ) : void fireTableRowsUpdated( startRow : int, endRow : int ) : void COMPSCI 230 Software Design & Construction
JTable instance CourseAdapter instance Course instance 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( … ) 5: tableChanged( evt ) 3: fireTableDataChanged( ) 2: setAssessmentPolicy( … ) 4: create For each student, recalculate overall mark (see slide 5) All rows changed TableModelEvent instance (evt) COMPSCI 230 Software Design & Construction
Framework classes JPanel << interface >> TableModelListener JTable << interface >> TableModel AbstractTableModel DistributionPanelAdapter StatisticsPanelAdapter CourseAdapter Adapter classes DistributionPanel StatisticsPanel Course StudentResult Application classes COMPSCI 230 Software Design & Construction
TableModelEvent instance (evt) 3: create 1: setValueAt 2: getResultAt 4: tableChanged( evt ) 7: tableChanged( evt ) 5: tableChanged( evt ) 6: repaint 8: repaint Key instances & messages CourseAdapter instance JTable instance Course instance adaptee model listeners adaptee adaptee DistributionPanelAdapter instance StatisticsPanelAdapter instance DistributionPanel instance StatisticsPanel instance COMPSCI 230 Software Design & Construction
What can we say to sum up the benefits of the model/view approach? • 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 COMPSCI 230 Software Design & Construction