Chapter 5
JFC
JFC is a collection of packages that provide GUI and graphics components:
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:
GUI 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.
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:
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.
Graphical Contexts
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:
Client Area
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.
Fonts
A font face name, or font name, has the form "FAMILY STYLE". For example:
FontPanel Class
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 FontDemo()
{
setTitle("Font Demo");
Container contentPane = getContentPane();
contentPane.add(new FontPanel());
}
public static void
main(String[] args) {
FontDemo demo = new FontDemo();
demo.show();
}
}
ColorDemo
ColorPanel Class
public void paintComponent(Graphics
g) {
super.paintComponent(g);
int column = 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));
red = generator.nextInt(256);
green = generator.nextInt(256);
blue = generator.nextInt(256);
g.drawString(message[i], row, column);
column += 20;
}
}
}
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:
Here is a listing for this image:
Metrics Demo
MetricPanel Class
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.right;
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 static void
main(String[] args) {
JFrame demo = new MetricsDemo();
demo.show();
}
}
Functors
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;
}
}
public void paintComponent(Graphics
g) {
super.paintComponent(g);
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);
}
}
}
}
PolyPanel Class
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);
}
}
Activating a control-- pushing a button, adjusting a scroll bar, typing in a text box, selecting an item from a list -fires a semantic event. The control is the source of the event. Semantic events are actually sequences of low-level events. For example, the adjusting-the-scroll-bar event is a sequence of mouse events: moving the mouse over the scroll bar's thumb tab, holding down the left mouse button, moving the mouse up or down, then releasing the left mouse button. Semantic and low-level events are represented by objects in Java:
ButtonPanel Class
public ButtonPanel()
{
JButton rb = new JButton("Red");
JButton bb = new JButton("Blue");
JButton gb = new JButton("Green");
rb.addActionListener(this);
gb.addActionListener(this);
bb.addActionListener(this);
add(rb);
add(bb);
add(gb);
}
public void actionPerformed(ActionEvent
ae) {
String cmmd = ae.getActionCommand();
if (cmmd.equals("Green")) {
setBackground(Color.green);
} else if (cmmd.equals("Blue")) {
setBackground(Color.blue);
} else {
setBackground(Color.red);
}
repaint();
}
}
MousePanel Class
public MousePanel()
{
addMouseListener(new MouseEventHandler());
addMouseMotionListener(new MouseMotionHandler());
}
public void paintComponent(Graphics
g) {
super.paintComponent(g);
g.setColor(new Color(red, green, blue));
for(int i = 0; i < drawing.size(); i++) {
Point p = (Point)drawing.get(i);
g.fillOval(p.x, p.y, brushWidth, brushWidth);
}
}
}
class MouseEventHandler extends
MouseAdapter {
public void mouseClicked(MouseEvent
e) {
if (e.getClickCount() > 1) {
drawing.clear();
repaint(); // repaint entire window
}
}
public void mouseReleased(MouseEvent
e) {
setCursor(Cursor.getDefaultCursor());
}
}
public class MainJFrame extends JFrame {
protected int screenHeight;
protected int screenWidth;
protected String
title = "Main Window";
private class Terminator
extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
public MainJFrame()
{
addWindowListener(new Terminator());
setTitle(title);
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
screenHeight = d.height;
screenWidth = d.width;
setSize(screenWidth/2, screenHeight/2);
setLocation(screenWidth/4, screenHeight/4);
}
// test harness
public static void
main(String[] args) {
MainJFrame frame = new MainJFrame();
frame.show();
}
}
Flow Layout
The default layout manager for JDialog is flow layout. This manager adds controls from left to right, starting a new row each time it runs out of room. The main feature of flow layout is that it attempts to center align the components.
Here is a simple keypad created using the flow layout manager:
KeyPadPanel Class
public KeyPadPanel()
{
add(display);
add(b1);
add(b2);
add(b3);
add(b4);
add(b5);
add(b6);
add(b7);
add(b8);
add(b9);
add(bSharp);
add(b0);
add(bStar);
}
}
KeyPadPanel Constructor
Paneled Controls
KeyPadPanel Constructor
Text Fields and Labels
Example: Temperature Converter
If the user mistakenly enters a non-numeric temperature (e.g. "zero"), then an error dialog box appears:
TempConverter Class
A temperature panel is an action listener for the two text fields it contains:
The layout adds a paneled label and a paneled text field to rows one and two of a grid layout panel. This happens twice, once for each text field. (Setting the background color to white makes it easier to see the paneled controls.)
p = new JPanel();
p.setLayout(new GridLayout(2, 1));
p1 = new JPanel();
p1.add(new JLabel("centigrade"));
p.add(p1);
p1 = new JPanel();
p1.add(cenField);
p.add(p1);
add(p);
cenField.setHorizontalAlignment(JTextField.CENTER);
farField.setHorizontalAlignment(JTextField.CENTER);
cenField.addActionListener(this);
farField.addActionListener(this);
}
Dialog Boxes
Let's add a modal dialog box to the draw program:
Here's what the dialog looks like:
Mouse Panel
public MousePanel panel = new MousePanel();
public Draw() {
setTitle("Draw");
Container contentPane = getContentPane();
contentPane.add(panel);
addKeyListener(new KeyboardHandler());
}
class KeyboardHandler extends KeyAdapter { ... }
public static void
main(String[] args) {
JFrame demo = new Draw();
demo.show();
}
}
private TextField
redField = new TextField("255", 20);
private TextField
greenField = new TextField("0", 20);
private TextField
blueField = new TextField("0", 20);
private TextField
penField = new TextField("0", 20);
private JButton
okButton = new JButton("OK");
private JButton
cancelButton = new JButton("Cancel");
public int red
= 255, green = 0, blue = 0, pen = 5;
public DrawDialog(JFrame
parent, int r, int g, int b, int pn) {
...
}
public void actionPerformed(ActionEvent
ae) { ... }
}
JPanel dialogPanel = new JPanel();
dialogPanel.setLayout(new GridLayout(4, 1));
JPanel p, p1;
p = new JPanel();
p.setLayout(new GridLayout(1, 2));
p1 = new JPanel();
p1.add(new JLabel("Red"));
p.add(p1);
p1 = new JPanel();
p1.add(redField);
p.add(p1);
//redField.setHorizontalAlignment(JTextField.CENTER);
redField.addActionListener(this);
dialogPanel.add(p);
// same for green, blue, and pen
p = new JPanel();
p.add(okButton);
okButton.addActionListener(this);
p.add(cancelButton);
cancelButton.addActionListener(this);
Container contentPane = getContentPane();
contentPane.add("Center", dialogPanel);
contentPane.add("South", p);
}
Now let's make the Draw Dialog modeless. Here's the only change to the constructor: