CPSC 124, Fall 2001
Lab 10: Silly Button

This lab begins with a short exercise related to Buttons and events. The remainder of the lab will walk you through the creation of an applet that can serve as your starting point for Programming Assignment 3.

The exercise is due in class next Wednesday, November 7. Please turn in a printout of your applet source code. The second part of the lab is not to be turned in.

The files for today's lab can be found in the directory /home/cs124/lab10, which you should copy into your account. The files in this directory relate only the second, non-credit part of the lab.


Exercise: Who's Got the Button?

In this exercise, you will add a button to an applet and make the applet respond when the button is pressed. When a button is pressed, it generates an ActionEvent. We did an example of using of using Buttons in class, and there are several examples in the book. To review briefly, there are four things that you need to do to make an applet respond to button clicks: (1) import java.awt.event.* (in addition to java.awt.* and java.applet.*); (2) declare that the applet implements the ActionListener interface; (3) define an actionPerformed() method in the applet to respond to clicks on the button; and (4) register the applet to listen for ActionEvents from the button by calling button.addActionListener(this), where button is a variable that refers to the Button. In addition, to get the button to actually appear in the applet, you have to use the applet's add() method to add the button to the applet.

Besides working with a button, in this exercise you'll learn a bit about doing "layout." Usually, applets and other containers use layout managers to manage the sizes and positions of the components that they contain. With a layout manager, all you do is add the component to the applet. The layout manager decides where to put the component and how big to make it. There are a lot of advantages to this system, especially when the applet can change size. However, using layout managers can be tricky. Conceptually, it's easier to do the layout by hand. For this to work, you have to get rid of the layout manager that the applet uses by default. To do this, add the statement

          setLayout(null);

to the init() method of the applet. You are now responsible for setting the size and position of any component that you add to the applet by calling the component's setBounds(x,y,width,height) method. You can do this in the init() method as well. For example, if button refers to a Button, you can call

          button.setBounds(10,20,100,30);

to put the Button's upper left corner at (10,20) and to make the button 100 pixels wide and 30 pixels high. You can compute the values of the parameters, if you want. For example, if you want the upper left corner to be at (a,b), you would just call button.setBounds(a,b,100,30).

Now, there is nothing to stop you from moving the button elsewhere in the applet. You just have to call setBounds() again. That is your assignment for this exercise: Write an applet that contains a button that jumps to a randomly selected location when you click on it. Here is an applet that does this:

The button should start out at the center of the applet, and when it jumps, it should remain entirely within the applet. Ideally, you should ensure that the new position of the applet does not intersect the old position, but I will give extra credit for this, since the calculation is a little tricky. (The idea is to keep selecting possible random positions until you get one that doesn't intersect the current position.)

If you want an even cuter applet, you can make a button that jumps as soon as the mouse enters the button, which makes it just about impossible to click the button:

To do this, you have to register the applet to listen for MouseEvents on the button. Use the mouseEntered method to move the button.


A Little Knowledge

It's annoying to keep making small .html files just to test your applets. Here's something you can do about that, but if you do this, make sure that you understand what you are doing, or you will confuse yourself.

The appletviewer will actually take any kind of file. It doesn't have to be an .html file. It only has to contain a valid <applet> tag. The trick is to put the applet tag in a comment in your .java file. For example, this might look like:

     /*
        <applet code="SillyButton.class" width=300 height=200>
        </applet>
     */

The java compiler will ignore this since it is in a comment, but if you apply appletviewer to the .java file -- for example, appletviewer SillyButton.java -- then appletviewer will find the applet tag and run the applet. You don't need a separate .html file.


Starting Assignment 3

Programming Assignment 3 asks you to write a Mosaic Drawing program. The rest of this lab is a "tutorial" that will get you started on that assignment and show you how to do some things that you wouldn't know how to do on your own. The result of the tutorial is an applet like the following one. Use the scroll bar at the bottom of the applet to adjust the color. Click on the mosaic area (initially, a big black square) to fill it with colored tiles. The color is taken from the scroll bar, but is randomized a bit to give some variation in the colors of the tiles. I will assume that the applet is 350 pixels wide and 400 pixels high.

Start writing your applet with the usual three import statements. The applet should implement MouseListener so that it will be able to respond to mouse clicks. This means that it has to define the five methods specified by that interface. (The mousePressed() method is the only one that needs to contain any code.) It will also need an init() method. So, in outline, we have this outline for the applet so far:

         import java.awt.*;
         import java.awt.event.*;
         import java.applet.*;
         
         public class MosaicDraw extends Applet implements MouseListener {
         
            public void init() {
            }
            
            public void mousePressed(MouseEvent evt) {
            }
            
            public void mouseReleased(MouseEvent evt) { }
            public void mouseClicked(MouseEvent evt) { }
            public void mouseEntered(MouseEvent evt) { }
            public void mouseExited(MouseEvent evt) { }
         
         } // end class MosaicDraw

I have typed this much for you in a file called MosaicDraw.java.

You will need variables to represent the components in the applet. The mosaic itself belongs to the class MosaicCanvasX. You can look at the source code, MosaicCanvasX.java, to learn more about this class. The scroll bar belongs to the class named Scrollbar, which is one of Java's standard classes. The little color patch next to the scroll bar is also an object. It belongs to class Canvas, another of Java's standard classes. This class is usually used only for making subclasses, since a Canvas is just a blank square that shows nothing but the background color of the canvas. For a color patch like the one we need here, however, a plain Canvas object works well.

Declare an instance variable belonging to each of these classes. The variables must be instance variables, not local variables, since they will be used in several different methods in your class. I will assume that the names of the variables are mosaic, colorPatch, and colorSlider.

Now, let's start writing the init() method.. You will be doing the layout by hand, so add the command setLayout(null) to the init() method. Create your mosaic, color patch, and scroll bar objects. The constructor for the mosaic specifies the number of rows and columns of the mosaic. I used new MosaicCanvasX(15,15). The Canvas constructor has no parameters, so you just need new Canvas(). Scroll bars are more complicated. The constructor takes five parameters. Use the constructor

       new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 110)

