CPSC 170 Lab 4
Interfaces, More Listeners, & GUIs

As usual, create a lab4 subdirectory for today's lab, open this document in FireFox, and start emacs.

Checkboxes and Radio Buttons

Up until now, you hav been creating simple GUIs with panels, textboxes and buttons. Recall that these programs used ActionListeners to carry out certain actions when a button was pressed. In today's exercise you will see two new GUI components (checkboxes and radio buttons) along with their listeners (ItemListeners and ActionListeners respectively).

The files StyleOptions.java and StyleOptionsPanel.java are adapted from Listings 5.22 and 5.23 of the text. (A variable fontSize is used rather than an integer literal in various instances and the variable style is an instance variable rather than local to the itemStateChanged method). Save these files to your directory and compile and run StyleOptions.java to see how it works. This is the driver; StyleOptionsPanel.java contains the code for the GUI.

Now you will add a set of 3 radio buttons to let the user choose among three font sizes. The method of adding the radio buttons will be very similar to that in the QuoteOptionPanel class (Listing 5.25 of the text), so study this example before you continue.

Do the following to add the radio buttons to the GUI:

  1. Declare three objects small, medium, and large of type JRadioButton.

  2. Instantiate the button objects labeling them "Small Font," "Medium Font," "Large Font." Initialize the large font button to true. Set the background color of the buttons to cyan.

  3. Instantiate a button group object and add the buttons to it.

  4. Radio buttons produce action events so you need to add an inner class (name it SizeListener) to implement ActionListener and listen for radio button clicks. The code for actionPerformed can be similar to that in the QuoteListener in Listing 5.25. (Or if you prefer, you can use the isSelected method to see which button was selected instead of getting and checking the source.) You need to set the fontSize variable (use 16 for small, 24 for medium, and 32 for large) in the if statement, then call the setFont method to set the font for the saying object.

  5. In StyleGUI() instantiate a SizeListener and add it to each button. Also add each button to the panel.

  6. Compile and run the program. Note that as the font size changes the checkboxes and buttons re-arrange themselves in the panel. To fix this, you could put each of the major components into separate panels (you don't have to right now). There are also other ways to address this problem that we will address later in the course.

Mouse Events

Programs can also respond to mouse events -- moving and clicking the mouse -- that are not tied to any particular GUI component. Java divides these into two categories: mouse events and mouse motion events. A MouseListener (a class that implements the MouseListener interface) must provide bodies for methods corresponding to each of the mouse events. A MouseMotionListener (implementing the MouseMotionListener interface) must provide bodies for methods corresponding to each of the mouse motion events. These events are described in section 7.9 of the text.

  1. File MousePlay.java contains a program that displays a message saying when the mouse button is pressed and released. It uses MousePlayPanel.java to define the JPanel that is actually displayed. Save these files to your directory, run MousePlay to see how it works, and open MousePlayPanel in emacs. Note that MousePlayPanel has an inner class MousePlayListener that implements MouseListener by giving bodies for the five MouseListener methods. Even though nothing is done when the mouse is clicked or when it enters or exits, those methods still appear with empty bodies -- this is required for the MouseListener interface to be implemented.

    Modify MousePlayPanel so that it also prints appropriate messages when the mouse is clicked, when it enters the panel, and when it exits. Play with the resulting program until you understand how the mouse events are generated.

  2. Files Dots.java and DotsPanel.java contain revised versions of the code in listings 7.18 and 7.19 in the text. Dots draws a dot every time the uses presses the mouse button; only the current dot is shown in the revised version. Save these files to your directory and run Dots to see how it works. Then modify DotsPanel as follows:
    1. Make the dots that appear alternately red and green. To do this, you will need to make the following changes:
      • Add an instance variable to hold the dot color.
      • Add an instance variable to count the number of dots that have been drawn.
      • When the mouse is pressed, in addition to setting the draw point you need to update the counter and change the dot color to green if the counter is odd, to red if it is even. Then call repaint.
    2. Modify your code so that the dot is drawn where the mouse is released, not where it is pressed.

    Print DotsPanel.java to turn in.

  3. File Circles.java sets up a GUI that creates and draws a circle as defined in Circle.java of random size and color at each mouse press Each circle replaces the one before it. The code to handle the mouse press and do the drawing is in CirclePanel.java. Save these files to your directory, compile them and run them and experiment with the GUI. Then modify these files as described below.
    1. This program creates a new circle each time -- you can tell because each circle is a different color and size. Write a method void move(Point p) for your Circle class that takes a Point and moves the circle so its center is at that point. Now modify your CirclesListener class (defined inside CirclePanel) so that instead of creating a new circle every time the user presses the mouse, if a circle already exists it moves the existing circle to the point where the user pressed (the "presspoint"). If no circle exists, a new one should be created at the presspoint. So now a circle of the same color and size should move around the screen.

    2. Write a method boolean isInside(Point p) for your Circle class that takes a Point and tells whether it is inside the circle. A point is inside the circle if its distance from the center is less than the radius. Recall that the distance between two points (x1,y1) and (x2,y2) is sqrt((x2-x1)2+(y2-y1)2.

    3. Now modify the mousePressed method of CirclesListener so that the GUI behaves as follows:
      • If there is no circle (i.e., it is null) and the user presses anywhere, a new (random) circle should be drawn at the presspoint.
      • If there is a circle on the screen and the user presses inside that circle, the circle should go away. (Hint: To make the circle go away, set it to null and repaint.)
      • If there is a circle on the screen and the user presses somewhere else, the circle should move to that point (no change from before).
      So the logic for mousePressed should look like this:

        
        if there is currently no circle
           create a new circle at the presspoint
        else if the click is inside the circle
           make the circle go away
        else
           move the circle to the presspoint
      
        repaint
      

    4. Add bodies for the mouseEntered and mouseExited methods so that when the mouse enters the JPanel the background turns white, and when it exits the background turns blue. Remember that you can set the background color with the setBackground method.

    5. Now modify the code in CirclePanel.java so that a circle is drawn when the user presses a mouse, but the user can drag it around as long as the mouse button is depressed. If the mouse button is released and then pressed again, it deletes or moves the circle as before. You will need to make the following changes:
      1. In the CirclePanel constructor, make the CirclesListener object listen for both mouse events and mouse motion events. Don't create two separate listeners -- use the same object for both.

      2. Make the CirclesListener class implement the MouseMotionListener interface in addition to the MouseListener interface. This requires two steps:
        • Note in the header that CirclesListener implements MouseMotionListener.
        • Add bodies for the two MouseMotionListener methods, mouseDragged and mouseMoved. In mouseDragged, simply move the circle to the point returned by the getPoint method of the MouseEvent and repaint. (Remember that you already wrote a move() method for the Circle class.) Provide an empty body for mouseMoved.

      Print CirclesListener.java and Circle.java to turn in.

More Mouse Events

Create a new directory in your lab 4 directory called Cards. Download the file Cards.tgz to that directory and extract it using the command
tar xzf Cards.tgz
You will find that this file contained Run the program to understand how it works.

In the previous examples, we attached a mouselistener to the whole panel. It is often more beneficial to attach mouse listeners to individual GUI components. In this case, we would like to attach a mouse listener to each of the JLabels so that when the mouse clicks on the picture of a card, the program prints out the name of the card. This may seem easy at first, but there are a couple of hurdles that you will have to overcome to make this work nicely. Here is the general idea.

  1. In the CardPanel constructor, make the "CardListener" object and add it to both of the JLabels. Again, you shouldn't create two separate listeners -- use the same object for both.

  2. In the CardPanel class, create an inner class called CardListener that implements a Mouse Listener.

  3. In the mouseClicked method, determine which of the cards was pressed (hint: getSource) and print it out. What is wrong with this solution? Try to figure out the problem before you implement it. If you can't, that is ok too, try implementing it and see where it goes wrong.

    How do you fix the problem? See if you can think of a solution and then talk to me or the lab assistant to discuss your ideas.

    HAND IN: