Example: Building a Java GUI

Steven Zeil

Last modified: Dec 19, 2017
Contents:

The StringArt Program

 

To illustrate Java event handling, we’ll look at a simple, but typical GUI interface to a graphic application

1 Build 1: The “manual” build

We’ll actually build this program twice. First, we’ll do it “manually” as a step-by-step process using ordinary Java editing tools. Then we’ll do a second “assisted” build using WindowBuilder, a GUI design tool that comes with Eclipse.

First, a reminder of the process that we need to follow:

The Process

  1. Develop the model
  2. Select the GUI elements to portray the model
  3. Develop the layout of those elements
  4. Add listeners to the elements
  5. Implement custom drawing

1.1 Develop the Model

The model is the underlying data that is being manipulated and portrayed by the program.

// The Model
  private Color[] colors;
  private int stepSize = 5;


Select the GUI elements to portray the model

1.2 Develop the layout of those elements

There are two common approaches to laying out GUIs


Programmed Approach in Java

Uses a combination of


GUI Container Elements


GUI Elements in StringArt


Structural Summary

 


Where Did the Control Panel Come From?


Programming the Containment

GUI container elements are handled much like utility containers

containment.java
  public void createAndShowGUI() {
      window = new JFrame();       ➀
         //...
      canvas = new JPanel () {     ➁
	      //...
          }
      };
    canvas.setBackground(Color.white);
    window.getContentPane().add (canvas, BorderLayout.CENTER);  ➂
    canvas.setPreferredSize(new Dimension(400, 400));
    
    JPanel controls = new JPanel();                             ➃
    
    colorChooser1 = new JButton("Color 1");                     ➄
    controls.add (colorChooser1);
      //...
    colorChooser2 = new JButton("Color 2");
    controls.add (colorChooser2);
      //...
    stepSizeIn = new JTextField (""+stepSize, 5);
    controls.add (stepSizeIn);
      //...
    window.getContentPane().add (controls, BorderLayout.SOUTH);  ➅
      //...
    window.pack();                                               ➆
    window.setVisible(true);
  }

Laying out the components

Java has several layout managers.


Using Border Layout

  public void createAndShowGUI() {
      window = new JFrame();
      // set up the components
      window.getContentPane().setLayout (
            new BorderLayout());
    
      canvas = new JPanel () {
          ⋮
          }
      };
      ⋮
    window.getContentPane().add (canvas, 
             BorderLayout.CENTER);
    
    JPanel controls = new JPanel();
      ⋮
    window.getContentPane().add (controls, 
              BorderLayout.SOUTH);
      ⋮


Miscellaneous Appearance Code

appearance.java
  public void createAndShowGUI() {
      window = new JFrame();
      // set up the components
      window.getContentPane().setLayout (new BorderLayout());
    
      canvas = new JPanel () {
	      //...
          }
      };
    canvas.setBackground(Color.white);
    window.getContentPane().add (canvas, BorderLayout.CENTER);
canvas.setPreferredSize(new Dimension(400, 400));  ➀
    
    JPanel controls = new JPanel();
    
    colorChooser1 = new JButton("Color 1");
    controls.add (colorChooser1);
    setColor(colorChooser1, colors[0]);
    colorChooser1.addActionListener (new ColorChooser(colorChooser1, 0));
    
    colorChooser2 = new JButton("Color 2");
    controls.add (colorChooser2);
    setColor(colorChooser2, colors[1]);
    colorChooser2.addActionListener (new ColorChooser(colorChooser2, 1));
    
    stepSizeIn = new JTextField (""+stepSize, 5);
    controls.add (stepSizeIn);
    stepSizeIn.addActionListener (new ActionListener()
      {
        public void actionPerformed(ActionEvent e) {
          try {
            Integer newSize = new Integer(stepSizeIn.getText());
            stepSize = newSize.intValue();
            canvas.repaint();
          } catch (Exception ex) {};
        }
      });

    window.getContentPane().add (controls, BorderLayout.SOUTH);
    
    window.setDefaultCloseOperation((startedInAnApplet) ? JFrame.DISPOSE_ON_CLOSE : JFrame.EXIT_ON_CLOSE);
    
    window.pack();
    window.setVisible(true);
  }

  /**
   * Sets the background color of a button to the indicated color.
   * Changes the foreground to wither black or white, depending on
   * which will give more contrast agasint the new background.
   * 
   * @param button
   * @param color
   */
  private void setColor(JButton button, Color color) {   ➁
    button.setBackground(color);
    int brightness = color.getRed() + color.getGreen() + color.getBlue(); // max of 3*255
    if (brightness > 3*255/2) {
        // This is a fairly bright color. Use black lettering
        button.setForeground (Color.black);
    } else {
        // This is a fairly dark color. Use white lettering
        button.setForeground (Color.white);
    }
}