A scroll bar can be either horizontal or vertical. This is selected by the first parameter. A scroll bar has an integer value, which represents the position of the scroll. The remaining four parameters in the constructor imply that the value for this scroll bar will range from 0 to 100, and that the initial value is zero. You can look up the details later. (See Section 7.3).

The color patch should have a background color that is equal to the color selected on the scroll bar. The applet uses "HSB colors" which are specified by hue, saturation, and brightness values. (See Section 6.3.) I found it attractive to use a value of 0.7 for the brightness and for the saturation. The hue is controlled by the value of the scroll bar, which is initially zero. As a convenience, I've added a makeHSBColor() method to the MosaicCanvasX class. Using this method, you can set the background color of the color patch with the command

     colorPatch.setBackground( mosaic.makeHSBColor( 0.0, 0.7, 0.7 ) );

Now, each object must be added to the applet to make it appear in the applet. Use three commands such as add(mosaic). Finally, for now, you have to specify the bounds of the objects. This is discussed in the first exercise of this lab. For the mosaic, I used mosaic.setBounds(25,25,300,300). Figure out reasonable bounds for the scroll bar and the color patch.

At this point, you should be able to test your applet and see the objects. You will need an applet tag of the form

      <applet code="MosaicDraw.class" width=350 height=400>
      </applet>

Now, let's work on filling the mosaic with color. You have to make the applet respond to mouse clicks on the mosaic, so add the command

     mosaic.addMouseListener(this);

to the init() method. When the user clicks the mosaic, you need to determine which color is currently selected on the scroll bar. The scroll bar controls the hue. The hue value must be calculated based on the value of the scroll bar, and it must be a number between 0.0 and 1.0. Since the scroll bar value is an integer in the range 0 to 100, the hue can be calculated as colorSlider.getValue()/100.0. You can add the following method to your class to get the current color:

           Color getSelectedColor() {
              double hue = colorSlider.getValue()/100.0;
              return mosaic.makeHSBColor( hue, 0.7, 0.7 );
           }

In the mousePressed() routine, you could simply call mosaic.fill(getSelectedColor()) to fill the mosaic with squares of the selected color. You might want to try this now to see if it all works. However, you are really supposed to randomize the color a bit. You might want to come back and do this later. To implement randomization, I wrote another routine, getRandomizedColor(). It is similar to getSelectedColor(), except that it adds a small random number to the hue. In my applet, I add a number between -0.04 and 0.04. If this causes the hue to become less than zero, you should add 1 to bring it back into the legal range. Similarly, if the value of hue becomes bigger than 1, you should subtract 1 from it. Once you have this routine, you can use two nested for loops in the mousePressed() method to visit every row and column in the grid and set the color to getRandomizedColor().


The final step in the tutorial is to make the color of the color patch change continually when the user drags the scroll bar. When a scroll bar is manipulated by the user, it generates AdjustmentEvents. You just have to make your applet listen for AdjustmentEvents and change the color of the color patch whenever one occurs. Add AdjustmentListener to the list of interfaces implemented by the applet. In the init() method, register the applet to listen for AdjustmentEvents from the scroll bar by calling colorSlider.addAdjustmentListener(this). Add an adjustmentValueChanged() method to the applet. This method just has to set the background color of the color patch according to the current value of the scroll bar and repaint the color patch:

       public void adjustmentValueChanged(AdjustmentEvent evt) {
          colorPatch.setBackground(getSelectedColor());
          colorPatch.repaint();
       }

This should show you that events are not so hard, once you get used to them.

The applet should now be complete except for one detail. My applet has a black border around the edges. I draw this in the applet's paint method. In fact, that's all that the applet's paint method draws. All the components in the applet take care of drawing themselves. Here is a general purpose paint method that draws a two-pixel border around the applet, no matter what size the applet is:

       public void paint(Graphics g){
          int w = getSize().width;
          int h = getSize().height;
          g.drawRect( 0, 0, w-1, h-1 );
          g.drawRect( 1, 1, w-3, h-3 );
       }

As a final touch, you might want to set the background color of the applet. I use a very light gray. It should be set in the init() method with a command such as setBackground(new Color(230,230,230)).


David Eck, 1 November 2001