JMU JMU - Department of Computer Science
Help Tools
Lab: Gaining Experience with GUI Construction


Instructions: Answer the following questions one at a time. After answering each question, check your answer (by clicking on the check-mark icon if it is available) before proceeding to the next question.

Getting Ready: Before going any further, you should:

  1. Setup your development environment.
  2. Download the following files:
    to an appropriate directory/folder (e.g., the course downloads directory/folder). In most browsers/OSs, the easiest way to do this is by right-clicking/control-clicking on each of the links above and then selecting Save as... or Save link as....

1. Working with Windows: This part of the lab will give you some experience with windows.
  1. Execute CashMachine. What happened?


    A window (with nothing in it) appeared.
    Expand
  2. Click on the icon that closes the window (which will vary with the operating system you are using). What happened? (Be careful!)


    The window closed but the application did not terminate.
    Expand
  3. Terminate CashMachine.
  4. Add the following statement to the end of the constructor in the PINPadWindow class.
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
  5. Execute CashMachine and again click on the icon that closes the window (which will vary with the operating system you are using). What happened now?


    The window closed and the application terminated.
    Expand
2. Layout: This part of the lab will give you some experience constructing GUI components and working with layout managers.
  1. Suppose you needed to layout a Container in a table/matrix that contains four rows and three columns. What layout manager would you use?


    A GridLayout constructed as follows:
    new GridLayout(4, 3);
    
    Expand
  2. Complete the setupLayout() method in the NumberPad class. Your implementation must contain 12 JButton objects and be consistent with the following wireframe.
    NumberPad.png
    What code did you add? (Hint: The "ERASE TO THE LEFT" character is Unicode character 232B.)


        private void setupLayout()
        {
            JButton      button;
            
            setLayout(new GridLayout(4, 3));
            
            for (int i=1; i<=9; i++)
            {
                button = new JButton(String.format("%1d", i));
                add(button);
            }
    
            button = new JButton("\u232B");
            add(button);
    
            button = new JButton("0");
            add(button);
    
            button = new JButton("C");
            add(button);
        }
    
    Expand
  3. Your implementation may include duplicate code. If so, correct your implementation by adding one or more private methods (and, perhaps, a "constant"). What does your code look like now?


        private static final Font BUTTON_FONT = new Font("DejaVu Sans", Font.PLAIN, 12);
        
    
    
        private void addButton(String text)
        {
            JButton button = new JButton(text);
            button.setFont(BUTTON_FONT);
            add(button);
        }
    
        private void setupLayout()
        {
            setLayout(new GridLayout(4, 3));
            
            for (int i=1; i<=9; i++) addButton(String.format("%1d", i));
            addButton("\u232B");
            addButton("0");
            addButton("C");
        }
    
    Expand
  4. Modify the setupLayout() method in the PINPadWindow class so that it now constructs a NumberPad and adds it to the content pane. What code is in the setupLayout() method now?


        private void setupLayout()
        {
            setSize(300, 300); 
            setTitle("ATM");        
    
            Container contentPane = getContentPane();
            NumberPad numberPad = new NumberPad();
            contentPane.add(numberPad);
        }
    
    Expand
  5. Execute CashMachine. How big is the window and how big are the buttons?


    The window is approximately 300x300 and each button is about 100x75.
    Expand
  6. Terminate CashMachine.
  7. The "ERASE TO THE LEFT" character might not have rendered properly if the default font on your system does not include it. Find a font that does support it (by searching the Internet), construct an appropriate Font object, and call the setFont() method on each JButton (passing it the Font you created).
  8. Add the following to the bottom of the constructor in the PINPadWindow class.
            pack();
    

    Execute CashMachine. How big is the window now?


    The window is just big enough to hold the buttons. That's what the pack() method in the Window class does.
    Expand
  9. Resize the window. What happens and why?


    The buttons get larger, continuing to fill the entire window because that's how the GridLayout works.
    Expand
  10. Add the following to the bottom of the constructor in the PINPadWindow class.
            setResizable(false);
    

    Execute CashMachine. Can you re-size the window?


    No.
    Expand