Most of the highlighted code is self-explanatory

1.3 Add listeners to the elements

We need to implement some behaviors


Closing the Window

Here we use a shorthand technqiue rather than create a full-fledged functor to listen:

public void createAndShowGUI() {
    window = new JFrame();
       ⋮
  window.setDefaultCloseOperation(
    (startedInAnApplet)
        ? JFrame.DISPOSE_ON_CLOSE 
        : JFrame.EXIT_ON_CLOSE);
       ⋮
}


Entering the Step Size

This is handled by an ActionListener, which is triggered when we hit Enter/Return with the cursor positioned in the text box.

 stepSizeIn = new JTextField (""+stepSize, 5);
 controls.add (stepSizeIn);
 stepSizeIn.addActionListener (new ActionListener()
   {
     public void actionPerformed(ActionEvent e) {
       try {
         Integer newSize = new Integer(
                       stepSizeIn.getText());
         stepSize = newSize.intValue();
            ⋮
       } catch (Exception ex) {};
     }
   });


Color Selection

Buttons notify an ActionListener when a button is clicked

    colorChooser1 = new JButton("Color 1");
    controls.add (colorChooser1);
    setColor(colorChooser1, colors[0]);
    colorChooser1.addActionListener (
        new ColorChooser(colorChooser1, 0));
    
    colorChooser2 = new JButton("Color 2");
    controls.add (colorChooser2);
    setColor(colorChooser2, colors[1]);
    colorChooser2.addActionListener (
        new ColorChooser(colorChooser2, 1));

We implement this with our own subclass of ActionListener, ColorChooser.


ColorChooser

colorchooser.java
  private class ColorChooser implements ActionListener {
      private JButton button;          ➀
      private int colorNum;

      public ColorChooser (JButton button, int colorNum) {
          this.button = button;        ➁
          this.colorNum = colorNum;
      }

      @Override
      public void actionPerformed(ActionEvent arg0) {   ➂
          Color chosen = JColorChooser.showDialog(window, "Choose a color", 
                  colors[colorNum]);
          if (chosen != null) {
              colors[colorNum] = chosen;   ➃
              setColor (button, chosen);
              canvas.repaint();
          }
      }
  };

The JColorChooser invoked in the first line pops up the stnadard Java color selection box.

1.4 Implement custom drawing

The proper moment for drawing GUI elements is a bit tricky.

Think of some of the various situations in which all or part of a window needs to be redrawn:

Any of these can force part or all of an application to need redrawing. Some of these (state changes) are under direct program control. Most are not.


paint and repaint

    canvas = new JPanel () {
        public void paint (Graphics g) {
          super.paint(g);
          drawLines (g, getSize());
        }
      };

Paint and Repaint


Repainting the StringArt Canvas

repainting.java
  private class ColorChooser implements ActionListener {
      private JButton button;
      private int colorNum;

      public ColorChooser (JButton button, int colorNum) {
          this.button = button;
          this.colorNum = colorNum;
      }

      @Override
      public void actionPerformed(ActionEvent arg0) {
          Color chosen = JColorChooser.showDialog(window, "Choose a color", colors[colorNum]);
          if (chosen != null) {
              colors[colorNum] = chosen;
              setColor (button, chosen);
              canvas.repaint();
          }
      }
  };


  public void createAndShowGUI() {
      window = new JFrame();
      // set up the components
      window.getContentPane().setLayout (new BorderLayout());
    
      canvas = new JPanel () {
          public void paint (Graphics g) {
              super.paint(g);
              drawLines (g, getSize());
          }
      };
    canvas.setBackground(Color.white);
    window.getContentPane().add (canvas, BorderLayout.CENTER);
    canvas.setPreferredSize(new Dimension(400, 400));
    
    JPanel controls = new JPanel();
    
    colorChooser1 = new JButton("Color 1");
    controls.add (colorChooser1);
    setColor(colorChooser1, colors[0]);
    colorChooser1.addActionListener (new ColorChooser(colorChooser1, 0));
    
    colorChooser2 = new JButton("Color 2");
    controls.add (colorChooser2);
    setColor(colorChooser2, colors[1]);
    colorChooser2.addActionListener (new ColorChooser(colorChooser2, 1));
    
    stepSizeIn = new JTextField (""+stepSize, 5);
    controls.add (stepSizeIn);
    stepSizeIn.addActionListener (new ActionListener()
      {
        public void actionPerformed(ActionEvent e) {
          try {
            Integer newSize = new Integer(stepSizeIn.getText());
            stepSize = newSize.intValue();
            canvas.repaint();
          } catch (Exception ex) {};
        }
      });

    window.getContentPane().add (controls, BorderLayout.SOUTH);
    
    window.setDefaultCloseOperation((startedInAnApplet) ? JFrame.DISPOSE_ON_CLOSE : JFrame.EXIT_ON_CLOSE);
    
    window.pack();
    window.setVisible(true);
  }

The Full Listing

StringArt.java
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * A simple example of GUI event handling in a Java application.
 * 
 * This can be run as a main program or as an applet.
 * 
 * @author zeil
 *
 */


public class StringArt extends JApplet {

  private boolean startedInAnApplet;
	
// The Model
  private Color[] colors;
  private int stepSize = 5;
  
  // The View & Controls
  private JFrame window;
  private JPanel canvas;
  private JButton colorChooser1;
  private JButton colorChooser2;
  private JTextField stepSizeIn;
  

  private class ColorChooser implements ActionListener {
      private JButton button;
      private int colorNum;

      public ColorChooser (JButton button, int colorNum) {
          this.button = button;
          this.colorNum = colorNum;
      }

      @Override
      public void actionPerformed(ActionEvent arg0) {
          Color chosen = JColorChooser.showDialog(window, "Choose a color", colors[colorNum]);
          if (chosen != null) {
              colors[colorNum] = chosen;
              setColor (button, chosen);
              canvas.repaint();
          }
      }
  };

  public StringArt()
  {
      startedInAnApplet = false;
      window = null;
      colors = new Color[2];
      colors[0] = Color.red;
      colors[1] = Color.blue;
  }
  
  
  public static void main (String[] args)
  {
      StringArt instance = new StringArt();
      instance.createAndShowGUI();  
  }
  
  public void createAndShowGUI() {
      window = new JFrame();
      // set up the components
      window.getContentPane().setLayout (new BorderLayout());
    
      canvas = new JPanel () {
          public void paint (Graphics g) {
              super.paint(g);
              drawLines (g, getSize());
          }
      };
    canvas.setBackground(Color.white);
    window.getContentPane().add (canvas, BorderLayout.CENTER);
    canvas.setPreferredSize(new Dimension(400, 400));
    
    JPanel controls = new JPanel();
    
    colorChooser1 = new JButton("Color 1");
    controls.add (colorChooser1);
    setColor(colorChooser1, colors[0]);
    colorChooser1.addActionListener (new ColorChooser(colorChooser1, 0));
    
    colorChooser2 = new JButton("Color 2");
    controls.add (colorChooser2);
    setColor(colorChooser2, colors[1]);
    colorChooser2.addActionListener (new ColorChooser(colorChooser2, 1));
    
    stepSizeIn = new JTextField (""+stepSize, 5);
    controls.add (stepSizeIn);
    stepSizeIn.addActionListener (new ActionListener()
      {
        public void actionPerformed(ActionEvent e) {
          try {
            Integer newSize = new Integer(stepSizeIn.getText());
            stepSize = newSize.intValue();
            canvas.repaint();
          } catch (Exception ex) {};
        }
      });

    window.getContentPane().add (controls, BorderLayout.SOUTH);
    
    window.setDefaultCloseOperation((startedInAnApplet) ? JFrame.DISPOSE_ON_CLOSE : JFrame.EXIT_ON_CLOSE);
    
    window.pack();
    window.setVisible(true);
  }
  
