Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 3 – Model-View-Controller

Similar presentations


Presentation on theme: "Chapter 3 – Model-View-Controller"— Presentation transcript:

1 Chapter 3 – Model-View-Controller
Outline 3.1 Introduction Model-View-Controller Architecture Observable Class and Observer Interface Jlist Jtable Jtree Using DefaultTreeModel Custom TreeModel Implementation

2 Delegate-model architecture Observer design pattern
3.1 Introduction MVC Model-view-controller architecture Data components Presentation components Input-processing components Delegate-model architecture Observer design pattern

3 3.2 Model-View-Controller Architecture
Application data View Graphical presentation components Controller Logic for processing user input Fig. 3.1 Model-view-controller architecture.

4 3.2 Model-View-Controller Architecture (Cont.)
Delegate-model architecture Variant of MVC Combines the view and controller into a single object Fig. 3.2 Delegate-model architecture in Java Swing components.

5 3.3 Observable Class and Observer Interface
Observer design pattern Loose coupling Java implementation of observer design pattern Class java.util.Observable Interface Observer Example Fig. 3.3 AccountManager application MVC architecture.

6 Initialize the name and balance properties.
1 // Account.java 2 // Account is an Observable class that represents a bank 3 // account in which funds may be deposited or withdrawn. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.util.Observable; 8 9 public class Account extends Observable { 10 // Account balance private double balance; 13 // readonly Account name private String name; 16 // Account constructor public Account( String accountName, double openingDeposit ) { name = accountName; setBalance( openingDeposit ); } 23 // set Account balance and notify observers of change private void setBalance( double accountBalance ) { balance = accountBalance; 28 // must call setChanged before notifyObservers to // indicate model has changed setChanged(); 32 // notify Observers that model has changed notifyObservers(); } Fig. 3.4 Account Observable class that represents a bank account. Line 9 Lines Lines Line 31 Line 34 Class Account extends class Observable and acts as a model in the application. Initialize the name and balance properties. Method setBalance changes the model by updating the account balance. Invokes method setChanged of class Observable to set the model’s changed flag. Invokes method notifyObservers of class Observable to notify all Account Obserbers of the change.

7 Return the current Account balance.
36 // get Account balance public double getBalance() { return balance; } 42 // withdraw funds from Account public void withdraw( double amount ) throws IllegalArgumentException { if ( amount < 0 ) throw new IllegalArgumentException( "Cannot withdraw negative amount" ); 50 // update Account balance setBalance( getBalance() - amount ); } 54 // deposit funds in account public void deposit( double amount ) throws IllegalArgumentException { if ( amount < 0 ) throw new IllegalArgumentException( "Cannot deposit negative amount" ); 62 // update Account balance setBalance( getBalance() + amount ); } Return the current Account balance. Fig. 3.4 Account Observable class that represents a bank account. Lines Lines Lines 56-65 Method withdraw subtracts the given amount from the Account balance. Method deposit adds the amount input to the Account balance.

8 Fig. 3.4 Account Observable class that represents a bank account.
66 // get Account name (readonly) public String getName() { return name; } 72 } Fig. 3.4 Account Observable class that represents a bank account.

9 Constructor sets the account member variable to the new Account.
1 // AbstractAccountView.java 2 // AbstractAccountView is an abstract class that represents 3 // a view of an Account. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.util.*; 8 import java.awt.*; 9 10 // Java extension packages 11 import javax.swing.JPanel; 12 import javax.swing.border.*; 13 14 public abstract class AbstractAccountView extends JPanel implements Observer { 16 // Account to observe private Account account; 19 // AbstractAccountView constructor public AbstractAccountView( Account observableAccount ) throws NullPointerException { // do not allow null Accounts if ( observableAccount == null ) throw new NullPointerException(); 27 // update account data member to new Account account = observableAccount; 30 // register as an Observer to receive account updates account.addObserver( this ); 33 Fig. 3.5 AbstractAccountView abstract base class for observing Accounts. Lines Line 32 Constructor sets the account member variable to the new Account. Invokes method addObserver of class Observable to register the newly created AbstractAccountView instance as an Observer of the new Account.

10 34 // set display properties
setBackground( Color.white ); setBorder( new MatteBorder( 1, 1, 1, 1, Color.black ) ); } 38 // get Account for which this view is an Observer public Account getAccount() { return account; } 44 // update display with Account balance protected abstract void updateDisplay(); 47 // receive updates from Observable Account public void update( Observable observable, Object object ) { updateDisplay(); } 53 } Fig. 3.5 AbstractAccountView abstract base class for observing Accounts. Line 46 Lines 49-52 Method updateDisplay is marked abstract, requiring each AbstractAccountView subclass to provide an appropriate implementation for displaying the Account information. Method update invokes method updateDisplay each time an Account notifies the AbstractAccountView of a change.

11 1 // AccountTextView.java
2 // AccountTextView is an AbstractAccountView subclass 3 // that displays an Account balance in a JTextField. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.util.*; 8 import java.text.NumberFormat; 9 10 // Java extension packages 11 import javax.swing.*; 12 13 public class AccountTextView extends AbstractAccountView { 14 // JTextField for displaying Account balance private JTextField balanceTextField = new JTextField( 10 ); 17 // NumberFormat for US dollars private NumberFormat moneyFormat = NumberFormat.getCurrencyInstance( Locale.US ); 21 // AccountTextView constructor public AccountTextView( Account account ) { super( account ); 26 // make balanceTextField readonly balanceTextField.setEditable( false ); 29 // lay out components add( new JLabel( "Balance: " ) ); add( balanceTextField ); 33 updateDisplay(); } Fig. 3.6 AccountTextView for displaying observed Account information in JTextField. Line 13 Lines Line 28 Extends AbstractAccountView to provide a text-based view of Account data. Create a NumberFormat field to format the Account balance as U.S. dollars. Makes the balanceTextField uneditable to prevent users from modifying the balance directly.

12 Set the balanceTextField’s text to the formatted Account balance.
Method updateDisplay implements abstract method updateDisplay of class AbstractAccountView. 36 // update display with Account balance public void updateDisplay() { // set text in balanceTextField to formatted balance balanceTextField.setText( moneyFormat.format( getAccount().getBalance() ) ); } 44 } Fig. 3.6 AccountTextView for displaying observed Account information in JTextField. Lines Lines 41-42 Set the balanceTextField’s text to the formatted Account balance.

13 1 // AccountBarGraphView.java
2 // AccountBarGraphView is an AbstractAccountView subclass 3 // that displays an Account balance as a bar graph. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.awt.*; 8 9 // Java extension packages 10 import javax.swing.*; 11 12 public class AccountBarGraphView extends AbstractAccountView { 13 // AccountBarGraphView constructor public AccountBarGraphView( Account account ) { super( account ); } 19 // draw Account balance as a bar graph public void paintComponent( Graphics g ) { // ensure proper painting sequence super.paintComponent( g ); 25 // get Account balance double balance = getAccount().getBalance(); 28 // calculate integer height for bar graph (graph // is 200 pixels wide and represents Account balances // from -$5,000.00to +$5,000.00) int barLength = ( int ) ( ( balance / ) * 200 ); 33 Fig. 3.7 AccountBarGraphView for rendering observed Account information as a bar graph. Line 12 Lines 21-57 Extends AbstractAccountView to provide a bar-graph view of Account data. Method paintComponent draws a bar graph for the current Account balance.

14 34 // if balance is positive, draw graph in black
g.setColor( Color.black ); g.fillRect( 105, 15, barLength, 20 ); } 39 // if balance is negative, draw graph in red else { g.setColor( Color.red ); g.fillRect( barLength, 15, -barLength, 20 ); } 45 // draw vertical and horizontal axes g.setColor( Color.black ); g.drawLine( 5, 25, 205, 25 ); g.drawLine( 105, 5, 105, 45 ); 50 // draw graph labels g.setFont( new Font( "SansSerif", Font.PLAIN, 10 ) ); g.drawString( "-$5,000", 5, 10 ); g.drawString( "$0", 110, 10 ); g.drawString( "+$5,000", 166, 10 ); 56 } // end method paintComponent 58 // repaint graph when display is updated public void updateDisplay() { repaint(); } 64 // get AccountBarGraphView's preferred size public Dimension getPreferredSize() { return new Dimension( 210, 50 ); Draw the bar graph in black for positive Account balance and in red for negative Account balance. Fig. 3.7 AccountBarGraphView for rendering observed Account information as a bar graph. Lines Lines Line 68 Method updateDisplay invokes method repaint to update the bar graph’s display. Returns a new Dimension object that specifies the AccountBarGraphView’s preferred size as 210 pixels wide by 50 pixels high.

15 } 70 // get AccountBarGraphView's minimum size public Dimension getMinimumSize() { return getPreferredSize(); } 76 // get AccountBarGraphView's maximum size public Dimension getMaximumSize() { return getPreferredSize(); } 82 } Override methods getMinimumSize and getMaximumSize to return the AccountBarGraphView’s preferred size. Fig. 3.7 AccountBarGraphView for rendering observed Account information as a bar graph. Lines 72-81

16 1 // AssetPieChartView.java
2 // AssetPieChartView is an AbstractAccountView subclass that 3 // displays multiple asset Account balances as a pie chart. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.awt.*; 8 import java.util.*; 9 import java.util.List; 10 11 // Java extension packages 12 import javax.swing.*; 13 import javax.swing.border.*; 14 15 public class AssetPieChartView extends JPanel implements Observer { 17 // Set of observed Accounts private List accounts = new ArrayList(); 20 // Map of Colors for drawing pie chart wedges private Map colors = new HashMap(); 23 // add Account to pie chart view public void addAccount( Account account ) { // do not add null Accounts if ( account == null ) throw new NullPointerException(); 30 // add Account to accounts Vector accounts.add( account ); 33 // add Color to Hashtable for drawing Account's wedge colors.put( account, getRandomColor() ); Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Lines Line 35 Method addAccount adds an Account to the List of Accounts shown in the pie chart. Invokes method getRandomColor and adds the random Color to the colors Map.

17 Method removeAccount removes an Account from the pie chart.
36 // register as Observer to receive Account updates account.addObserver( this ); 39 // update display with new Account information repaint(); } 43 // remove Account from pie chart view public void removeAccount( Account account ) { // stop receiving updates from given Account account.deleteObserver( this ); 49 // remove Account from accounts Vector accounts.remove( account ); 52 // remove Account's Color from Hashtable colors.remove( account ); 55 // update display to remove Account information repaint(); } 59 // draw Account balances in a pie chart public void paintComponent( Graphics g ) { // ensure proper painting sequence super.paintComponent( g ); 65 // draw pie chart drawPieChart( g ); 68 Invokes method addObserver of class Account to register the AssetPieChartView for Account updates. Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Line 38 Line 41 Lines Line 48 Lines 61-71 Invokes method repaint to display the pie chart with the new Account’s information. Invokes method deleteObserver of class Account to unregister the AssetPieChartView as an Observer of the Account. Method removeAccount removes an Account from the pie chart. Method paintComponent invokes method drawPieChart and drawLegend to draw the pie chart and chart legend respectively.

18 Method drawPieChart draws a pie-chart wedge for each Account.
// draw legend to describe pie chart wedges drawLegend( g ); } 72 // draw pie chart on given Graphics context private void drawPieChart( Graphics g ) { // get combined Account balance double totalBalance = getTotalBalance(); 78 // create temporary variables for pie chart calculations double percentage = 0.0; int startAngle = 0; int arcAngle = 0; 83 Iterator accountIterator = accounts.iterator(); Account account = null; 86 // draw pie wedge for each Account while ( accountIterator.hasNext() ) { 89 // get next Account from Iterator account = ( Account ) accountIterator.next(); 92 // draw wedges only for included Accounts if ( !includeAccountInChart( account ) ) continue; 96 // get percentage of total balance held in Account percentage = account.getBalance() / totalBalance; 99 // calculate arc angle for percentage arcAngle = ( int ) Math.round( percentage * 360 ); 102 Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Lines Lines Line 94 Method drawPieChart draws a pie-chart wedge for each Account. The while loop calculates the percentage of the total balance held in each Account and draw the wedges. Invokes method includeAccountInChart to determine if the pie chart should include the current Account.

