F27SB2 Software Development 2 Lecture 8: State Diagram & GUI Example
Overview software address book each entry consists of: – name – street – town – postcode – telephone number – address
Overview load entries from file save entries to file controls to: – move forwards/backwards through entries – add new entry – delete entry – change entry – quit
Overview inspect/ modify extend NEW/ clear data fields ADD/ insert new entry CHANGE/ modify entry; update display UP;DOWN/ update display.../ load file; display first entry QUIT/ update file
Design suggests: – JButton s for controls – JLabel s for headings/prompts – JTextField s for changeable details separate JPanel s for controls & details – may want to vary number of controls or details
Design NEW ADD CHANGE UP DOWN QUIT name street town postcode phone JFrame JPanel JButton s JLabel s JTextField s
Entry representation represent entry within program as a class 6 String fields use individual variables? advantage – can see which field is which disadvantage – need lots of individual assignments/accesses – might want to add fields – e.g. given/family names; country
Entry representation use array of string to hold details for each entry advantage – manipulate with for loops disadvantage – may forget which array element corresponds to which field – does this matter?
Entry representation class Entry { String [] details; final int MAXDETAILS = 6; public Entry(String [] newdetails) { details = new String[MAXDETAILS]; for(int i=0;i<MAXDETAILS;i++) details[i]=newdetails[i]; }
Entry representation outside program, keep Entry s in a text file save each Entry to file when program ends public void writeEntry(PrintWriter file) { for(int i=0;i<MAXDETAILS;i++) file.println(details[i]); }
Entry representation within program, hold Entry s in an array class Address extends JFrame implements ActionListener {... Entry [] entries; int entryno; final int MAXENTRIES = 100; identify file final String addressbook = "addressbook.txt";
Entry representation load array from file when program starts void readEntries() throws IOException { BufferedReader file; entries = new Entry[MAXENTRIES]; entryno=0; String [] details = new String[MAXDETAILS]; open file try { file = new BufferedReader (new FileReader(addressbook)); } catch(FileNotFoundException e){ return; }
Entry representation read 1st line from file => name for 1st Entry String line = file.readLine(); loop until file empty while(line!=null) {
Entry representation program will check for space in array before additions using New however, user may extend text file by hand outside program if(entryno==MAXENTRIES) { System. out.println("More than "+MAXENTRIES+ " entries."); System.exit(0); }
Entry representation set up name, read rest of details, build new Entry and add to array details[0]=line; for(int i=1;i<MAXDETAILS;i++) details[i]=file.readLine(); entries[entryno]=new Entry(details); entryno++;
Entry representation try to read next line of file => name for next Entry line = file.readLine(); } close file at end file.close();... } not quite enough for readEntries...
Interface & Initialisation require: – JButton for each control action array of JButton – text for JButton s array of String class Address extends JFrame implements ActionListener { JButton [] actions; String [] actionText = {“New”,”Add”,”Change”,”Delete”, “Forward”,”Back”,”Quit”}; final int MAXACTIONS = 7;
Interface & Initialisation NB need to remember JButton map – actions[0] => New – actions[1] => Add – actions[2] => Change – actions[3] => Delete – actions[4] => Forward – actions[5] => Back – actions[6] => Quit
Interface & Initialisation JLabel s for field titles – array of JLabel text for JLabel – array of String JLabel [] headings; String [] text ={“Name”,”Street”,”Town”,”Postcode”, “Telephone”,” ”};
Interface & Initialisation JTextField s for modifiable field details – array of JTextField JTextField [] details; final int MAXDETAILS = 6; JPanel s for Entry on interface and controls JPanel entry,controls;
Interface & Initialisation methods to initialise JLabel s, JButton s and JTextField s: JTextField setupTextField(String s,Container c) { JTextField t = new JTextField(s); t.setFont(new Font(“Sansserif”,Font.PLAIN,18)); t.setBackground(Color.white); c.add(t); t.addActionListener(this); return t; }
JButton setupButton(String s,Container c) { JButton b = new JButton(s); b.setFont(new Font(“Sansserif”,Font.PLAIN,18)); b.setBackground(Color.white); c.add(b); b.addActionListener(this); return b; } JLabel setupLabel(String s,Container c) { JLabel l = new JLabel(s,JLabel.CENTER); l.setFont(new Font(“Sansserif”,Font.PLAIN,18)); l.setBackground(Color.white); c.add(l); return l; } Interface & Initialisation
constructor initialises interface with empty details public Address() { entry = new JPanel(new GridLayout(MAXDETAILS,2)); headings = new JLabel[MAXDETAILS]; details = new JTextField[MAXDETAILS]; for(int i=0;i<MAXDETAILS;i++) { headings[i]=setupLabel(text[i],entry); details[i]=setupTextField(“”,entry); } add(“Center”,entry); Interface & Initialisation
controls = new JPanel(new GridLayout(MAXACTIONS,1)); actions = new JButton[MAXACTIONS]; for(int i=0;i<MAXACTIONS;i++) actions[i]= setupButton(actionText[i],controls);... add(“West”,controls); } Interface & Initialisation
method to display Entry from array in interface void showEntry(Entry e) { for(int i=0;i<MAXDETAILS;i++) details[i].setText(e.details[i]); } need to keep track of current Entry i.e. Entry on display in interface int current; Interface & Initialisation
at end of readEntries, need to set current Entry to first in array and display it... file.close(); if(entryno!=0) { current=0; showEntry(entries[0]); } Interface & Initialisation
write array back to file at end of program void doQuit() { if(entryno==0) System.exit(0); try { PrintWriter file = new PrintWriter(new FileWriter(addressbook)); for(int i=0;i<entryno;i++) entries[i].writeEntry(file); file.close(); System.exit(0); } catch(IOException e){}; } Quit control
Add controls use a pair of JButton s New – clear all details JTextField s – disable all JButton s apart from “Add” – user adds new details to empty JTextField s Add – copy details from JTextField s to new Entry – insert Entry into array in ascending name order – enable all JButton s and disable “Add”
Add controls disable Add in Address() at start actions[1].setEnabled(false); for New – check if space in array void doNew() { int i; if(entryno==MAXENTRIES) { System.out.println("More than "+ MAXENTRIES+" entries."); return; }
Add controls – disable all JButton s and enable Add actions[0].setEnabled(false); actions[1].setEnabled(true); for(i=2;i<MAXACTIONS;i++) actions[i].setEnabled(false); – set all JTextField s to “” for(i=0;i<MAXDETAILS;i++) details[i].setText(""); }
Add controls for Add… – get details from JTextField s and make new Entry void doAdd() { int i,j; String [] newdetails = new String[MAXDETAILS]; for(i=0;i<MAXDETAILS;i++) newdetails[i]=details[i].getText(); Entry e = new Entry(newdetails);
Add controls – search array to find first with name > new Entry ’s name for(i=0;i<entryno;i++) if(e.details[0].compareTo(entries[i].details[0])<0) break; – move all Entry s down one place in array for(j=entryno;j>i;j--) entries[j]=entries[j-1];
Add controls – put new Entry in place, update count and remember new as current entries[i]=e; current=i; entryno++; – enable all JButton s and disable Add actions[0].setEnabled(true); actions[1].setEnabled(false); for(i=2;i<MAXACTIONS;i++) actions[i].setEnabled(true); }
Move controls for Forward... – check if at end of array – increment current and display Entry void doForward() { if(entryno==0 || current+1==entryno) return; current++; showEntry(entries[current]); }
Move controls for Back… – check if at start of array – decrement current and display Entry void doBack() { if(entryno==0 || current==0) return; current--; showEntry(entries[current]); }
Delete control for Delete… – ignore if no Entry s void doDelete() { if(entryno==0) return; – move all Entry s after current back one place in array and decrement number of Entry s for(int i=current;i<entryno-1;i++) entries[i]=entries[i+1]; entryno--;
Delete control – if no Entry s then set all interface details to “” if(entryno==0) { for(int i=0;i<MAXDETAILS;i++) details[i].setText(""); return; } – if deleted Entry was last Entry then reset current to new last Entry if(current==entryno) current--;
Delete control – display Entry in current position usually Entry following deleted Entry showEntry(entries[current]); }
Change control for Change… – user can alter any JTextField at any time – change only takes effect if Change selected – can’t just copy details back to current – if name changes then entry order changes – delete current entry – can’t use doDelete - updates text fields – add details from text fields as new entry - use doAdd
Change control void doChange() { if(entryno==0) return; for(int i=current;i<entryno-1;i++) entries[i]=entries[i+1]; entryno--; if(entryno==0) { for(int i=0;i<MAXDETAILS;i++) details[i].setText(""); return; } if(current==entryno) current--; doAdd(); }
Change control void doChange() { if(entryno==0) return; for(int i=current;i<entryno-1;i++) entries[i]=entries[i+1]; entryno--; if(entryno==0) { for(int i=0;i<MAXDETAILS;i++) details[i].setText(""); return; } if(current==entryno) current--; doAdd(); }
actionPerformed int state; final int INSPECT = 0; final int EXTEND = 1; public void actionPerformed(ActionEvent e) { switch(state) { case INSPECT: if(e.getSource()==actions[0]) // NEW { doNew(); state = EXTEND; return; } if(e.getSource()==actions[2]) // CHANGE { doChange(); return; } if(e.getSource()==actions[3]) // DELETE { doDelete(); return; }
actionPerformed if(e.getSource()==actions[4]) // UP { doForward(); return; } if(e.getSource()==actions[5]) // DOWN { doBack(); return; } if(e.getSource()==actions[6]) // QUIT { doQuit(); return; } case EXTEND: if(e.getSource()==actions[1]) // ADD { doAdd(); state = INSPECT; return; } }
main class TestAddress { public static void main(String [] args) throws IOException { Address a; a = new Address(); a.setSize(600,280); a.setTitle("Address"); a.setVisible(true); a.addWindowListener (new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); a.readEntries(); a.state = a.INSPECT; }
Program
Improvements/extensions 1.add a JLabel to display system messages 2.after NEW, check that all requisite fields have been filled in before ADD can succeed 3.add a search facility 4.check for duplicate entries before CHANGE or ADD can succeed