3. Specializing GUI Components: This part of the lab will give you some experience adding capabilities to GUI components using specialization.
  1. Create a Display class that specializes the JLabel class. The default constructor must call the single-parameter constuctor in the parent class passing it " " and then call the setBorder() method passing it an etched border (see BorderFactory javax.swing.BorderFactory for help in creating a border).

    What code is in the class?


    import javax.swing.*;
    
    public class Display extends JLabel
    {
        public Display()
        {
            super(" ");
            setBorder(BorderFactory.createEtchedBorder());
        }
    }
    
    Expand
4. More Layout: This part of the lab will give you more experience with layout.
  1. Modify the setupLayout() method in the PINPadWindow class so that it adds a Display above the NumberPad in a fashion that is consistent with the following wireframe.
    CashMachine.png

    What code is in this method now? (Note: Remember to construct an appropriate layout manager and pass it to setLayout().)


        private void setupLayout()
        {
            setSize(300, 300); 
            setTitle("ATM");        
    
            Container contentPane = getContentPane();
            contentPane.setLayout(new BorderLayout());
    
            Display display = new Display();        
            contentPane.add(display, BorderLayout.NORTH);
            
            NumberPad numberPad = new NumberPad();
            contentPane.add(numberPad, BorderLayout.CENTER);
        }
    
    Expand
5. Event-Driven Programming: This part of the lab will give you some experience with event-driven programming.
  1. Modify the Display class so that it now realizes the ActionListener java.awt.event.ActionListener interface. Specifically, it should respond to ActionEvent objects that have an action command of "C" by clearing its contents (i.e., by setting its text to " ") and any other ActionEvent objects by appending the action command to its current contents.

    What code is in this class now?


    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Display extends    JLabel
                         implements ActionListener
    {
        private static final String     CLEAR = "C";    
    
        public Display()
        {
            super(" ");
            setBorder(BorderFactory.createEtchedBorder());
        }
    
        public void actionPerformed(ActionEvent ae)
        {
            String ac = ae.getActionCommand();
            if (ac.equals(CLEAR)) setText(" ");
            else                  setText(getText()+ac);
        }
    }
    
    Expand
  2. Modify the constructor in the NumberPad class so that it is now passed an ActionListener object that it stores in a private attribute named listener, before calling setupLayout(). (Obviously, you;ll also need to add the attribute). What code is in the constructor now?


       public NumberPad(ActionListener listener)
        {
            super();
    
            this.listener = listener;        
            setupLayout();
        }
    
    Expand
  3. Modify the addButton() method in the NumberPad class so that it makes listener an ActionListener on the button it is adding.

    What code did you add?


            button.addActionListener(listener);
    
    Expand
  4. Modify the setupLayout() method in the PINPadWindow class so that the Display is now an ActionListener on the NumberPad.

    What code did you change?


            numberPad = new NumberPad(display);
    
    Expand
  5. Execute CashMachine. What happens when you click on the various buttons?


    Except for the "ERASE TO THE LEFT" button, everything works as expected. However, I don't like the fact that the Display is aligned left.
    Expand
  6. Modify the Display class so that the text is aligned to the right. What change did you make?


    I changed the call to the parent's constructor as follows.
            super(" ", SwingConstants.RIGHT);
    
    Expand
  7. Now, modify the Display so that:
    1. The "ERASE TO THE LEFT" button works as expected.
    2. When the Display is empty it displays the text "Enter your PIN".
    3. When the Display is not empty it shows (an appropriate number of) asterisks rather than the PIN.
    What code is in your Display class now?


    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Display extends    JLabel
                         implements ActionListener
    {
        private String contents;    
    
        private static final String     CLEAR = "C";
        private static final String     ERASE_TO_THE_LEFT = "\u232B";    
    
        public Display()
        {
            super(" ", SwingConstants.RIGHT);
            setBorder(BorderFactory.createEtchedBorder());
            contents = "";
            updateDisplay();
        }
    
        public void actionPerformed(ActionEvent ae)
        {
            String ac = ae.getActionCommand();
    
            if (ac.equals(CLEAR))
            {
                contents = "";
                setText("Enter your PIN");
            }
            else if (ac.equals(ERASE_TO_THE_LEFT)) 
            {
                if (!contents.equals(""))
                    contents = contents.substring(0, contents.length()-1);
            }
            else
            {
                contents += ac;
            }
            updateDisplay();
        }
        
        private void updateDisplay()
        {
            if (contents.equals("")) setText("Enter your PIN");
            else 
            {
                String    asterisks = "";
                
                for (int i=0; i<contents.length(); i++) asterisks += "*";
                setText(asterisks);
            }
        }
    }
    
    
    Expand
  8. Now, modify the Display so that the text is gray when "Enter your PIN" is displayed and red when the asterisks are displayed. What code did you change?


        private void updateDisplay()
        {
            if (contents.equals("")) 
            {
                setForeground(Color.GRAY);            
                setText("Enter your PIN");
            }
            else 
            {
                String    asterisks = "";
                
                for (int i=0; i<contents.length(); i++) asterisks += "*";
                setForeground(Color.RED);            
                setText(asterisks);
            }
        }
    
    Expand
6. Keyboard Support: There are two ways to add keyboard support to GUIs. One is to use objects that implement the KeyListener java.awt.event.KeyListener interface. The other is to use key bindings. This section of the lab will help you understand how to use the latter.
  1. Read the documentation for the Action javax.swing.Action interface. What is the purpose of this interface?


    It provides a way to create a class that has everthing that an ActionListener needs for it to be associated with one or more subjects (e.g., the text on the subject, the icon on the subject).
    Expand
  2. The fist step in using key bindings is to create an ActionListener that will be associated with a particular key (really the String on that key) and will programmatically "click" the corresponding JButton (e.g., that will cause the 1 key to click the 1 button. Create a class named ClickAction that extends AbstractAction with a constructor that is passed a JButton and stores it in an approrpiate attribute. What code did you add?


    import java.awt.event.*;
    import javax.swing.*;
     
    public class ClickAction extends AbstractAction
    {
        private JButton       button;
        
     
        public ClickAction(JButton button)
        {
            super();
            this.button = button;
        }
    }
    
    Expand
  3. Add an actionPerformed() method that has the button grab the focus and click itself (staying pressed for 50 milliseconds). What code did you add?


        public void actionPerformed(ActionEvent evt)
        {
            button.grabFocus();
            button.doClick(50);
        }
    
    Expand
  4. The next step in the process is to add a ClickAction to the NumberPad object's ActionMap javax.swing.ActionMap for each JButton. To do this you need to modify the NumebrPad object's addButton() method. Specifically, you need to get the NumberPad object's ActionMap, construct a ClickAction for the JButton and put it in the map. What code did you add to the addButton() method?


            ActionMap actionMap = this.getActionMap();
            actionMap.put(text, new ClickAction(button));
    
    Expand
  5. The final step in the process is to setup the InputMap javax.swing.InputMap for the NumberPad that maps each key stroke of interest to the text on the corresponding button (e.g., maps KeyStroke.getKeyStroke(KeyEvent.VK_0, 0) to "0"). What code did you add to the constructor in the NumberPad class?


            InputMap inputMap  = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_0, 0), "0");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), "1");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), "2");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_3, 0), "3");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_4, 0), "4");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_5, 0), "5");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_6, 0), "6");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_7, 0), "7");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_8, 0), "8");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_9, 0), "9");
    
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "\u232B");
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0), "C");
    
    Expand
  6. Compile and test your code.

Copyright 2023