  /**
   * Sets the background color of a button to the indicated color.
   * Changes the foreground to wither black or white, depending on
   * which will give more contrast agasint the new background.
   * 
   * @param button
   * @param color
   */
  private void setColor(JButton button, Color color) {
    button.setBackground(color);
    int brightness = color.getRed() + color.getGreen() + color.getBlue(); // max of 3*255
    if (brightness > 3*255/2) {
        // This is a fairly bright color. Use black lettering
        button.setForeground (Color.black);
    } else {
        // This is a fairly dark color. Use white lettering
        button.setForeground (Color.white);
    }
}

  // Applet functions
  
  public void init() {
      startedInAnApplet = true;
  }

public void start() {
    if (window == null)
        createAndShowGUI();
  }
  
  public void stop() {
  }
  
  public void destroy() {
  }
  
  
  
  
  int interpolate (int x, int y, int i, int steps)
  {
    return (i * x + (steps-i)*y) / steps;
  }
  
  
  Color interpolate(Color c1, Color c2, int i, int steps)
  {
    return new Color (interpolate(c1.getRed(), c2.getRed(), i, steps),
                      interpolate(c1.getGreen(), c2.getGreen(), i, steps),
                      interpolate(c1.getBlue(), c2.getBlue(), i, steps));
  }
  

  class Point {
    double x;
    double y;
  }

  Point ptOnCircle (int degrees, int radius, Point center) 
  {
    Point p = new Point();
    double theta = Math.toRadians((double)degrees);
    p.x = center.x + (double)radius * Math.cos(theta);
    p.y = center.y + (double)radius * Math.sin(theta);
    return p;
  }

  public void drawLines(Graphics g, Dimension d)
  {
    int dmin = (d.width < d.height) ? d.width : d.height;
    
    if (stepSize < 1)
      stepSize = 1;

    Point center = new Point();
    center.x = (double)d.width/2.0;
    center.y = (double)d.height/2.0;

    for (int i = 0; i < 60; ++i) {
      Point origin = ptOnCircle(6*i, dmin/2, center);
      int j = i + stepSize;
      while (j >= 60)
        j -= 60;
      while (i != j) {
        Point destination = ptOnCircle(6*j, dmin/2, center);
        g.setColor(interpolate(interpolate(colors[0], colors[1], i%30, 30),
                               interpolate(colors[0], colors[1], j, 60),
                               1, 2));
        g.drawLine ((int)origin.x, (int)origin.y,
                    (int)destination.x, (int)destination.y);
        j += stepSize;
        while (j >= 60)
          j -= 60;
      }
    }
  }
  
  
}

2 Build 2: An “assisted” build with WindowBuilder

 

WindowBuilder is a GUI construction tool that is included in the more recent Java-based versions of Eclipse and can be added as an option to older versions.

WindowBuilder lets you position your GUI elements visually and generates the corresponding source code. It can sometimes be applied to existing GUI code as well, depending upon how complicated the code is.

WindowBuilder is not a replacement for understanding how the GUI elements work in Java, or for understanding the logic of layout managers. It provides only limited help for implementing behaviors – it can insert code skeletons in the appropriate place but you still need to fill those in with the implementation of the behaviors that you need.

Let’s look at how we could have used WindowBuilder to streamline the construction of the same string art program.