19 The for loop draw the legend item for each Account.
Invokes method fillArc of class Graphics to draw the Account’s pie wedge. // set drawing Color for Account pie wedge g.setColor( ( Color ) colors.get( account ) ); 105 // draw Account pie wedge g.fillArc( 5, 5, 100, 100, startAngle, arcAngle ); 108 // calculate startAngle for next pie wedge startAngle += arcAngle; } } // end method drawPieChart 113 // draw pie chart legend on given Graphics context private void drawLegend( Graphics g ) { Iterator accountIterator = accounts.iterator(); Account account = null; 119 // create Font for Account name Font font = new Font( "SansSerif", Font.BOLD, 12 ); g.setFont( font ); 123 // get FontMetrics for calculating offsets and // positioning descriptions FontMetrics metrics = getFontMetrics( font ); int ascent = metrics.getMaxAscent(); int offsetY = ascent + 2; 129 // draw description for each Account for ( int i = 1; accountIterator.hasNext(); i++ ) { 132 // get next Account from Iterator account = ( Account ) accountIterator.next(); 135 Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Line 107 Lines Lines Lines Method drawLegend draws a legend to show which color represents each Account. Use a FontMetrics object to calculate the heights of characters in the current Font. The for loop draw the legend item for each Account.

20 136 // draw Account color swatch at next offset
g.setColor( ( Color ) colors.get( account ) ); g.fillRect( 125, offsetY * i, ascent, ascent ); 139 // draw Account name next to color swatch g.setColor( Color.black ); g.drawString( account.getName(), 140, offsetY * i + ascent ); } } // end method drawLegend 146 // get combined balance of all observed Accounts private double getTotalBalance() { double sum = 0.0; 151 Iterator accountIterator = accounts.iterator(); Account account = null; 154 // calculate total balance while ( accountIterator.hasNext() ) { account = ( Account ) accountIterator.next(); 158 // add only included Accounts to sum if ( includeAccountInChart( account ) ) sum += account.getBalance(); } 163 return sum; } 166 Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Lines Line 161 Method getTotalBalance calculates the total balance for all included Accounts. Adds the Account’s balance to variable sum if the calculation should include the Account.

21 Method update invokes method repaint to update the pie-chart display.
// return true if given Account should be included in // pie chart protected boolean includeAccountInChart( Account account ) { // include only Asset accounts (Accounts with positive // balances) return account.getBalance() > 0.0; } 175 // get a random Color for drawing pie wedges private Color getRandomColor() { // calculate random red, green and blue values int red = ( int ) ( Math.random() * 256 ); int green = ( int ) ( Math.random() * 256 ); int blue = ( int ) ( Math.random() * 256 ); 183 // return newly created Color return new Color( red, green, blue ); } 187 // receive updates from Observable Account public void update( Observable observable, Object object ) { repaint(); } 193 // get AccountBarGraphView's preferred size public Dimension getPreferredSize() { return new Dimension( 210, 110 ); } 199 Method includeAccountInChart returns a boolean indicating whether the Account should be included in the pie chart. Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart. Lines Lines Lines AssetPieChartView uses method getRandomColor to generate a different Color for each Account in the pie chart. Method update invokes method repaint to update the pie-chart display.

22 200 // get AccountBarGraphView's preferred size
public Dimension getMinimumSize() { return getPreferredSize(); } 205 // get AccountBarGraphView's preferred size public Dimension getMaximumSize() { return getPreferredSize(); } 211 } Fig. 3.8 AssetPieChartView for rendering multiple observed asset Accounts as a pie chart.

23 AccountController implements the controller in the MVC architecture.
1 // AccountController.java 2 // AccountController is a controller for Accounts. It provides 3 // a JTextField for inputting a deposit or withdrawal amount 4 // and JButtons for depositing or withdrawing funds. 5 package com.deitel.advjhtp1.mvc.account; 6 7 // Java core packages 8 import java.awt.*; 9 import java.awt.event.*; 10 11 // Java extension packages 12 import javax.swing.*; 13 14 public class AccountController extends JPanel { 15 // Account to control private Account account; 18 // JTextField for deposit or withdrawal amount private JTextField amountTextField; 21 // AccountController constructor public AccountController( Account controlledAccount ) { super(); 26 // account to control account = controlledAccount; 29 // create JTextField for entering amount amountTextField = new JTextField( 10 ); 32 Fig. 3.9 Accountcontroller for obtaining user input to modify Account information. Line 14 Line 31 AccountController implements the controller in the MVC architecture. Creates a JTextField into which users can enter an amount to withdraw from, or deposit in, the controlled Account.

24 Create a JButton for depositing the given amount into the Account.
// create JButton for deposits JButton depositButton = new JButton( "Deposit" ); 35 depositButton.addActionListener( new ActionListener() { 38 public void actionPerformed( ActionEvent event ) { try { 42 // deposit amount entered in amountTextField account.deposit( Double.parseDouble( amountTextField.getText() ) ); } 47 catch ( NumberFormatException exception ) { JOptionPane.showMessageDialog ( AccountController.this, "Please enter a valid amount", "Error", JOptionPane.ERROR_MESSAGE ); } } // end method actionPerformed } ); 57 // create JButton for withdrawals JButton withdrawButton = new JButton( "Withdraw" ); 60 withdrawButton.addActionListener( new ActionListener() { 63 public void actionPerformed( ActionEvent event ) { Create a JButton for depositing the given amount into the Account. Fig. 3.9 Accountcontroller for obtaining user input to modify Account information. Lines Lines 59-81 Create a JButton for withdrawing the given amount from the Account.

25 try { 67 // withdraw amount entered in amountTextField account.withdraw( Double.parseDouble( amountTextField.getText() ) ); } 72 catch ( NumberFormatException exception ) { JOptionPane.showMessageDialog ( AccountController.this, "Please enter a valid amount", "Error", JOptionPane.ERROR_MESSAGE ); } } // end method actionPerformed } ); 82 // lay out controller components setLayout( new FlowLayout() ); add( new JLabel( "Amount: " ) ); add( amountTextField ); add( depositButton ); add( withdrawButton ); } 90 } Fig. 3.9 Accountcontroller for obtaining user input to modify Account information.

26 1 // AccountManager.java
2 // AccountManager is an application that uses the MVC design 3 // pattern to manage bank Account information. 4 package com.deitel.advjhtp1.mvc.account; 5 6 // Java core packages 7 import java.awt.*; 8 import java.awt.event.*; 9 10 // Java extension packages 11 import javax.swing.*; 12 import javax.swing.border.*; 13 14 public class AccountManager extends JFrame { 15 // AccountManager no-argument constructor public AccountManager() { super( "Account Manager" ); 20 // create account1 with initial balance Account account1 = new Account( "Account 1", ); 23 // create GUI for account1 JPanel account1Panel = createAccountPanel( account1 ); 26 // create account2 with initial balance Account account2 = new Account( "Account 2", ); 29 // create GUI for account2 JPanel account2Panel = createAccountPanel( account2 ); 32 // create AccountPieChartView to show Account pie chart AssetPieChartView pieChartView = new AssetPieChartView(); Fig AccountManager application for displaying and modifying Account information using the model-view-controller architecture. Lines 22 and 28 Lines 25 and 31 Lines 34-35 Creates a new Account with the name Account 1 and a $1, balance, and Account 2 with a $3, balance. Invokes method getAccountPanel of class AccountManager to create a JPanel containing view and controller components for account1 and account2. Create an AssetPieChartView for displaying account1 and account2 information in a pie chart.

27 Create a JPanel with a TitledBorder for the AssetPieChartView.
36 // add both Accounts to AccountPieChartView pieChartView.addAccount( account1 ); pieChartView.addAccount( account2 ); 40 // create JPanel for AccountPieChartView JPanel pieChartPanel = new JPanel(); 43 pieChartPanel.setBorder( new TitledBorder( "Assets" ) ); 46 pieChartPanel.add( pieChartView ); 48 // lay out account1, account2 and pie chart components Container contentPane = getContentPane(); contentPane.setLayout( new GridLayout( 3, 1 ) ); contentPane.add( account1Panel ); contentPane.add( account2Panel ); contentPane.add( pieChartPanel ); 55 setSize( 425, 450 ); 57 } // end AccountManager constructor 59 // create GUI components for given Account private JPanel createAccountPanel( Account account ) { // create JPanel for Account GUI JPanel accountPanel = new JPanel(); 65 // set JPanel's border to show Account name accountPanel.setBorder( new TitledBorder( account.getName() ) ); 69 Invoke method addAccount of class AssetPieChartView to add account1 and account2 to the pie chart. Fig AccountManager application for displaying and modifying Account information using the model-view-controller architecture. Lines Lines Lines Lines Lines 64-68 Create a JPanel with a TitledBorder for the AssetPieChartView. Lay out the JPanels for each account and AssetPieChartView. Method createAccountPanel creates a JPanel containing an AccountController, AccountTextView and AccountBarGraphView for the given Account. Create a JPanel with a TitledBorder to contain the Account’s GUI components.

28 Create an AccountTextView for the Account.
// create AccountTextView for Account AccountTextView accountTextView = new AccountTextView( account ); 73 // create AccountBarGraphView for Account AccountBarGraphView accountBarGraphView = new AccountBarGraphView( account ); 77 // create AccountController for Account AccountController accountController = new AccountController( account ); 81 // lay out Account's components accountPanel.add( accountController ); accountPanel.add( accountTextView ); accountPanel.add( accountBarGraphView ); 86 return accountPanel; 88 } // end method getAccountPanel 90 // execute application public static void main( String args[] ) { AccountManager manager = new AccountManager(); manager.setDefaultCloseOperation( EXIT_ON_CLOSE ); manager.setVisible( true ); } 98 } Create an AccountTextView for the Account. Fig AccountManager application for displaying and modifying Account information using the model-view-controller architecture. Lines Lines Lines Lines 83-85 Create an AccountBarGraphView for the Account. Create an AccountController for the Account. Lay out the AccountTextview, AccountBarGraphView and AccountController components on accountPanel.

29 Fig AccountManager application for displaying and modifying Account information using the model-view-controller architecture. Program output

30 3.4 JList JList ListModel Implements the delegate-model architecture
Delegates for ListModels ListModel Define methods Register/unregister ListDataListener Fig JList and ListModel delegate-model architecture.

31 Add several philosophers to the DefaultListModel.
1 // PhilosophersJList.java 2 // MVC architecture using JList with a DefaultListModel 3 package com.deitel.advjhtp1.mvc.list; 4 5 // Java core packages 6 import java.awt.*; 7 import java.awt.event.*; 8 9 // Java extension packages 10 import javax.swing.*; 11 12 public class PhilosophersJList extends JFrame { 13 private DefaultListModel philosophers; private JList list; 16 // PhilosophersJList constructor public PhilosophersJList() { super( "Favorite Philosophers" ); 21 // create a DefaultListModel to store philosophers philosophers = new DefaultListModel(); philosophers.addElement( "Socrates" ); philosophers.addElement( "Plato" ); philosophers.addElement( "Aristotle" ); philosophers.addElement( "St. Thomas Aquinas" ); philosophers.addElement( "Soren Kierkegaard" ); philosophers.addElement( "Immanuel Kant" ); philosophers.addElement( "Friedrich Nietzsche" ); philosophers.addElement( "Hannah Arendt" ); 32 // create a JList for philosophers DefaultListModel list = new JList( philosophers ); 35 Fig PhilosophersJList application demonstrating Jlist and DefaultListModel. Line 23 Lines Line 34 Creates a new DefaultListModel which provides a basic ListModel implementation. Add several philosophers to the DefaultListModel. Creates a new JList and passes the philosophers DefaultListModel to the JList constructor.

32 Create a JButton for adding new philosophers to the DefaultListModel.
// allow user to select only one philosopher at a time list.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); 39 // create JButton for adding philosophers JButton addButton = new JButton( "Add Philosopher" ); addButton.addActionListener( new ActionListener() { 44 public void actionPerformed( ActionEvent event ) { // prompt user for new philosopher's name String name = JOptionPane.showInputDialog( PhilosophersJList.this, "Enter Name" ); 50 // add new philosopher to model philosophers.addElement( name ); } } ); 56 // create JButton for removing selected philosopher JButton removeButton = new JButton( "Remove Selected Philosopher" ); 60 removeButton.addActionListener( new ActionListener() { 63 public void actionPerformed( ActionEvent event ) { // remove selected philosopher from model philosophers.removeElement( list.getSelectedValue() ); } } Set the JList’s selection mode to allow the user to select only one philosopher at a time. Fig PhilosophersJList application demonstrating Jlist and DefaultListModel. Lines Lines Line 52 Lines 58-71 Create a JButton for adding new philosophers to the DefaultListModel. Invokes method addElement of class DefaultListModel to add the new philosopher to the list. Create a JButton for deleting a philosophers from the DefaultListModel.

33 ); 72 // lay out GUI components JPanel inputPanel = new JPanel(); inputPanel.add( addButton ); inputPanel.add( removeButton ); 77 Container container = getContentPane(); container.add( list, BorderLayout.CENTER ); container.add( inputPanel, BorderLayout.NORTH ); 81 setDefaultCloseOperation( EXIT_ON_CLOSE ); setSize( 400, 300 ); setVisible( true ); 85 } // end PhilosophersJList constructor 87 // execute application public static void main( String args[] ) { new PhilosophersJList(); } 93 } Lay out the GUI components and set JFrame properties for the application window. Fig PhilosophersJList application demonstrating Jlist and DefaultListModel. Lines 74-84

