Session 30
Review Interfaces, Inner Classes
DoubleBuffering
Review : Listeners Implement
Interfaces
Examples
• ScrollBarListener implements AdjustmentListener
• FireButtonListener implements ActionListener
What is an interface?
• A list of responsibilities.
• A set of messages to which an object promises to
respond.
• Sometimes called a protocol.
• Like a class with no behavior.
Review : Listeners in the
CannonWorld
Each GUI component needs a listener to wait for
the user to manipulate and then relay the event to
the CannonWorld.
private JScrollBar slider;
private class ScrollBarListener implements AdjustmentListener {
public void adjustmentValueChanged( AdjustmentEvent e ){
angle = slider.getValue();
message = "Angle: " + angle;
repaint();
}
}
Review : Listeners in the
CannonWorld
private class FireButtonListener implements ActionListener {
public void actionPerformed( ActionEvent e ) {
double radianAngle = angle * Math.PI / 180.0;
double sinAngle = Math.sin(radianAngle);
double cosAngle = Math.cos(radianAngle);
cannonBall = new CannonBall( 20 + (int) (30 * cosAngle),
dy(5+(int) (30 * sinAngle)),
5,
12 * cosAngle,
-12 * sinAngle );
repaint();
}
}
Review : Listeners Implement
Interfaces
How do we create one?
Just like a class, without the method bodies:
public interface AdjustmentListener {
public void adjustmentValueChanged(
AdjustmentEvent e );
}
Why Use Interfaces
Why do we use interfaces in this program?
• When the user presses a button, the Java run-
time system sends an actionPerformed()
message to any object that is listening for the
button’s events. If we want our listener to listen,
it must listen for that particular message.
• When the user adjusts a slider, the Java run-time
system sends an adjustmentValueChanged()
message to any object that is listening for the
slider’s events. If we want our listener to listen, it
must listen for that particular message.
Why Use Interfaces
More generally, why do we use interfaces?
• To allow our objects to work inside an
existing framework.
• To allow programmers to create objects
that fulfill responsibilities — without
committing to how their objects do so!
Introducing Double Buffering
• How can we get rid of that annoying
flickering?
Double buffering
• That annoying “flicker” in
the graphics is caused
by the program drawing
to the “active” Graphics
object.
• Double buffering is a
mechanism for drawing
to one, “offscreen”
Graphics object and then
replacing it for the
“onscreen” Graphics
object.
Double Buffering
• With the AWT you used to have to
“manage” this switch between primary
surface and back buffer.
• With Swing it is handled for you _IF_ you
use the appropriate objects.
Ways to DoubleBuffer
• Making the drawing surface be a separate
JPanel that is incorporated into the
JFrame
– 03_PanelBased_DB_CannonWorld
• Use the built in BufferStrategy class
– 04_StrategyBased_DB_CannonWorld
public class CannonWorld extends JFrame {
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 400;
private int angle;
private String message;
private CannonBall cannonBall;
private JScrollBar slider;
public CannonWorld() {
setSize ( FRAME_WIDTH, FRAME_HEIGHT );
setTitle( "Cannon Game" );
angle = 45;
message = "Angle: " + angle;
cannonBall = null;
slider = new JScrollBar( JScrollBar.VERTICAL, angle, 5, 0, 90 );
slider.addAdjustmentListener( new ScrollBarListener() );
getContentPane().add( "East", slider );
JButton fire = new JButton( "fire" );
fire.addActionListener( new FireButtonListener() );
getContentPane().add( "North", fire );
}
public void paint( Graphics g )
{
super.paint(g);
drawCannon ( g );
drawTarget ( g );
drawCannonBall( g );
writeMessage ( g );
}
…
}
public class CannonWorld extends JFrame{
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 400;
private int angle;
private String message; Let’s separate the “graphics” issues
private CannonBall cannonBall; from the other issues.
private JScrollBar slider;
public CannonWorld() { Separate this one class into two –
setSize ( FRAME_WIDTH, FRAME_HEIGHT ); one that extends JPanel and one
setTitle( "Cannon Game" );
that extends JFrame.
angle = 45;
message = "Angle: " + angle;
cannonBall = null;
slider = new JScrollBar( JScrollBar.VERTICAL, angle, 5, 0, 90 );
slider.addAdjustmentListener( new ScrollBarListener() );
getContentPane().add( "East", slider );
JButton fire = new JButton( "fire" );
fire.addActionListener( new FireButtonListener() );
getContentPane().add( "North", fire );
}
public void paint( Graphics g )
{
super.paint(g);
drawCannon ( g );
drawTarget ( g );
drawCannonBall( g );
writeMessage ( g );
}
…
}
public class CannonWorld extends JFrame {
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 400;
private int angle;
private String message; Let’s separate the “graphics” issues
private CannonBall cannonBall; from the other issues.
private JScrollBar slider;
public CannonWorld() { Separate this one class into two –
setSize ( FRAME_WIDTH, FRAME_HEIGHT ); one that extends JPanel and one
setTitle( "Cannon Game" );
that extends JFrame.
angle = 45;
message = "Angle: " + angle;
cannonBall = null;
slider = new JScrollBar( JScrollBar.VERTICAL, angle, 5, 0, 90 );
slider.addAdjustmentListener( new ScrollBarListener() );
getContentPane().add( "East", slider );
JButton fire = new JButton( "fire" );
fire.addActionListener( new FireButtonListener() );
getContentPane().add( "North", fire );
}
public void paint( Graphics g )
{
super.paint(g);
drawCannon ( g );
drawTarget ( g );
drawCannonBall( g );
writeMessage ( g );
}
…
}
public class CannonWorld extends JFrame {
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 400;
private int angle;
private String message; Let’s separate the “graphics” issues
private CannonBall cannonBall; from the other issues.
private JScrollBar slider;
public CannonWorld() { Separate this one class into two –
setSize ( FRAME_WIDTH, FRAME_HEIGHT ); one that extends JPanel and one
setTitle( "Cannon Game" );
that extends JFrame.
angle = 45;
message = "Angle: " + angle;
cannonBall = null;
slider = new JScrollBar( JScrollBar.VERTICAL, angle, 5, 0, 90 );
slider.addAdjustmentListener( new ScrollBarListener() );
getContentPane().add( "East", slider );
JButton fire = new JButton( "fire" );
fire.addActionListener( new FireButtonListener() );
getContentPane().add( "North", fire );
}
public void paint( Graphics g )
{
super.paint(g);
drawCannon ( g );
drawTarget ( g );
drawCannonBall( g );
writeMessage ( g );
}
…
}
public class CannonPanel extends JPanel {
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 340;
private String message;
private CannonBall cannonBall;
private int angle;
public CannonPanel() {
super();
setPreferredSize( new Dimension(FRAME_WIDTH, FRAME_HEIGHT) );
angle = 0;
message = "";
cannonBall = null;
}
//Just about all the methods from CannonWorld
public void launchCannonBall() { //Moved from FireButtonListener to here
double radianAngle = angle * Math.PI / 180.0;
double sinAngle = Math.sin( radianAngle );
double cosAngle = Math.cos( radianAngle );
cannonBall = new CannonBall (
10 + (int) (30 * cosAngle),
dy(10+(int) (30 * sinAngle)),
5, 12 * cosAngle, -12 * sinAngle );
}
public void setAngle(int in) { //Moved from AngleListener to here
angle = in;
message = "Angle : "+in;
}
}
public class CannonWorld extends JFrame {
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT = 400;
private JScrollBar slider;
private CannonPanel myPanel;
public CannonWorld() {
setSize ( FRAME_WIDTH, FRAME_HEIGHT );
setTitle( "Cannon Game" );
myPanel = new CannonPanel(); // automatically double buffered
getContentPane().add("Center",myPanel);
myPanel.setAngle(45);
slider = new JScrollBar( JScrollBar.VERTICAL, 45, 5, 0, 90 );
slider.addAdjustmentListener( new ScrollBarListener() );
getContentPane().add( "East", slider );
JButton fire = new JButton( "fire" );
fire.addActionListener( new FireButtonListener() );
getContentPane().add( "North", fire );
}
//Notice, no paint method any more
private class FireButtonListener implements ActionListener {
public void actionPerformed( ActionEvent e ) {
myPanel.launchCannonBall();
repaint();
}
}
private class ScrollBarListener implements AdjustmentListener {
public void adjustmentValueChanged (AdjustmentEvent e) {
myPanel.setAngle( slider.getValue() );
repaint();
}
}
Ways to DoubleBuffer
• Making the drawing surface be a separate
JPanel that is incorporated into the
JFrame
– 03_PanelBased_DB_CannonWorld
• Use the built in BufferStrategy class
– 04_StrategyBased_DB_CannonWorld
//all the previous imports
import java.awt.image.BufferStrategy;
public class CannonWorld extends JFrame
{
//all the previous instance variables
private BufferStrategy myStrategy;
public CannonWorld()
{
//all the body of the previous constructor
this.setVisible(true);
this.createBufferStrategy(2);
}
public void paint( Graphics g )
{
while (myStrategy==null)
{
myStrategy=this.getBufferStrategy();
}
g = myStrategy.getDrawGraphics();
//all the body from the previous paint method
myStrategy.show();
//g.dispose();
// Toolkit.getDefaultToolkit().sync();
}