2.1 Setting Up the Model

We still start with the idea of a model containing the basic data about what we are drawing and interacting with. For this build, i will break the model out into a separate class, StringArtModel, shown here:

StringArtModel.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JButton;

/**
 * The core data and drawing algorithm for the CS330 string art program.
 * 
 * @author zeil
 *
 */
public class StringArtModel {

	/**
	 * The two colors used in drawing the picture.
	 * Lines are shaded gradually from colors[0]
	 * to colors[1].
	 */
    private Color[] colors;    
	
	/**
	 * Each of the 60 points around the circle will get
	 * a line connecting it to the points k*stepSize away,
	 * for all values of k (until we start to repeat
	 * the same lines).
	 */
	private int stepSize = 5;    ➁


	/**
	 * Create a new set of model data.
	 */
	public StringArtModel() {
		colors = new Color[2];
		colors[0] = Color.red;
		colors[1] = Color.blue;
	}

	/**
	 * Get the value of one of the colors.
	 * @param colorNum an integer in the range 0..1
	 * @return the requested color.
	 */
    public Color getColor(int colorNum) {     ➂
		return colors[colorNum];
	}

	/**
	 * Change the value of one of the colors.
	 * @param colorNum an integer in the range 0..1
	 * @param color the desired color
	 */
	public void setColor(int colorNum, Color color) {  ➂
		colors[colorNum] = color;
	}

	/**
	 * Get the current step size.  When drawLines is called,
	 * each of the 60 points around the circle will get
	 * a line connecting it to the points k*getStepSize() away,
	 * for all values of k (until we start to repeat
	 * the same lines).
	 * 
	 * @return the step size
	 */
	public int getStepSize() {     ➂
		return stepSize;
	}

	/**
	 * Change the step size value.
	 * @param newStepSize the desired step size
	 */
	public void setStepSize (int newStepSize) {    ➂
		stepSize = newStepSize;
	}


	/**
	 * Compute the value that lies (i/n) of the way between x and y.
	 * 
	 * @param x any integer value
	 * @param y any integer value
	 * @param i an integer in the range 0..steps indicating how close to y we want to be
	 * @param steps how many steps from x to y
	 * @return an interpolated value between x and y
	 */
	private int interpolate (int x, int y, int i, int steps) {
		return (i * x + (steps-i)*y) / steps;
	}

	/**
	 * Compute a color that lies between c1 and c2.  This is the function
	 * used  to "shade" lines between the two base colors.
	 *  
	 * @param c1 any color
	 * @param c2 any colot
	 * @param i an integer in the range 0..steps indicating how close to c2 we want to be
	 * @param steps how many steps from c1 to c2
	 * @return an interpolated value between c1 and c2
	 * @return an interpolated color value.
	 */

	private Color interpolate(Color c1, Color c2, int i, int steps) {
		return new Color (interpolate(c1.getRed(), c2.getRed(), i, steps),
				interpolate(c1.getGreen(), c2.getGreen(), i, steps),
				interpolate(c1.getBlue(), c2.getBlue(), i, steps));
	}


	/**
	 * An (x,y) Cartesian coordinate.
	 * @author zeil
	 *
	 */
	private class Point {
		public double x;
		public double y;
	}

	/**
	 * Get the coordinates of a point on a circle.
	 * @param degrees The angle around the circle where we want the point.
	 * @param radius  The radius of the circle.
	 * @param center  The center of the circle.
	 * @return  the coordinates of a point on the edge of the circle at the desired angle. 
	 */
	private Point ptOnCircle (int degrees, int radius, Point center) {
		Point p = new Point();
		double theta = Math.toRadians((double)degrees);
		p.x = center.x + (double)radius * Math.cos(theta);
		p.y = center.y + (double)radius * Math.sin(theta);
		return p;
	}

