Solution for
Programming Exercise 7.3


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 7.3: The RGBColorChooser applet lets the user set the red, green, and blue levels in a color by manipulating scroll bars. Something like this could make a useful custom component. Such a component could be included in a program to allow the user to specify a drawing color, for example. Rewrite the RGBColorChooser as a component. Make it a subclass of Panel instead of Applet. Instead of doing the initialization in an init() method, you'll have to do it in a constructor. The component should have a method, getColor(), that returns the color currently displayed on the component. It should also have a method, setColor(Color c), to set the color to a specified value. Both these methods would be useful to a program that uses your component.

In order to write the setColor(Color c) method, you need to know that if c is a variable of type Color, then c.getRed() is a function that returns an integer in the range 0 to 255 that gives the red level of the color. Similarly, the functions c.getGreen() and c.getBlue() return the blue and green components.

Test your component by using it in a simple applet that sets the component to a random color when the user clicks on a button, like this one:


Discussion

This exercise demonstrates that it is possible to write a new component class as a subclass of Panel. Then the new component can contain several other components. However, it is still used in programs as a single object, with a well-defined responsibility. In this case, the responsibility is "let the user input a color", just as the responsibility of a TextField is "let the user input some text". The program needs a method to get the value input by the user. A TextField has a getText() method. The color chooser component needs a getColor() method. And just as a TextField has a setText(String text) method, the color chooser needs a setColor(Color c) method. In a typical application, a program could use setColor() to show the user a currently selected color. The user could then modify the color, and the program could get the modified color with the getColor() method.