34 Fig. 3.12 PhilosophersJList application demonstrating Jlist and DefaultListModel. Program output

35 3.5 JTable JTable TableModel
Implements the delegate-model architecture Delegates for TableModels TableModel Declare methods Retrieving and modifying data Fig JTable and TableModel delegate-model architecture.

36 3.5 Jtable (Cont.) Fig (Part 1 of 2) TableModel interface methods and descriptions.

37 3.5 JTable (Cont.) Fig (Part 2 of 2) TableModel interface methods and descriptions.

38 Creates the philosophers DefaultTableModel.
1 // PhilosophersJTable.java 2 // MVC architecture using JTable with a DefaultTableModel 3 package com.deitel.advjhtp1.mvc.table; 4 5 // Java core packages 6 import java.awt.*; 7 import java.awt.event.*; 8 9 // Java extension packages 10 import javax.swing.*; 11 import javax.swing.table.*; 12 13 public class PhilosophersJTable extends JFrame { 14 private DefaultTableModel philosophers; private JTable table; 17 // PhilosophersJTable constructor public PhilosophersJTable() { super( "Favorite Philosophers" ); 22 // create a DefaultTableModel to store philosophers philosophers = new DefaultTableModel(); 25 // add Columns to DefaultTableModel philosophers.addColumn( "First Name" ); philosophers.addColumn( "Last Name" ); philosophers.addColumn( "Years" ); 30 // add philosopher names and dates to DefaultTableModel String[] socrates = { "Socrates", "", " B.C." }; philosophers.addRow( socrates ); 34 Fig PhilosophersJTable application demonstrating JTable and DefaultTableModel. Line 24 Lines Lines 32-53 Creates the philosophers DefaultTableModel. Add columns to the DefaultTableModel for the philosophers’ first names, last names and years in which they lived. Create rows for seven philosophers.

39 Create rows for seven philosophers.
String[] plato = { "Plato", "", " B.C." }; philosophers.addRow( plato ); 37 String[] aquinas = { "Thomas", "Aquinas", " " }; philosophers.addRow( aquinas ); 40 String[] kierkegaard = { "Soren", "Kierkegaard", " " }; philosophers.addRow( kierkegaard ); 44 String[] kant = { "Immanuel", "Kant", " " }; philosophers.addRow( kant ); 47 String[] nietzsche = { "Friedrich", "Nietzsche", " " }; philosophers.addRow( nietzsche ); 51 String[] arendt = { "Hannah", "Arendt", " " }; philosophers.addRow( arendt ); 54 // create a JTable for philosophers DefaultTableModel table = new JTable( philosophers ); 57 // create JButton for adding philosophers JButton addButton = new JButton( "Add Philosopher" ); addButton.addActionListener( new ActionListener() { 62 public void actionPerformed( ActionEvent event ) { // create empty array for new philosopher row String[] philosopher = { "", "", "" }; 67 // add empty philosopher row to model philosophers.addRow( philosopher ); Create rows for seven philosophers. Fig PhilosophersJTable application demonstrating JTable and DefaultTableModel. Lines Line 56 Lines 59-72 Creates the JTable that will act as a delegate for the philosophers DefaultTableModel. Create a JButton and ActionListener for adding a new philosopher to the DefaultTableModel.

40 Add the JTable to a JScrollPane.
} ); 73 // create JButton for removing selected philosopher JButton removeButton = new JButton( "Remove Selected Philosopher" ); 77 removeButton.addActionListener( new ActionListener() { 80 public void actionPerformed( ActionEvent event ) { // remove selected philosopher from model philosophers.removeRow( table.getSelectedRow() ); } } ); 89 // lay out GUI components JPanel inputPanel = new JPanel(); inputPanel.add( addButton ); inputPanel.add( removeButton ); 94 Container container = getContentPane(); container.add( new JScrollPane( table ), BorderLayout.CENTER ); container.add( inputPanel, BorderLayout.NORTH ); 99 setDefaultCloseOperation( EXIT_ON_CLOSE ); setSize( 400, 300 ); setVisible( true ); 103 } // end PhilosophersJTable constructor Fig PhilosophersJTable application demonstrating JTable and DefaultTableModel. Lines Lines 96-97 Create a JButton and ActionListener for removing a philosopher from the DefaultTableModel. Add the JTable to a JScrollPane.

