190 likes | 197 Views
Learn how interfaces work, create listeners in Java, prevent flickering in graphics with double buffering, and manage responsibilities using examples. Separate graphics code for better program structure.
E N D
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 ); } … }
Let’s separate the “graphics” issues from the other issues. Separate this one class into two – one that extends JPanel and one that extends JFrame. 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 ); } … }
Let’s separate the “graphics” issues from the other issues. Separate this one class into two – one that extends JPanel and one that extends JFrame. 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 ); } … }
Let’s separate the “graphics” issues from the other issues. Separate this one class into two – one that extends JPanel and one that extends JFrame. 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 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 importsimport 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 constructorthis.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(); }