Example: Building a Java GUI

Steven Zeil

Last modified: Mar 21, 2014

Contents:
1. Develop the Model
2. Develop the layout of those elements
3. Add listeners to the elements
4. Implement custom drawing

The StringArt Program


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

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. 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

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

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.

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;
      }
    }
  }
  
  
}