41 105 // execute application public static void main( String args[] ) { new PhilosophersJTable(); } 111 } Fig PhilosophersJTable application demonstrating JTable and DefaultTableModel. Program output

42 3.6 JTree JTree TreeModel Implements the delegate-model architecture
Delegates for TreeModels TreeModel Hierarchical data Parents Children Siblings Ancestors Descendents

43 3.6 Jtree (Cont.) Fig JTree showing a hierarchy of philosophers.

44 3.6.1 Using DefaultTreeModel
Interface TreeModel Declares methods for representing tree structure Class DefaultTreeModel Default TreeModel implementation TreeNode MutableTreeNode DefaultMutableTreeNode

45 1 // PhilosophersJTree.java
2 // MVC architecture using JTree with a DefaultTreeModel 3 package com.deitel.advjhtp1.mvc.tree; 4 5 // Java core packages 6 import java.awt.*; 7 import java.awt.event.*; 8 import java.util.*; 9 10 // Java extension packages 11 import javax.swing.*; 12 import javax.swing.tree.*; 13 14 public class PhilosophersJTree extends JFrame { 15 private JTree tree; private DefaultTreeModel philosophers; private DefaultMutableTreeNode rootNode; 19 // PhilosophersJTree constructor public PhilosophersJTree() { super( "Favorite Philosophers" ); 24 // get tree of philosopher DefaultMutableTreeNodes DefaultMutableTreeNode philosophersNode = createPhilosopherTree(); 28 // create philosophers DefaultTreeModel philosophers = new DefaultTreeModel( philosophersNode ); 31 // create JTree for philosophers DefaultTreeModel tree = new JTree( philosophers ); 34 Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Line 30 Line 33 Invoke method createPhilosopherTree to get the root, DefaultMutableTreeNode, which contains all the philosopher nodes. Creates a DefaultTreeModel and passes the philosophersNode DefaultMutableTreeNode to the DefaultTreeModel constructor. Creates a JTree and passes DefaultTreeModel philosophers to the JTree constructor.

46 Create a JButton and an ActionListener for adding a philosopher to the philosophers DefaultTreeModel. // create JButton for adding philosophers JButton addButton = new JButton( "Add" ); addButton.addActionListener( new ActionListener() { 39 public void actionPerformed( ActionEvent event ) { addElement(); } } ); 46 // create JButton for removing selected philosopher JButton removeButton = new JButton( "Remove" ); 50 removeButton.addActionListener( new ActionListener() { 53 public void actionPerformed( ActionEvent event ) { removeElement(); } } ); 60 // lay out GUI components JPanel inputPanel = new JPanel(); inputPanel.add( addButton ); inputPanel.add( removeButton ); 65 Container container = getContentPane(); 67 container.add( new JScrollPane( tree ), BorderLayout.CENTER ); Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Lines 48-59 Create a JButton and an ActionListener for removing a philosopher from the philosophers DefaultTreeModel.

47 70 container.add( inputPanel, BorderLayout.NORTH ); 72 setDefaultCloseOperation( EXIT_ON_CLOSE ); setSize( 400, 300 ); setVisible( true ); 76 } // end PhilosophersJTree constructor 78 // add new philosopher to selected era private void addElement() { // get selected era DefaultMutableTreeNode parent = getSelectedNode(); 84 // ensure user selected era first if ( parent == null ) { JOptionPane.showMessageDialog( PhilosophersJTree.this, "Select an era.", "Error", JOptionPane.ERROR_MESSAGE ); 90 return; } 93 // prompt user for philosopher's name String name = JOptionPane.showInputDialog( PhilosophersJTree.this, "Enter Name:" ); 97 // add new philosopher to selected era philosophers.insertNodeInto( new DefaultMutableTreeNode( name ), parent, parent.getChildCount() ); 102 } // end method addElement 104 Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Lines Line 101 Method addElement gets the currently selected node in the JTree and inserts the new philosopher node as a child of the currently selected node. Invoke method insertNodeInto of class DefaultTreeModel to insert the new philosopher in the model. Invokes method getChildCount of class DefaultMutableTreeNode to get the total number of children in node parent.

48 Create a DefaultMutableTreeNode for the tree’s root.
// remove currently selected philosopher private void removeElement() { // get selected node DefaultMutableTreeNode selectedNode = getSelectedNode(); 110 // remove selectedNode from model if ( selectedNode != null ) philosophers.removeNodeFromParent( selectedNode ); } 115 // get currently selected node private DefaultMutableTreeNode getSelectedNode() { // get selected DefaultMutableTreeNode return ( DefaultMutableTreeNode ) tree.getLastSelectedPathComponent(); } 123 // get tree of philosopher DefaultMutableTreeNodes private DefaultMutableTreeNode createPhilosopherTree() { // create rootNode DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "Philosophers" ); 130 // Ancient philosophers DefaultMutableTreeNode ancient = new DefaultMutableTreeNode( "Ancient" ); rootNode.add( ancient ); 135 ancient.add( new DefaultMutableTreeNode( "Socrates" ) ); ancient.add( new DefaultMutableTreeNode( "Plato" ) ); ancient.add( new DefaultMutableTreeNode( "Aristotle" ) ); 139 Invokes method getSelectedNode to get the currently selected node in the JTree. Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Line 113 Lines Lines Lines Lines Lines Invokes method removeNodeFromParent of class DefaultTreeModel to remove selectedNode from the model. Invokes method getLastSelectedPathComponent of class JTree to get a reference to the currently selected node. Method createPhilosopherTree creates DefaultMutableTreeNodes for several philosophers and for the eras in which the philosophers lived. Create a DefaultMutableTreeNode for the tree’s root. Create a DefaultMutableTreeNode for the ancient era of philosophy and add node ancient as a child of rootNode. Create DefaultMutableTreeNodes for three ancient philosophers and add each as a child of ancient DefaultMutableTreeNode.