To convert the color chooser applet to a component, the first line of the class is changed to make the component a subclass of Panel (and to give it a different name):

       public class RGBChooserComponent extends Panel 
                                implements AdjustmentListener {

The first line of the init() method is changed to make it the first line of a constructor:

       public RGBChooserComponent() {

Other than that, it is only necessary to add a few new routines. The getColor() method is supposed to return the color currently displayed by the component. The information about this color is in the scrollbars. The red level of the color, for example, is redScroll.getValue(). The color levels from the scrollbars are used to construct the color that is returned by getColor():

       public Color getColor() {
              // Get the color currently displayed by the component.
           int r = redScroll.getValue();
           int g = greenScroll.getValue();
           int b = blueScroll.getValue();
           return new Color(r, g, b);
       }

In setColor(), the new color affects three aspects of the components state: The values on the scrollbars, the numbers displayed on the labels, and the background color of the canvas. It is important to keep all these aspects of the state consistent with each other:

       public void setColor(Color c) {
             // Set the component to display the given color.
             // (Ignore this if c is null.)
          if (c != null) {
             int r = c.getRed();      // Get the color levels from the color.
             int g = c.getGreen();
             int b = c.getBlue();
             redLabel.setText(" R = " + r);   // Set the labels.
             greenLabel.setText(" G = " + g);
             blueLabel.setText(" B = " + b);
             redScroll.setValue(r);        // Set the scrollbars.
             greenScroll.setValue(g);
             blueScroll.setValue(b);
             colorCanvas.setBackground(new Color(r,g,b));  // Set the canvas.
             colorCanvas.repaint();
          }
       }

In order to follow good programming style, I also added a getPreferredSize() method to specify a reasonable size for the component. If I left this out, the Panel would compute a preferred size based on the components that it contains. But this doesn't give a good value in this case.

The complete source code is shown below, followed by the source code for the little test applet.


The Solution

The custom component class:


    /*
       A RGBChooserComponent is a component that allows the user to select
       an RGB color.  It shows three scroll bars that the user can manipulate
       to set the red, green, and blue, components of the color.  A color patch
       shows the selected color, and there are three labels that show the numerical
       values of all the components.  Values are in the range 0 to 255.
       The initial color is black.  The getColor() method can be used to retrieve
       the color that the user has set.  The setColor() can be used to set
       the color that is displayed in the component.
    */
    
    import java.awt.*;
    import java.awt.event.*;
    
    public class RGBChooserComponent extends Panel implements AdjustmentListener {
       
       private Scrollbar redScroll, greenScroll, blueScroll;   // Scroll bars.
       
       private Label redLabel, greenLabel, blueLabel;  // For displaying RGB values.
                     
                     
       private Canvas colorCanvas;  // Color patch for displaying the color.
                     
       public RGBChooserComponent() {  // Constructor.
       
           /* Create Scrollbars with possible values from 0 to 255. */
           
           redScroll = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 265);
           greenScroll = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 265);
           blueScroll = new Scrollbar(Scrollbar.HORIZONTAL, 0, 10, 0, 265);
           
           /* Create Labels showing current RGB and HSB values. */
           
           redLabel = new Label(" R = 0");
           greenLabel = new Label(" G = 0");
           blueLabel = new Label(" B = 0");
           
           /* Set background colors for Scrollbars and Labels, so they don't
              inherit the gray background of the applet. */
           
           redScroll.setBackground(Color.lightGray);
           greenScroll.setBackground(Color.lightGray);
           blueScroll.setBackground(Color.lightGray);
           
           redLabel.setBackground(Color.white);
           greenLabel.setBackground(Color.white);
           blueLabel.setBackground(Color.white);
           
           /* Set the panel to listen for changes to the Scrollbars' values */
           
           redScroll.addAdjustmentListener(this);
           greenScroll.addAdjustmentListener(this);
           blueScroll.addAdjustmentListener(this);
           
           /* Create a canvas whose background color will always be set to the
              currently selected color. */
           
           colorCanvas = new Canvas();
           colorCanvas.setBackground(Color.black);
           
           /* Create the applet layout, which consists of a row of
              three equal-sized regions holding the Scrollbars,
              the Labels, and the color patch.  The background color
              of the applet is gray, which will show around the edges
              and between components. */
           
           setLayout(new GridLayout(1,3,3,3));
           setBackground(Color.gray);
           Panel scrolls = new Panel();
           Panel labels = new Panel();
           
           add(scrolls);
           add(labels);
           add(colorCanvas);
           
           /* Add the Scrollbars and the Labels to their respective panels. */
           
           scrolls.setLayout(new GridLayout(3,1,2,2));
           scrolls.add(redScroll);
           scrolls.add(greenScroll);
           scrolls.add(blueScroll);
           
           labels.setLayout(new GridLayout(3,1,2,2));
           labels.add(redLabel);
           labels.add(greenLabel);
           labels.add(blueLabel);
           
       } // end init();
       
       
       public Color getColor() {
              // Get the color currently displayed by the component.
           int r = redScroll.getValue();
           int g = greenScroll.getValue();
           int b = blueScroll.getValue();
           return new Color(r, g, b);
       }
       
       
       public void setColor(Color c) {
             // Set the component to display the given color.
             // (Ignore this if c is null.)
          if (c != null) {
             int r = c.getRed();      // Get the color levels from the color.
             int g = c.getGreen();
             int b = c.getBlue();
             redLabel.setText(" R = " + r);   // Set the labels.
             greenLabel.setText(" G = " + g);
             blueLabel.setText(" B = " + b);
             redScroll.setValue(r);        // Set the scrollbars.
             greenScroll.setValue(g);
             blueScroll.setValue(b);
             colorCanvas.setBackground(new Color(r,g,b));  // Set the canvas.
             colorCanvas.repaint();
          }
       }
       
       
       public Dimension getMinimumSize() {
              // Specify the minimum reasonable size of this component.
          return new Dimension(150,40);
       }
       
       
       public Dimension getPreferredSize() {
              // Specify the preferred size of this component.
          return new Dimension(280,80);
       }
       
    
       public void adjustmentValueChanged(AdjustmentEvent evt) {
               // This is called when the user has changed the values on
               // one of the scrollbars.  All the scrollbars are checked,
               // the labels are set to display the correct values,
               // and the color patch is reset to correspond to the new color.
           int r = redScroll.getValue();
           int g = greenScroll.getValue();
           int b = blueScroll.getValue();
           redLabel.setText(" R = " + r);
           greenLabel.setText(" G = " + g);
           blueLabel.setText(" B = " + b);
           colorCanvas.setBackground(new Color(r,g,b));
           colorCanvas.repaint();  // Redraw the canvas in its new color.
       } // end adjustmentValueChanged
    
       
       public Insets getInsets() {
              // The system calls this method to find out how much space to
              // leave between the edges of the applet and the components that
              // it contains.  I want a 2-pixel border at each edge.
          return new Insets(2,2,2,2);
       }
       
    }  // end class RGBColorChooser
    

A small applet to test the component class:

    /*
       This is a trivial applet that tests the RGBChooserComponent class.
       In particular, it tests the setColor() method in that class by 
       setting the color to a random color when the user clicks on a button.
    */
    
    
    import java.awt.*;
    import java.awt.event.*;
    import java.applet.*;
    
    
    public class TestRGB extends Applet implements ActionListener {
    
       RGBChooserComponent rgb;
    
       public void init() {
          setLayout(new BorderLayout(5,5));
          rgb = new RGBChooserComponent();
          add(rgb, BorderLayout.CENTER);
          Button button = new Button("Set Random Color");
          button.addActionListener(this);
          add(button, BorderLayout.SOUTH);
       }
       
       public void actionPerformed(ActionEvent evt) {
          int r = (int)(Math.random()*256) + 1;
          int g = (int)(Math.random()*256) + 1;
          int b = (int)(Math.random()*256) + 1;
          rgb.setColor( new Color(r,g,b) );
       }
    
    }


[ Exercises | Chapter Index | Main Index ]