	/**
	 * Draw a series of lines connecting 60 points set around
	 * a circle.
	 * 
	 * @param g the graphics device on which we are to drawn.
	 * @param d the dimension (width and height) of the available
	 *          drawing area.
	 */
    public void drawLines(Graphics g, Dimension d) {    ➃
		int dmin = (d.width < d.height) ? d.width : d.height;

		if (stepSize < 1)
			stepSize = 1;

		Point center = new Point();
		center.x = (double)d.width/2.0;
		center.y = (double)d.height/2.0;

		for (int i = 0; i < 60; ++i) {
			Point origin = ptOnCircle(6*i, dmin/2, center);
			int j = i + stepSize;
			while (j >= 60)
				j -= 60;
			while (i != j) {
				Point destination = ptOnCircle(6*j, dmin/2, center);
				g.setColor(interpolate(interpolate(colors[0], colors[1], i%30, 30),
						interpolate(colors[0], colors[1], j, 60),
						1, 2));
				g.drawLine ((int)origin.x, (int)origin.y,
						(int)destination.x, (int)destination.y);
				j += stepSize;
				while (j >= 60)
					j -= 60;
			}
		}
	}


	/**
	   * Sets the background color of a button to the indicated color.
	   * Changes the foreground to either black or white, depending on
	   * which will give more contrast against the new background.
	   * 
	   * @param button
	   * @param color
	   */
	  public void setColor(JButton button, Color color) {  ➄
	    button.setBackground(color);
	    int brightness = color.getRed() + color.getGreen() + color.getBlue(); // max of 3*255
	    if (brightness > 3*255/2) {
	        // This is a fairly bright color. Use black lettering
	        button.setForeground (Color.black);
	    } else {
	        // This is a fairly dark color. Use white lettering
	        button.setForeground (Color.white);
	    }
	}
}

2.2 Develop the layout of the elements

Now we are ready to create a separate StringArtGUI class.

View recording.

  1. Right-click on the package. Select NewOtherWindowBuilder => Swing Designer => Application Window. Name this as StringArtGUI.

  2. We can run this. It brings up an empty window. We want to fill that window with the same kinds of elements that we used in our manual example.

  3. Click the Design tab at the bottom of the source pane.

  4. See the image of our empty window. Click on the top bar. The properties tab shows that we have a variable named “frame” of type Jframe. Click on the source tab to ses several uses of the variable name “frame”, including its initialization in the initialize() function. Return to the Design window. Let’s rename “frame” to “mainWindow”. Check the source and see that this change has been carried out.

  5. Click in the main area. It shows that we have a Container and that it has already been assigned the layout manager BorderLayout. If you remember, that is what we chose as our overall layout in the manual design, so we are already one step ahead. Let’s save the file.

  6. In the central area of our window, we want a JPanel that will serve as our drawing canvas. With the main Container still selected, click on JPanel in the Palette. Move the cursor over our window’s Container area, and you see the characteristic NSWE-Central patterns of a BorderLayout. Click in the central area to deposit our new panel there.

  7. The Properties pane updates to describe out new panel. Change the variable name to “canvas”. Now visit the source. You can see that canvas is just a local variable in initialize(). We’re going to need to allow access to that from a number of different functions, so let’s change that to a data member.

    Highlight “canvas”. Right-click and select RefactorConvert local variable to field… Accept the defaults (private, initialize in current method).

  8. Back to the design. The properties shows that our canvas has a grey background color (238,238,238). Click the small button to bring up a color selector. Change this to white. Save .

  9. We want a panel on the south to contain our various controls. Select the container again (easiest to use the Components area). Click on JPanel in the Palette and place this panel in the South area. Let’s rename this variable to “controlPanel”. Save.

  10. We can try running again. You can see a white main area with a thin grey (our controlpanel) beneath it.

  11. Select the control panel. The layout is shown in the properties area as FlowLayout, which gives us a left-to-righ arrangement. That’s fine. Add two buttons labeled “Color1 “ and “Color 2” by selecting JButton from the palette and depositing them in the control panel, than changing the text property. Also, change the names to colorButton1 and colorButton2. Save.

  12. Next add a JTextField to the control panel to hold our step size. The default number of columns is 10. Let’s reduce that to 3. Fill in the initial text of 88. We’ll fix that later. Save and run.

We actually have all of our GUI elements in place at the moment, but we don’t have any behaviors set up yet.

2.3 Implement behaviors and custom drawing

View recording.

  1. Most of the behaviors will need to work with the StringArtModel info, so let’s get that set up. Return to the source. Add a data member “art” of type StringArtModel and initialize it in the StringArtGUI constructor. Save.

  2. Search for the place where we put 88 into the GUI text field. Change this to use the initial value from the model. Save.

  3. Similarly, we want to set the background colors of the buttons to reflect the model initial values. Easiest way to do that is to use the Design properties box to change the background color, then return to the source pane, find those color settign commands, and replace by calls to our own setColor function.

  4. Now let’s get the canvas painting properly. We need to override the paint() function for our canvas (the inherited paint() simply fills in the background color). Here we go back to the source. Look at the initialization of canvas. We will need a subtype of Panel in order to override the Paint function. Easiest to do this as an anonymous subtype overriding paint. Code for the override is almost identical to out manual version. Only change is the use of the “art” data member. Save & run. You can see that now we are really getting somewhere.

  5. Back to design. Let’s add an event handler for the color1 button. Right-click on it it in the design view, select Add event handler => action => actionPerformed.

    We are immediately taken to the source code with a new event handler attached to the button. We need to fill in the behavior we want. We do that. Note that we get an issue trying to access the local variable colorButton1. So change that into a data member as we did earlier with canvas. Save & run. This time try the button.

  6. A simple copy and paste in the source window adds a similar handler for colorButton2.

  7. Back to design. Add an event handler to the text box by right-clicking on it and selecting Add event handler => action => actionPerformed.

Final listing:

StringArtGUI.java
import java.awt.EventQueue;
import java.awt.Graphics;

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JTextField;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class StringArtGUI {

	private JFrame mainWindow;
	private JPanel canvas;
	private JTextField textField;
	private JTextField stepSizeIn;
	
	private StringArtModel art;
	private JButton colorButton1;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					StringArtGUI window = new StringArtGUI();
					window.mainWindow.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public StringArtGUI() {
		art = new StringArtModel();
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		mainWindow = new JFrame();
		mainWindow.setBounds(100, 100, 450, 300);
		mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		canvas = new JPanel() {
			public void paint (Graphics g) {
				super.paint(g);
				art.drawLines(g, getSize());
			}
		};
		canvas.setBackground(Color.WHITE);
		mainWindow.getContentPane().add(canvas, BorderLayout.CENTER);
		
		JPanel controlPanel = new JPanel();
		mainWindow.getContentPane().add(controlPanel, BorderLayout.SOUTH);
		
		colorButton1 = new JButton("Color 1");
		colorButton1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Color chosen = JColorChooser.showDialog(mainWindow, "Choose a color", 
						art.getColor(0));
				if (chosen != null) {
					art.setColor(0, chosen);
					art.setColor (colorButton1, chosen);
					canvas.repaint();
				}
			}
		});
		art.setColor(colorButton1, art.getColor(0));
		controlPanel.add(colorButton1);

		JButton colorButton2 = new JButton("Color 2");
		colorButton2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Color chosen = JColorChooser.showDialog(mainWindow, "Choose a color", 
						art.getColor(1));
				JButton button = (JButton)e.getSource();
				if (chosen != null) {
					art.setColor(1, chosen);
					art.setColor (button, chosen);
					canvas.repaint();
				}
			}
		});
		art.setColor(colorButton2, art.getColor(1));
		controlPanel.add(colorButton2);
		
		stepSizeIn = new JTextField();
		stepSizeIn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
		            Integer newSize = new Integer(stepSizeIn.getText());
		            art.setStepSize(newSize.intValue());
		            canvas.repaint();
		          } catch (Exception ex) {};
		        }
		});
		stepSizeIn.setText("" + art.getStepSize());
		controlPanel.add(stepSizeIn);
		stepSizeIn.setColumns(3);
		
	}

}