49 140 // Medieval philosophers
DefaultMutableTreeNode medieval = new DefaultMutableTreeNode( "Medieval" ); rootNode.add( medieval ); 144 medieval.add( new DefaultMutableTreeNode( "St. Thomas Aquinas" ) ); 147 // Renaissance philosophers DefaultMutableTreeNode renaissance = new DefaultMutableTreeNode( "Renaissance" ); rootNode.add( renaissance ); 152 renaissance.add( new DefaultMutableTreeNode( "Thomas More" ) ); 155 // Early Modern philosophers DefaultMutableTreeNode earlyModern = new DefaultMutableTreeNode( "Early Modern" ); rootNode.add( earlyModern ); 160 earlyModern.add( new DefaultMutableTreeNode( "John Locke" ) ); 163 // Enlightenment Philosophers DefaultMutableTreeNode enlightenment = new DefaultMutableTreeNode( "Enlightenment" ); rootNode.add( enlightenment ); 168 enlightenment.add( new DefaultMutableTreeNode( "Immanuel Kant" ) ); 171 Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Create several additional DefaultMutableTreeNodes for other eras in the history of philosophy and for philosophers in those eras.

50 172 // 19th Century Philosophers
DefaultMutableTreeNode nineteenth = new DefaultMutableTreeNode( "19th Century" ); rootNode.add( nineteenth ); 176 nineteenth.add( new DefaultMutableTreeNode( "Soren Kierkegaard" ) ); 179 nineteenth.add( new DefaultMutableTreeNode( "Friedrich Nietzsche" ) ); 182 // 20th Century Philosophers DefaultMutableTreeNode twentieth = new DefaultMutableTreeNode( "20th Century" ); rootNode.add( twentieth ); 187 twentieth.add( new DefaultMutableTreeNode( "Hannah Arendt" ) ); 190 return rootNode; 192 } // end method createPhilosopherTree 194 // execute application public static void main( String args[] ) { new PhilosophersJTree(); } 200 } Fig PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Lines Create several additional DefaultMutableTreeNodes for other eras in the history of philosophy and for philosophers in those eras.

51 Fig. 3.17 PhilosophersJTree application demonstrating Jtree and DefaultTreeModel. Program output

52 3.6.2 Custom TreeModel Implementation
Implement interface TreeModel Example: FileSystemModel

53 FileSystemModel implements TreeModel interface.
1 // FileSystemModel.java 2 // TreeModel implementation using File objects as tree nodes. 3 package com.deitel.advjhtp1.mvc.tree.filesystem; 4 5 // Java core packages 6 import java.io.*; 7 import java.util.*; 8 9 // Java extension packages 10 import javax.swing.*; 11 import javax.swing.tree.*; 12 import javax.swing.event.*; 13 14 public class FileSystemModel implements TreeModel { 15 // hierarchy root private File root; 18 // TreeModelListeners private Vector listeners = new Vector(); 21 // FileSystemModel constructor public FileSystemModel( File rootDirectory ) { root = rootDirectory; } 27 // get hierarchy root (root directory) public Object getRoot() { return root; } 33 Fig FileSystemModel implementation of interface TreeModel to represent a file system. Line 14 Lines Lines 29-32 FileSystemModel implements TreeModel interface. Constructor takes a File argument for the FileSystemModel root. Returns the FileSystemModel’s root node.

