- You be the layout manager. Sketch what the GUI will look like if we run the following snippet of code (assume we've set up the headers correctly and added the appropriate methods to the frame to tell it the size, visible flag and DefaultCloseOperation) and then resize the window to be bigger (in both horizontal and vertical directions):
JFrame frame = new JFrame();
JPanel panelWest = new JPanel();
JPanel panelEast = new JPanel();
JPanel panelSouth = new JPanel();
JButton b1 = new JButton("1");
JButton b2 = new JButton("2");
JButton b3 = new JButton("3");
JButton b4 = new JButton("4");
JButton b5 = new JButton("5");
JButton b6 = new JButton("6");
JButton b7 = new JButton("7");
JButton b8 = new JButton("8");
JButton b9 = new JButton("9");
JButton ba = new JButton("a");
JButton bb = new JButton("b");
JButton bc = new JButton("c");
frame.getContentPane().add(BorderLayout.CENTER, b1);
frame.getContentPane().add(BorderLayout.NORTH, b2);
frame.getContentPane().add(BorderLayout.EAST, b3);
frame.getContentPane().add(BorderLayout.SOUTH, b4);
frame.getContentPane().add(BorderLayout.WEST, panelWest);
panelWest.setLayout(new BorderLayout());
panelWest.add(BorderLayout.CENTER, b5);
panelWest.add(BorderLayout.NORTH, b6);
panelWest.add(BorderLayout.EAST, panelEast);
panelWest.add(BorderLayout.SOUTH, panelSouth);
panelWest.add(BorderLayout.WEST, b7);
panelEast.setLayout(new BoxLayout(panelEast, BoxLayout.Y_AXIS));
panelEast.add(b8);
panelEast.add(b9);
panelSouth.add(ba);
panelSouth.add(bb);
panelSouth.add(bc);
Answer:
These questions are actually pretty easy. We see the top-level Frame is a BorderLayout with 1,2,3 and 4 placed in the CENTER, N, E and S spots respectively. The W spot is a panel which is also a BorderLayout (recall the default layout for a Panel is FlowLayout). It has buttons 5,6 and 7 placed in its CENTER, N and W slots. Its E slot is yet another Panel, but this time it's BoxLayout with a vertical stack. So that covers 8 and 9. The S panel has default placement (FlowLayout, which means L to R and wrap when needed), so we place a, b and c on the bottom of the inner Border's south slot (same width as the 6 in the north slot) , L to R.

- Complete the following code to author a simple GUI that counts how many times a button (labeled "Click me") has been clicked and displays that number below the button saying: "i clicks", where i should be a number from 0 (at startup) to the number of clicks. Try to write it in the fewest number of lines.
Here's a screenshot of what it should look like at startup:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Clicker ___________________________________ {
public static void main (String [] args) {
Clicker gui = new Clicker();
gui.go();
}
public void go() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(100,100);
frame.setVisible(true);
}
}
Answer:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Clicker implements ActionListener {
JLabel iLabel;
int i = 0;
public static void main (String [] args) {
Clicker gui = new Clicker();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
JButton button = new JButton("Click me");
iLabel = new JLabel("" + i);
JLabel clicksLabel = new JLabel("clicks");
button.addActionListener(this);
frame.getContentPane().add(BorderLayout.NORTH, button);
frame.getContentPane().add(BorderLayout.CENTER, iLabel); // or WEST...
frame.getContentPane().add(BorderLayout.EAST, clicksLabel); // and CENTER
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(100,100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
iLabel.setText("" + ++i);
}
}
- What is the reason we often use inner classes with GUIs?
Answer:
Well, since we can't implement a method with the same signature more than once in a class (how would the system know which one to invoke?), we often get stuck because the ActionListener interface can ony be implemented once. However, we will probably need to get action events for more than one GUI element (say, two buttons). We have no way (except option two on page 373 which breaks the beautiful OO paradigm) to have two different listeners that have access to our internal state. Inner classes are the beautiful solution, in that they allow us to have multiple listener classes which all implement the ActionListener interface, and all have access to our GUI's instance variables.
- Now, we'd like to build a GUI that animates a ball (with diameter of
DIAMETER) bouncing left and right in a box (outer length OUTER, overall distance traveled each bounce INNER), faster and faster, doubling in speed with every bounce until it jumps off the screen. Note, we wrote this code in a rush, there may be bugs in it! Your job is to find them and identify whether they are syntax, logical, or style bugs, then correct all the bugs...

import java.awt.*;
public class Bounce {
int x = 0;
public static final int DIAMETER = 100;
public static final int OUTER = 356; // some power of 2 + DIAMETER
public static final int INNER = OUTER - DIAMETER;
public static void main (String [] args) {
JFrame frame = new JFrame();
MyDrawPanel drawPanel = new MyDrawPanel();
frame.add(BorderLayout.CENTER, drawPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(OUTER, DIAMETER);
frame.setVisible(true);
int speed = 0;
while(Math.abs(speed) <= INNER) { // can't jump out in 1 step
while(0 <= x && x <= INNER) { // x is still inside box
paintComponent(g);
Thread.sleep(5);
x += speed;
}
x -= speed; // Step back (the last while stepped out)
speed *= -2; // Double speed but reverse direction
}
}
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(blue);
g.fillOval(x, 0, DIAMETER, DIAMETER);
}
}
}
Answer:
Here are the problems we found (the corrected solution is below)
- Syntax: We forgot to include "import javax.swing.*"
- Syntax: This one is subtle -- you might remember from the days when you learned about static and non-static methods that non-static instance variables (here, x) can't be referenced from a static method. Well, we're doing that here. We've got to wrap all of our GUI code (that uses x) in a non-static method, say,
go.
- Style: It's more appropriate to add the panel to the pane than the frame directly. See HFJ 2/e p.354.
- Logical: We have to initialize
speed with 1 (or a non-zero number) if we're going to increase it with multiplication
- Syntax: paintComponent() is NEVER called explicitly by the user (see HFJ 2/e p. 364). We call the drawing panel's
repaint and it takes care of calling paintComponent for us.
- Syntax: We have to wrap the
Thread.sleep() call around a try/catch block because it'll throw a java.lang.InterruptedException which must be caught or thrown.
- Logical: We need to erase what was already there, otherwise we'll get smeared results.
- Syntax:
blue is an unbound variable; we have to use Color.blue here.
import javax.swing.*; ### bug 1
import java.awt.*;
public class Bounce {
int x = 0;
public static final int DIAMETER = 100;
public static final int OUTER = 356; // some power of 2 + DIAMETER
public static final int INNER = OUTER - DIAMETER;
public static void main (String [] args) {
Bounce gui = new Bounce(); ### bug 2
gui.go(); ### bug 2
} ### bug 2
public void go() { ### bug 2
JFrame frame = new JFrame();
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel); ### bug 3
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(OUTER, DIAMETER);
frame.setVisible(true);
int speed = 1; ### bug 4
while(Math.abs(speed) <= INNER) { // can't jump out in 1 step
while(0 <= x && x <= INNER) { // x is still inside box
drawPanel.repaint(); ### bug 5
try { ### bug 6
Thread.sleep(5);
} catch(Exception ex) { }
x += speed;
}
x -= speed; // Step back (the last while stepped out)
speed *= -2; // Double speed but reverse direction
}
}
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.white); ### bug 7
g.fillRect(0, 0, this.getWidth(), this.getHeight()); ### bug 7
g.setColor(Color.blue); ### bug 8
g.fillOval(x, 0, DIAMETER, DIAMETER);
}
}
}