JFC is a collection of packages that provide GUI and graphics components:
awt = peer-based controls & event handling
swing = peerless controls
accessibility API
2D Graphics API
drag-and-drop API
These notes will focus on building GUIs using swing controls and awt event handling.
There are two types of GUI components (buttons, menus, windows, etc.): peer-based and peerless. A peer-based component delegates commands to an associated system component. Using peer-based components can lead to subtle but annoying differences in the look and feel of a GUI from one platform to another. A peerless component is just a picture of a control in a window. The behavior of the control is completely determined by the Java VM. JComponent is the base class of all peerless components. JFrame and JDialolg are the only peer-based swing components:
We can view a graphical user interface (GUI) as a tree of GUI components. Parent nodes are containers that hold their children. Leaf nodes are views and controls:
A container is a GUI component that holds other GUI components, including other containers! The two main types of containers are windows and panels. A window has a border, a panel doesn't. Panels are useful for grouping components within a container.
The two main types of windows are dialog boxes and frames. If we view a GUI as a tree, then the root of the tree would usually be a frame. In other words, a frame is usually the main window of the application. Dialog windows are child windows that appear when user input is required or when a message needs to be displayed to the user.
Menu bars, menus, toolbars, applets, and control panels are examples of special purpose windows.
There are two types of controls. Input controls receive user input, while output controls display program output.
Input Controls
Button (JButton)
Combo Box (JComboBox)
List (JList)
Menu (JMenu)
Slider (JSlider)
Text Field (JTextField)
Text (JTextArea)
Tree (JTree)
Color Chooser (JColorChooser)
File Chooser (JFileChooser)
Table (JTable)
Output Controls
Label (JLabel)
Progress Bar (JProgressBar)
Tool Tip (JToolTip)
The basic application window is moveable, resizable, has a title bar, and minimize, maximize, and close buttons:
Structurally, an application window is a JPanel contained in a JFrame:
GraphicsDemo.java begins by importing the most commonly used JFC packages:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
The GraphicsDemo class extends MainJFrame, which is simply a closeable JFrame (we will describe this class in detail, later). The constructor sets the title, then adds a graphics panel to the content pane of the JFrame:
public
class GraphicsDemo extends jutil.MainJFrame {
public GraphicsDemo() {
setTitle("Graphics Demo");
Container contentPane =
getContentPane();
JPanel panel = new GraphicsPanel();
contentPane.add(panel);
}
The main() method creates a GraphicsDemo instance, then calls its show() method. This method causes the window to display itself on the desktop and begin listening for user input events such as mouse and keyboard events:
public static void
main(String[] args) {
JFrame demo = new GraphicsDemo();
demo.show();
}
} // GraphicsDemo
The GraphicsPanel extends Swing's JPanel class. The programmer can implement a paintComponent() method to draw in the panel:
class GraphicsPanel extends JPanel {
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// graphics code goes here
}
}
A JFrame consists of four panes: root pane, layered pane, content pane, and glass pane:
Components are added to the content pane, although we will usually add a JPanel to the content pane and then add controls to the JPanel.
A graphical context is an instance of the Graphics class. We can think of a graphical context as an abstraction of a graphics device. It provides a blank canvas (i.e., a bitmap) and tools for drawing curves, shapes, and text:
class Graphics {
void drawLine(...) { ... }
void drawOval(...) { ... }
void drawString(...) { ... }
// etc.
}
Each time a component, c, needs to be redrawn, a graphical context for c is created and passed to the component's update method:
Graphics g = c.getGraphics();
c.update(g);
Component.update() erases the background of the window, then calls:
c.paintComponent(g);
We can force a call to Component.update() from within the program by calling:
c.repaint();
The paintComponent() method is already defined for controls and containers. Programmers redefine paintComponent() for components to add text, images, and shapes to the graphical context.
The client area is the region of a container excluding the borders and title bar. This is where client drawings actually appear. Points in the client area are identified by their coordinates in a coordinate system in which the positive x-axis runs across the top of the container and the positive y-axis runs down the left side of the container. The unit of measurement is the PIXEL.
A font face name, or font name, has the form "FAMILY STYLE". For example:
"Helvetica Bold"
We create a font object using the Font constructor:
Font(FAMILY, STYLE, SIZE)
class FontPanel extends JPanel {
// Pre-defined logical font names (new
and old):
String[] fonts = {
"SanSerif",
"Serif", "Monospaced", "Dialog",
"DialogInput",
"Helvetica",
"Courier", "ZapfDingbats"
};
// A few pre-defined styles:
int[] styles = {
Font.PLAIN, Font.ITALIC, Font.BOLD,
Font.BOLD + Font.ITALIC
};
public void
paintComponent(Graphics g) {
super.paintComponent(g);
Font font;
FontMetrics fm;
int row = 10;
int points = 10;
for(int i = 0; i < fonts.length;
i++) {
int col = 1;
int height = 0;
for(int j = 0; j <
styles.length; j++) {
// use next font to display
its name:
font = new Font(fonts[i],
styles[j], points);
g.setFont(font);
g.drawString(fonts[i], col,
row);
// update height and row:
fm = g.getFontMetrics(font);
height = Math.max(height,
fm.getHeight());
col +=
(fm.stringWidth(fonts[i]) + 2);
}
row += height + 2; // = next row
}
}
}
public class FontDemo extends jutil.MainJFrame {
public FontDemo() {
setTitle("Font Demo");
Container contentPane =
getContentPane();
contentPane.add(new FontPanel());
}
public static void main(String[] args)
{
FontDemo demo = new FontDemo();
demo.show();
}
}
class ColorPanel extends JPanel {
String[] message = {
"Mercury",
"Venus", "Earth", "Mars", "Jupiter",
"Saturn",
"Neptune", "Uranus", "Pluto"
};
java.util.Random generator = new
java.util.Random();
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int col = 10, row = 10;
int red = 255, green = 0, blue = 0;
for(int i = 0; i <
message.length; i++) {
g.setColor(new Color(red, green,
blue));
g.drawString(message[i], col,
row);
red = generator.nextInt(256);
green = generator.nextInt(256);
blue = generator.nextInt(256);
row += 20;
}
}
}
public class ColorDemo extends jutil.MainJFrame {
public ColorDemo() {
setTitle("Color Demo");
// setBackground(Color.yellow);
Container contentPane = getContentPane();
contentPane.add(new ColorPanel());
}
public static void main(String[] args)
{
ColorDemo demo = new ColorDemo();
demo.show();
}
}
A graphical context provides methods for drawing rectangles:
Here is the listing for this image:
class DrawDemoPanel extends JPanel {
public DrawDemoPanel() {
setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int corner = 20;
int size = 300;
int dec = 20;
g.setColor(Color.gray);
g.fill3DRect(corner, corner, size,
size, true);
corner += dec;
size -= 2 * dec;
g.fill3DRect(corner, corner, size,
size, false);
g.setColor(Color.green);
corner += dec;
size -= 2 * dec;
g.fillRect(corner, corner, size,
size);
g.setColor(Color.red);
corner += dec;
size -= 2 * dec;
g.drawRect(corner, corner, size,
size);
}
}
A graphical context provides methods for drawing ovals:
Here is a listing for this image:
class DrawDemoPanel extends JPanel {
public DrawDemoPanel() {
setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.blue);
g.fillOval(10, 10, getWidth() - 20,
getHeight() - 20);
g.setColor(Color.red);
g.fillOval(40, 40, getWidth() - 80,
getHeight() - 80);
g.setColor(Color.yellow);
g.fillOval(70, 70, getWidth() - 140,
getHeight() - 140);
}
}
public class MetricPanel extends JPanel {
// client area metrics:
protected int xUpperLeft, yUpperLeft,
xLowerRight, yLowerRight;
protected int clientHeight,
clientWidth, xCenter, yCenter;
// screen metrics:
protected int screenHeight,
screenWidth;
protected int xScreenCenter,
yScreenCenter;
protected boolean drawAxes = false;
public void setDrawAxes(boolean flag) {
drawAxes = flag;
}
protected void
updateMetrics() {
Dimension d = getSize();
Insets in = getInsets();
clientWidth = d.width - in.right -
in.left;
clientHeight = d.height - in.bottom
- in.top;
xUpperLeft = in.left;
yUpperLeft = in.top;
xLowerRight = xUpperLeft +
clientWidth;
yLowerRight = yUpperLeft +
clientHeight;
xCenter = xUpperLeft +
clientWidth/2;
yCenter = yUpperLeft +
clientHeight/2;
Toolkit tk =
Toolkit.getDefaultToolkit();
d = tk.getScreenSize();
screenHeight = d.height;
screenWidth = d.width;
xScreenCenter = screenWidth/2;
yScreenCenter = screenHeight/2;
}
public void
drawAxes(Graphics g) {
g.setColor(Color.black);
g.drawLine(xCenter, yUpperLeft,
xCenter, yLowerRight);
g.drawLine(xUpperLeft, yCenter,
xLowerRight, yCenter);
}
public void
paintComponent(Graphics g) {
super.paintComponent(g);
updateMetrics();
if (drawAxes) {
drawAxes(g);
g.setColor(Color.black);
g.drawRect(xUpperLeft + 1,
yUpperLeft + 1,
clientWidth - 2, clientHeight
- 2);
g.drawOval(xCenter - 5, yCenter -
5, 10, 10);
g.fillOval(xUpperLeft,
yUpperLeft, 5, 5);
g.fillOval(xLowerRight - 5,
yLowerRight - 5, 5, 5);
}
}
}
public class MetricsDemo extends jutil.MainJFrame {
public MetricsDemo() {
setTitle("Metrics Demo");
Container contentPane =
getContentPane();
MetricPanel mp = new MetricPanel();
mp.setDrawAxes(true);
contentPane.add(mp);
}
public static void main(String[] args)
{
JFrame demo = new MetricsDemo();
demo.show();
}
}
interface Functor {
double apply(double x);
}
class SineFunctor implements Functor {
public double apply(double x) {
return Math.sin(x);
}
}
class SquareFunctor implements Functor {
public double apply(double x) {
return x * x;
}
}
class GraphPanel extends MetricPanel {
Functor fun = null;
private int xRatio = 100;
private int yRatio = 100;
public void setRatio(int x, int y)
{
xRatio = x;
yRatio = y;
}
public void setFunctor(Functor f) {
fun = f;
}
public void
paintComponent(Graphics g) {
super.paintComponent(g);
updateMetrics();
drawAxes(g);
if (fun != null) {
for(int xc = 0; xc <
clientWidth; xc++) {
double x = (double)(xc -
xCenter)/(double)xRatio;
double y = fun.apply(x);
int yc = yCenter - (int)(y
* yRatio);
g.fillOval(xc - 1, yc - 1,
3, 3);
}
}
}
}
public class GraphDemo extends jutil.MainJFrame {
public GraphDemo() {
setTitle("Graph Demo");
Container contentPane =
getContentPane();
GraphPanel gp = new GraphPanel();
gp.setFunctor(new SineFunctor());
gp.setRatio(30, 100);
contentPane.add(gp);
}
public static void main(String[] args)
{
JFrame demo = new GraphDemo();
demo.show();
}
}
class PolyPanel extends MetricPanel {
private int red = 255, green = 0, blue
= 0;
private int sides = 5;
public void setSides(int s) {
sides = s;
}
public void setColor(int r, int g, int
b) {
red = r;
green = g;
blue = b;
}
public void
paintComponent(Graphics g) {
super.paintComponent(g);
updateMetrics();
g.setColor(new Color(red, green,
blue));
Polygon p = new Polygon();
double radius =
Math.min(clientHeight, clientWidth)/2;
double angle = 2 * Math.PI/sides;
for(int i = 0; i < sides; i++)
p.addPoint(
(int)(xCenter + radius *
Math.cos(i * angle)),
(int)(yCenter + radius *
Math.sin(i * angle)));
g.drawPolygon(p);
g.setColor(Color.black);
String s = "# sides = " +
sides;
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(s);
int h = fm.getHeight();
g.drawString(s, xCenter - w/2,
yCenter - h/2);
}
}
public class PolyDemo extends jutil.MainJFrame {
public PolyDemo() {
setTitle("Poly Demo");
Container contentPane =
getContentPane();
PolyPanel pp = new PolyPanel();
pp.setColor(255, 0, 255);
pp.setSides(9);
contentPane.add(pp);
}
public static void main(String[] args)
{
JFrame demo = new PolyDemo();
demo.show();
}
}