54 34 // get parent's child at given index
public Object getChild( Object parent, int index ) { // get parent File object File directory = ( File ) parent; 39 // get list of files in parent directory String[] children = directory.list(); 42 // return File at given index and override toString // method to return only the File's name return new TreeFile( directory, children[ index ] ); } 47 // get parent's number of children public int getChildCount( Object parent ) { // get parent File object File file = ( File ) parent; 53 // get number of files in directory if ( file.isDirectory() ) { 56 String[] fileList = file.list(); 58 if ( fileList != null ) return file.list().length; } 62 return 0; // childCount is 0 for files } 65 Method getChild returns argument parent’s child node at the given index. Fig FileSystemModel implementation of interface TreeModel to represent a file system. Lines Lines 49-64 Method getChildCount returns the number of children contained in argument parent.

55 Method isLeaf determines if Object argument node is a leaf node.
// return true if node is a file, false if it is a directory public boolean isLeaf( Object node ) { File file = ( File ) node; return file.isFile(); } 72 // get numeric index of given child node public int getIndexOfChild( Object parent, Object child ) { // get parent File object File directory = ( File ) parent; 78 // get child File object File file = ( File ) child; 81 // get File list in directory String[] children = directory.list(); 84 // search File list for given child for ( int i = 0; i < children.length; i++ ) { 87 if ( file.getName().equals( children[ i ] ) ) { 89 // return matching File's index return i; } } 94 return -1; // indicate child index not found 96 } // end method getIndexOfChild 98 Method isLeaf determines if Object argument node is a leaf node. Fig FileSystemModel implementation of interface TreeModel to represent a file system. Lines Lines Lines 86-93 Method getIndexOfChild returns argument child’s index in the given parent node. This for loop search through the list for the given child

56 Create File object targetFile using the new file name.
// invoked by delegate if value of Object at given // TreePath changes public void valueForPathChanged( TreePath path, Object value ) { // get File object that was changed File oldFile = ( File ) path.getLastPathComponent(); 106 // get parent directory of changed File String fileParentPath = oldFile.getParent(); 109 // get value of newFileName entered by user String newFileName = ( String ) value; 112 // create File object with newFileName for // renaming oldFile File targetFile = new File( fileParentPath, newFileName ); 117 // rename oldFile to targetFile oldFile.renameTo( targetFile ); 120 // get File object for parent directory File parent = new File( fileParentPath ); 123 // create int array for renamed File's index int[] changedChildrenIndices = { getIndexOfChild( parent, targetFile) }; 127 // create Object array containing only renamed File Object[] changedChildren = { targetFile }; 130 Method valueForPathChanged is invoked by JTree delegate when the user edits a node in the tree. Fig FileSystemModel implementation of interface TreeModel to represent a file system. Lines Line 105 Lines Line 119 Line 122 Invokes method getLastPathComponent of class TreePath to obtain the File object to rename. Create File object targetFile using the new file name. Invokes method renameTo of class File to rename oldFile to targetFile. Create a File object for the renamed file’s parent directory.

57 Invoke method fireTreeNodesChanged to issue the TreeModelEvent.
// notify TreeModelListeners of node change fireTreeNodesChanged( path.getParentPath(), changedChildrenIndices, changedChildren ); 134 } // end method valueForPathChanged 136 // notify TreeModelListeners that children of parent at // given TreePath with given indices were changed private void fireTreeNodesChanged( TreePath parentPath, int[] indices, Object[] children ) { // create TreeModelEvent to indicate node change TreeModelEvent event = new TreeModelEvent( this, parentPath, indices, children ); 145 Iterator iterator = listeners.iterator(); TreeModelListener listener = null; 148 // send TreeModelEvent to each listener while ( iterator.hasNext() ) { listener = ( TreeModelListener ) iterator.next(); listener.treeNodesChanged( event ); } } // end method fireTreeNodesChanged 155 // add given TreeModelListener public void addTreeModelListener( TreeModelListener listener ) { listeners.add( listener ); } 162 Invoke method fireTreeNodesChanged to issue the TreeModelEvent. Fig FileSystemModel implementation of interface TreeModel to represent a file system. Lines Lines Lines Lines Lines Method fireTreeNodesChanged issues a TreeModelEvent to all registered TreeModelListeners, indicating that nodes in the TreeModel have changed. Create the TreeModel event with the given event data. This while loop iterates through the list of TreeModelListeners, sending the TreeModelEvent to each. Method addTreeModelListener allow TreeModelListeners to register for TreeModelEvents.

58 Inner-class TreeFile overrides method toString of superclass File.
// remove given TreeModelListener public void removeTreeModelListener( TreeModelListener listener ) { listeners.remove( listener ); } 169 // TreeFile is a File subclass that overrides method // toString to return only the File name. private class TreeFile extends File { 173 // TreeFile constructor public TreeFile( File parent, String child ) { super( parent, child ); } 179 // override method toString to return only the File name // and not the full path public String toString() { return getName(); } } // end inner class TreeFile 187 } Method removeTreeModelListener allow TreeModelListeners to unregister for TreeModelEvents. Fig FileSystemModel implementation of interface TreeModel to represent a file system. Lines Lines Inner-class TreeFile overrides method toString of superclass File.

59 Create the uneditable JTextArea for displaying file information.
1 // FileTreeFrame.java 2 // JFrame for displaying file system contents in a JTree 3 // using a custom TreeModel. 4 package com.deitel.advjhtp1.mvc.tree.filesystem; 5 6 // Java core packages 7 import java.io.*; 8 import java.awt.*; 9 import java.awt.event.*; 10 11 // Java extension packages 12 import javax.swing.*; 13 import javax.swing.tree.*; 14 import javax.swing.event.*; 15 16 public class FileTreeFrame extends JFrame { 17 // JTree for displaying file system private JTree fileTree; 20 // FileSystemModel TreeModel implementation private FileSystemModel fileSystemModel; 23 // JTextArea for displaying selected file's details private JTextArea fileDetailsTextArea; 26 // FileTreeFrame constructor public FileTreeFrame( String directory ) { super( "JTree FileSystem Viewer" ); 31 // create JTextArea for displaying File information fileDetailsTextArea = new JTextArea(); fileDetailsTextArea.setEditable( false ); 35 Fig FileTreeFrame application for browsing and editing a file system using JTree and FileSystemModel. Lines 33-34 Create the uneditable JTextArea for displaying file information.

60 Create a FileSystemModel whose root is directory.
// create FileSystemModel for given directory fileSystemModel = new FileSystemModel( new File( directory ) ); 39 // create JTree for FileSystemModel fileTree = new JTree( fileSystemModel ); 42 // make JTree editable for renaming Files fileTree.setEditable( true ); 45 // add a TreeSelectionListener fileTree.addTreeSelectionListener( new TreeSelectionListener() { 49 // display details of newly selected File when // selection changes public void valueChanged( TreeSelectionEvent event ) { File file = ( File ) fileTree.getLastSelectedPathComponent(); 57 fileDetailsTextArea.setText( getFileDetails( file ) ); } } ); // end addTreeSelectionListener 63 // put fileTree and fileDetailsTextArea in a JSplitPane JSplitPane splitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane( fileTree ), new JScrollPane( fileDetailsTextArea ) ); 69 getContentPane().add( splitPane ); Create a FileSystemModel whose root is directory. Fig FileTreeFrame application for browsing and editing a file system using JTree and FileSystemModel. Lines Line 41 Line 44 Lines Lines Lines 65-69 Creates a JTree for the FileSystemModel. Sets the JTree’s editable property to true, to allow users to rename files displayed in the JTree. Create a TreeSelectionListener to listen for TreeSelectionEvents in the JTree. Get the selected File object from the JTree. Create a JSplitPane to separate the JTree and JTextArea.

61 71 setDefaultCloseOperation( EXIT_ON_CLOSE ); setSize( 640, 480 ); setVisible( true ); } 76 // build a String to display file details private String getFileDetails( File file ) { // do not return details for null Files if ( file == null ) return ""; 83 // put File information in a StringBuffer StringBuffer buffer = new StringBuffer(); buffer.append( "Name: " + file.getName() + "\n" ); buffer.append( "Path: " + file.getPath() + "\n" ); buffer.append( "Size: " + file.length() + "\n" ); 89 return buffer.toString(); } 92 // execute application public static void main( String args[] ) { // ensure that user provided directory name if ( args.length != 1 ) System.err.println( "Usage: java FileTreeFrame <path>" ); 100 // start application using provided directory name else new FileTreeFrame( args[ 0 ] ); } 105 } Fig FileTreeFrame application for browsing and editing a file system using JTree and FileSystemModel. Lines 78-91 Method getFileDetails takes a File argument and returns a String containing the File’s name, path and length.

62 Fig FileTreeFrame application for browsing and editing a file system using JTree and FileSystemModel. Program output


Download ppt "Chapter 3 – Model-View-Controller"

Similar presentations


Ads by Google