Presentation is loading. Please wait.

Presentation is loading. Please wait.

0/106 Continuation From Last Time. 1/102 A Third Swing App: ColorPanelApp ●Specification: An app with three sliders that the user can drag to change the.

Similar presentations


Presentation on theme: "0/106 Continuation From Last Time. 1/102 A Third Swing App: ColorPanelApp ●Specification: An app with three sliders that the user can drag to change the."— Presentation transcript:

1 0/106 Continuation From Last Time

2 1/102 A Third Swing App: ColorPanelApp ●Specification: An app with three sliders that the user can drag to change the RGB values of the background color ●Useful classes: JFrame, JPanel, JSlider, ChangeListener JFrame JSlider JPanel

3 2/102 DEMO: ColorPanelApp

4 3/102 ●A JSlider lets the user graphically select a value by sliding an arrow within a bounded interval ●One way to construct is by passing two int s (minimum and maximum values) as arguments: JSlider slider = new JSlider(0, 30); ●To get the value of the slider, we can use the getValue method ●A JSlider emits ChangeEvent s when value changes-- to listen for them, we use a ChangeListener Using JSliders

5 4/102 ChangeListener s ChangeListener public void stateChanged( ) { // Respond to ChangeEvent here! // (print something to the console, tell // a JLabel to update its text, etc.) } JSlider ChangeEvent e

6 5/102 Process: ColorPanelApp 1.Create top-level class that contains a JFrame 2.Write a subclass of JPanel that contains three JSlider s, add an instance of it to the JFrame 3.Write a ChangeListener that changes a JPanel ’s color based on value of a JSlider 4.Add ChangeListener s to the JSlider s JFrame JSlider JPanel

7 6/102 Top-level Class: ColorPanelApp ●This top-level class is almost identical to those of the other examples-- we follow same pattern of setting up JFrame ●Instantiate JFrame, set its close operation, call pack and setVisible public class ColorPanelApp { public ColorPanelApp() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new ColorPanelApp(); } }

8 7/102 Process: ColorPanelApp 1.Create top-level class that contains a JFrame 2.Write a subclass of JPanel that contains three JSlider s, add an instance of it to the JFrame 3.Write a ChangeListener that changes a JPanel ’s color based on value of a JSlider 4.Add ChangeListener s to the JSlider s JFrame JSlider JPanel

9 8/102 JPanel Subclass: ColorPanel ● ColorPanel “is a” JPanel ●As in other examples, create a Dimension, give it desired width and height, then call setPreferredSize ●Want panel to start out gray, so call method setBackground public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlider sliderRed = new JSlider(0, 255); JSlider sliderGreen = new JSlider(0, 255); JSlider sliderBlue = new JSlider(0, 255); this.add(sliderRed); this.add(sliderGreen); this.add(sliderBlue); } }

10 9/102 JPanel Subclass: ColorPanel ●Instantiate three JSliders : one to control each channel (red, green, and blue) ●Arguments are beginning/end of slider’s range: we give each range (one byte/RGB channel) ●Finally, add each slider to panel public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlider sliderRed = new JSlider(0, 255); JSlider sliderGreen = new JSlider(0, 255); JSlider sliderBlue = new JSlider(0, 255); this.add(sliderRed); this.add(sliderGreen); this.add(sliderBlue); } }

11 10/102 Adding a ColorPanelApp to the JFrame ●Just as we did in previous examples, first instantiate a panel, then call add to add it to the JFrame public class ColorPanelApp { public ColorPanelApp() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); ColorPanel panel = new ColorPanel(); frame.add(panel); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new ColorPanelApp(); } }

12 11/102 Process: ColorPanelApp 1.Create top-level class that contains a JFrame 2.Write a subclass of JPanel that contains three JSlider s, add an instance of it to the JFrame 3.Write a ChangeListener that changes a JPanel ’s color based on value of a JSlider 4.Add ChangeListener s to the JSlider s JFrame JSlider JPanel

13 12/102 Changing a JPanel ’s Color ●Every JPanel has a pair of accessor and mutator methods, setBackground and getBackground ●We have three JSlider s-- one to control each color channel of the Jpanel. Each Jslider has its own listener ●When red slider’s listener detects that its slider has been moved: o it calls getBackground to ask the panel for its current color o it creates a new instance of Color, using the “R” value from the slider and the “G” and “B” values from the panel’s current background color (since they haven’t changed) o it calls setBackground, passing in the new Color

14 13/102 Our ChangeListener : SliderListener ●A SliderListener ’s job is to change the color of a JPanel based on the value of a JSlider ●Since JSlider s emit ChangeEvent s whenever their value changes, SliderListener implements ChangeListener interface ●Defines method stateChanged. SliderListener will be coded generically so it can be used for each slider public class SliderListener implements ChangeListener { private JPanel _panel; private int _channel; private JSlider _slider; public SliderListener(JPanel panel, int channel, JSlider slider){ _panel = panel; _channel = channel; _slider = slider; public void stateChanged(ChangeEvent e) { // implementation elided for now }

15 14/102 Our ChangeListener : SliderListener ●To do its job, it needs to know three things: o The JPanel whose color it should change o Which channel (red, green, or blue) to modify o Which slider it listens to ●We’ll represent the channel as an int : 0 means red, 1 means green, 2 means blue public class SliderListener implements ChangeListener { private JPanel _panel; private int _channel; private JSlider _slider; public SliderListener(JPanel panel, int channel, JSlider slider){ _panel = panel; _channel = channel; _slider = slider; public void stateChanged(ChangeEvent e) { // implementation elided for now } }

16 15/106 The stateChanged method ●Method is called whenever value of the listener’s JSlider changes ●Should modify the appropriate color channel of the JPanel ’s background color ●Must take in a ChangeEvent as a parameter, because it is required by the ChangeListener interface; doesn’t use it in this case // rest of class SliderListener elided for public void stateChanged(ChangeEvent e) { int val = _slider.getValue(); Color bg = _panel.getBackground(); Color newbg; switch(_channel){ case 0: newbg = new Color(val, bg.getGreen(), bg.getBlue()); break; case 1: newbg = new Color(bg.getRed(), val, bg.getBlue()); break; default: newbg = new Color(bg.getRed(), bg.getGreen(), val); } _panel.setBackground(newbg); }

17 16/106 The stateChanged method ●Need to determine new value of JSlider ●Since we know the source is the JSlider, we can retrieve value by calling getValue on _slider // rest of class SliderListener elided for public void stateChanged(ChangeEvent e) { int val = _slider.getValue(); Color bg = _panel.getBackground(); Color newbg; switch(_channel){ case 0: newbg = new Color(val, bg.getGreen(), bg.getBlue()); break; case 1: newbg = new Color(bg.getRed(), val, bg.getBlue()); break; default: newbg = new Color(bg.getRed(), bg.getGreen(), val); } _panel.setBackground(newbg); } }

18 17/106 The stateChanged method ●Next, we get the JPanel ’s current background color ●We declare a new Color, newBg, to which we’ll assign a different value based on which color channel the listener is supposed to change // rest of class SliderListener elided for public void stateChanged(ChangeEvent e) { int val = _slider.getValue(); Color bg = _panel.getBackground(); Color newbg; switch(_channel){ case 0: newbg = new Color(val, bg.getGreen(), bg.getBlue()); break; case 1: newbg = new Color(bg.getRed(), val, bg.getBlue()); break; default: newbg = new Color(bg.getRed(), bg.getGreen(), val); } _panel.setBackground(newbg); } }

19 18/106 The stateChanged method ●Depending on the value of _channel, we set newBg to a different color ●In each case, only the specified color channel changes; we use the get s for the values of the unchanged channels ●Once we’ve changed the appropriate channel, we set the JPanel ’s background color to newBg // rest of class SliderListener elided for public void stateChanged(ChangeEvent e) { int val = _slider.getValue(); Color bg = _panel.getBackground(); Color newbg; switch(_channel){ case 0: newbg = new Color(val, bg.getGreen(), bg.getBlue()); break; case 1: newbg = new Color(bg.getRed(), val, bg.getBlue()); break; default: newbg = new Color(bg.getRed(), bg.getGreen(), val); } _panel.setBackground(newbg); }

20 19/106 Process: ColorPanelApp 1.Create top-level class that contains a JFrame 2.Write a subclass of JPanel that contains three JSlider s, add an instance of it to the JFrame 3.Write a ChangeListener that changes a JPanel ’s color based on value of a JSlider 4.Add ChangeListener s to the JSlider s JFrame JSlider JPanel

21 20/106 Setting up our SliderListener s ●Back in ColorPane l class ●Call method addChangeListener on each slider, passing in a new instance of SliderListener ●Pass in ColorPanel, appropriate channel number and slider as arguments to each SliderListener public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlider sliderRed = new JSlider(0, 255); JSlider sliderGreen = new JSlider(0, 255); JSlider sliderBlue = new JSlider(0, 255); sliderRed.addChangeListener( new SliderListener(this, 0, sliderRed)); sliderGreen.addChangeListener( new SliderListener(this, 1, sliderGreen)); sliderBlue.addChangeListener( new SliderListener(this, 2, sliderBlue)); this.add(sliderRed); this.add(sliderGreen); this.add(sliderBlue); } }

22 21/106 The Whole App (1/2) public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlider sliderRed = new JSlider(0, 255); JSlider sliderGreen = new JSlider(0, 255); JSlider sliderBlue = new JSlider(0, 255); sliderRed.addChangeListener( new SliderListener(this, 0, sliderRed)); sliderGreen.addChangeListener( new SliderListener(this, 1, sliderGreen)); sliderBlue.addChangeListener( new SliderListener(this, 2, sliderBlue)); this.add(sliderRed); this.add(sliderGreen); this.add(sliderBlue); } public class ColorPanelApp { public ColorPanelApp() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); ColorPanel panel = new ColorPanel(); frame.add(panel); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new ColorPanelApp(); }

23 22/106 The Whole App (2/2) public class SliderListener implements ChangeListener { private JPanel _panel; private int _channel; public SliderListener(JPanel panel, int channel){ _panel = panel; _channel = channel; public void stateChanged(ChangeEvent e) { int val = _slider.getValue(); Color bg = _panel.getBackground(); Color newbg; switch(_channel){ case 0: newbg = new Color(val, bg.getGreen(), bg.getBlue()); break; case 1: newbg = new Color(bg.getRed(), val, bg.getBlue()); break; default: newbg = new Color(bg.getRed(), bg.getGreen(), val); } _panel.setBackground(newbg); } }

24 23/106 Putting It All Together: ColorPanelApp ColorPanelApp JFrameColorPanel JSliderSliderListener

25 24/106 Buttons in Swing Some buttons in Swing: ● javax.swing.JButton ● javax.swing.JToggleButton ● javax.swing.JCheckBox ● javax.swing.JRadioButton

26 25/106 Buttons in Swing

27 26/106 ButtonGroups ●How do we make options mutually exclusive (i.e. you can only click one button at a time)? o put them in a button group o javax.swing.ButtonGroup ● Buttons that can be added to ButtonGroup : JRadioButton JToggleButton JMenuItem (for advanced users)

28 27/106 JRadioButtons & ButtonGroups ●How to use a ButtonGroup : o create buttons JRadioButton rB1 = new JRadioButton(“Green, not Red”); JRadioButton rB2 = new JRadioButton(“Red, not Green”); o create ButtonGroup ButtonGroup bGroup = new ButtonGroup(); o add buttons to ButtonGroup bGroup.add(rB1); bGroup.add(rB2);

29 28/106 public class RadioColorListener implements ActionListener { private JLabel _label; private Color _currentColor public RadioColorListener(JLabel label, Color color) { _label = label; _currentColor = color; public void actionPerformed(ActionEvent e) { _label.setForeground(_currentColor); } Modified ColorTextApp : RadioColorListener

30 29/106 Modified ColorTextApp : ColorTextPanel public class ColorTextPanel extends JPanel { public ColorTextPanel() { JLabel label = new JLabel("CS15 Rocks!"); JRadioButton buttonForRed = new JRadioButton("Red"); JRadioButton buttonForGreen = new JRadioButton(“Green); RadioColorListener redListener = new RadioColorListener(label, Color.RED); RadioColorListener greenListener = new RadioColorListener(label, Color.GREEN); //add listeners to their buttons buttonForRed.addActionListener(redListener); buttonForGreen.addActionListener(greenListener); //create button group and add buttons to it ButtonGroup buttonGroup = new ButtonGroup(); buttonGroup.add(buttonForRed); buttonGroup.add(buttonForGreen); Dimension panelSize = new Dimension(300, 100); this.setPreferredSize(panelSize); //add label and buttons to ColorTextPanel this.add(label); this.add(buttonForRed); this.add(buttonForGreen); } Assume all the swing components are imported! For more complex projects you might want to give the Buttons a separate JPanel and use LayoutManagers (stay tuned!)

31 30/106 More Complicated Swing Apps ●The examples we’ve seen so far have been small and simple ●What if we want to construct a more realistic GUI? ●We can use LayoutManager s to arrange components nicely within JPanel s

32 31/106 Using java.awt.LayoutManager s ●Each JPanel contains a LayoutManager to automatically arrange its sub-components ● LayoutManager interface implemented by classes: o java.awt.FlowLayout o java.awt.GridLayout o java.awt.BorderLayout ●To specify the LayoutManager for a JPanel, either pass an instance of one of these classes to the panel’s constructor or to its setLayout method later

33 32/106 FlowLayout ●Default layout used by all JPanel s ●Arranges components from left to right, top to bottom ●When it reaches the edge of the panel, moves to next row

34 33/106 public class FlowTest extends JFrame { public FlowTest() { super(); this.setSize(200, 300); JButton b1 = new JButton(“One”); JButton b2 = new JButton(“Two”); JButton b3 = new JButton(“Tree”); JButton b4 = new JButton(“Four”); JButton b5 = new JButton(“Five”); JButton b6 = new JButton(“Six”); JPanel mainPanel = new JPanel(); FlowLayout fl = new Flowlayout(); mainPanel.setLayout(fl); mainPanel.add(b1); mainPanel.add(b2); mainPanel.add(b3); mainPanel.add(b4); mainPanel.add(b5); mainPanel.add(b6); this.add(mainPanel); this.pack(); this.setVisible(true); } FlowLayout

35 34/106 GridLayout ●Arranges components in a grid ●Add components left to right, top to bottom ●Must specify grid size in constructor-- number of rows and columns ●Each cell in grid is same size, determined by largest element in grid

36 35/106 public class GridTest extends JFrame { public GridTest() { super(); this.setSize(200, 300); JPanel p1 = new JPanel(); JPanel p2 = new JPanel(); JPanel p3 = new JPanel(); JPanel p4 = new JPanel(); JPanel p5 = new JPanel(); JPanel p6 = new JPanel(); JPanel mainPanel = new JPanel(); p1.setBackground(Color.GREEN); p2.setBackground(Color.RED); p3.setBackground(Color.ORANGE); p4.setBackground(Color.CYAN); p5.setBackground(Color.BLUE); p6.setBackground(Color.YELLOW); GridLayout gl = new GridLayout(3, 2); mainPanel.setLayout(gl); mainPanel.add(p1); mainPanel.add(p2); mainPanel.add(p3); mainPanel.add(p4); mainPanel.add(p5); mainPanel.add(p6); this.add(mainPanel); this.pack(); this.setVisible(true); } GridLayout

37 36/106 BorderLayout ●Splits JPanel into 5 regions: NORTH, SOUTH, EAST, WEST, CENTER o These are all static constants ●Must specify region when adding component to its container

38 37/106 BorderLayout ●Don’t have to fill all regions-- default size of a region is (0, 0) ● BorderLayout will adjust dynamically depending on what has been added ●Typically add to CENTER first-- it should be largest

39 38/106 BorderLayout public class BorderTest extends JFrame { public BorderTest() { super(); this.setSize(300, 200); JPanel p1 = new JPanel(); JPanel p2 = new JPanel(); JPanel p3 = new JPanel(); JPanel p4 = new JPanel(); JPanel p5 = new JPanel(); JPanel mainPanel = new JPanel(); p1.setBackground(Color.RED); p2.setBackground(Color.GREEN); p3.setBackground(Color.ORANGE); p4.setBackground(Color.CYAN); p5.setBackground(Color.BLUE); BorderLayout bl = new BorderLayout(); mainPanel.setLayout(bl); mainPanel.add(p1, BorderLayout.CENTER); mainPanel.add(p2, BorderLayout.NORTH); mainPanel.add(p3, BorderLayout.SOUTH); mainPanel.add(p4, BorderLayout.EAST); mainPanel.add(p5, BorderLayout.WEST); this.add(mainPanel); this.pack(); this.setVisible(true); }

40 39/106 LayoutManager s ●You’ll be using layouts a lot in CS15-- practice playing around with them! ●Try taking the previous example and calling setPreferredSize on each sub-panel to change their sizes and shapes ●If you want to test your understanding (and want to get a headstart on Cartoon), try making something like this:

41 40/106 LayoutManager s

42 41/106 Swing: Summary ●Swing has a variety of tools to help you make GUIs: o JFrames, JPanels, JButtons, and JSliders, arranged using LayoutManagers o ActionListeners and ChangeListeners to respond to user input o Timers to perform a task at regular intervals ●On future projects, instead of relying on support code for graphical elements, you will use these tools yourself!

43 42/106 Lecture 10 Build Your Own Custom Graphics

44 43/106 Creating Custom Graphics ●Last lecture, we introduced you to Swing ●Lots of handy widgets for making your own graphical applications! ●What if you want to create and display your own custom graphics? ●This lecture: build your own graphics using TA-written Shape package and work them into Swing applications

45 44/106 java.awt.geom.RectangularShapes ●Swing provides built-in classes to represent a small amount of geometric information for 2D shapes – they can’t even draw themselves! ●Subclasses of RectangularShape ( Rectangle2D, Ellipse2D, etc.) know how to do a few basic things: o e.g., set and get their current size and graphical position

46 45/106 The Problem ●We need way more functionality than built-in RectangularShape s provide! ●Want all shapes to be able to: o Set and get current color o Have a border of user-specified width and color o Rotate o Know how to paint themselves on the screen!

47 46/106 The Solution: Shape ●The TAs have written the Shape package to provide you with “smart shapes” that have all these capabilities and more! ●You’ll be using Shape subclasses to create beautiful graphics for the rest of the semester

48 47/106 Shape Package To use the TAs’ Shape package, import it using import cs015.prj.Shape.*; Make your own subclass by extending the Shape class in the Shape package and passing a java.awt.geom.RectuangularShape into super ’s constructor.

49 48/106 Shape Class Hierarchy ● Shape package also contains specific shapes: e.g., RectangleShape (don’t confuse with RectangularShape ) and EllipseShape ●Subclasses of abstract Shape class ●All Shape s keep track of a basic set of properties for which they have accessors and mutators Shape RectangleShapeEllipseShape

50 49/106 Accessors and mutators of Shape s public double getX(); public double getY(); public void setLocation(double x, double y); public double getWidth(); public double getHeight(); public void setSize(double width, double height); public Color getBorderColor(); public Color getFillColor(); public void setBorderColor(Color c); public void setFillColor(Color c); // set both border and fill to same color public void setColor(Color c); public double getRotation(); public void setRotation(double degrees); public int getBorderWidth(); public void setBorderWidth(int width); public boolean getVisible(); public void setVisible(boolean visible); Location Size Border Rotation Visibility Color

51 50/106 Pixels and Coordinate System ●The screen is a grid of pixels (tiny dots) ●Cartesian plane with: o origin in upper-left corner o y-axis increasing downward o corresponds to writing order (0, 0) Y X pixels

52 51/106 Pixels and Coordinate System ●Set and get location of shapes using setLocation, getX, and getY ●Location of any shape is upper-left corner of its bounding box location bounding box location

53 52/106 paintComponent : Drawing on JPanel s ●How are components drawn on the screen? ● JComponent s (like JPanel s and JButton s) can paint themselves with Swing’s paintComponent method o you won’t have to call this method yourself - Swing does this automatically ●Also have other methods that automatically take care of painting any Swing components that were added to them ●Remember using the add method to add JButton s to a JPanel ?

54 53/106 paintComponent : Drawing on JPanel s ●Chain of command: 1. Swing automatically tells frame to paint itself 2. frame automatically tells contained panel to paint itself 3. panel paints itself (e.g., its background color) with paintComponent, then automatically tells contained button to paint itself public class PaintingExample { public PaintingExample() { JFrame frame = new JFrame(); JPanel panel = new JPanel(); JButton button = new JButton(); // setting sizes, etc. elided panel.add(button); frame.add(panel); frame.pack(); frame.setVisible(true); }

55 54/106 ●Whenever a JPanel is told to paint itself, paintComponent is called first-- this takes care of painting the panel itself ●Next, another method automatically paints all Swing components that have been added to the panel using the add method ●But the custom shapes we want to paint aren’t Swing components-- they won’t be painted automatically for us, alas Painting Custom Shapes

56 55/106 ●To paint custom shapes on a JPanel, create a subclass of JPanel that partially overrides paintComponent ●This way, we can add to the paintComponent method so it does more than just painting the background of the JPanel ●After painting the JPanel as normal, can add code to the method to specifically tell shapes to paint themselves Overriding paintComponent

57 56/106 Example: MovingShape ●Spec: App with central panel that displays a shape, and a slider that moves the shape back and forth on the panel

58 57/106 DEMO: MovingShape

59 58/106 Process: MovingShape 1.Write top-level class that contains a JFrame and write a constants class to keep track of important values 2.Create MainPanel, a JPanel subclass, to serve as top-level panel, and add an instance to the JFrame 3.Create another JPanel subclass to serve as the ShapePanel, and add an instance to the MainPanel 4.Write a ChangeListener that tells the associated ShapePanel to move the shape, and add an instance to the Jslider (an implicit association) MainPanel ShapePanel JSlider MovingShapeApp JFrameMainPanel ShapePanel Shape JSlider ChangeListener Replace this with subclass of Shape

60 59/106 Top-level Class: MovingShapeApp ●Starts out exactly like examples from last lecture ●Instantiate JFrame ●Specify its closing behavior ●Tell it to resize to fit contents ●Make sure it’s visible public class MovingShapeApp { public MovingShapeApp() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new MovingShapeApp(); } }

61 60/106 Constants Class ●Constants class keeps track of a few important numbers ●For this app, width and height of the shape and of the panel it sits in ●Units: pixels public class Constants { public static final int PANEL_W = 500; public static final int PANEL_H = 500; public static final int SHAPE_W = 100; public static final int SHAPE_H = 100; }

62 61/106 Process: MovingShape 1.Write top-level class that contains a JFrame and write a constants class to keep track of important values 2.Create JPanel subclass to serve as top-level panel, and add an instance to the JFrame 3.Create another JPanel subclass to serve as the shape panel, and add an instance to the top-level panel 4.Write a ChangeListener that tells the shape panel to move the shape, and add an instance to the Jslider MainPanel ShapePanel JSlider MovingShapeApp JFrameMainPanel ShapePanel Shape JSlider ChangeListener

63 62/106 JPanel Subclass: MainPanel ●We know we want main panel to contain a special JPanel with a shape in it, and a JSlider to move the shape ●First, we’ll set up the slider public class MainPanel extends JPanel { public MainPanel() { this.add(shapePanel, BorderLayout.CENTER); }

64 63/106 JPanel Subclass: MainPanel ●Want slider’s value to represent x-coordinate of shape on panel ●Thus, max value (of left top corner) is panel width - shape width public class MainPanel extends JPanel { public MainPanel() { ShapePanel shapePanel = new ShapePanel(); int sliderMax = Constants.PANEL_W - Constants.SHAPE_W; JSlider slider = new JSlider(0, sliderMax); } } PANEL_W SHAPE_W PANEL_W - SHAPE_W ●Instantiate new slider and pass in min/max values as arguments

65 64/106 ShapePanel ●We’re not adding any functionality to the ShapePanel until later ●Adding incomplete, placeholder components like this (to the MainPanel, on next slide) is a form of incremental development o working incrementally can be very useful for debugging! public class ShapePanel extends JPanel { /* we’ll fill this class in later, in particular adding the ellipse */ public ShapePanel(){ }

66 65/106 JPanel Subclass: MainPanel ●Give MainPanel a BorderLayout ● ShapePanel will be added to CENTER position ●Place our JSlider in SOUTH position MainPanel ShapePanel (CENTER) JSlider (SOUTH) public class MainPanel extends JPanel { public MainPanel() { ShapePanel shapePanel = new ShapePanel(); int sliderMax = Constants.PANEL_W-Constants.SHAPE_W; JSlider slider = new JSlider(0, sliderMax); slider.addChangeListener(listener); this.setLayout(new BorderLayout()); this.add(shapePanel, BorderLayout.CENTER); this.add(slider, BorderLayout.SOUTH); } }

67 66/106 Adding a MainPanel to our JFrame ●Just like in examples from last lecture, first instantiate a MainPanel, then call add to add it to the JFrame public class MovingShapeApp { public MovingShapeApp() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MainPanel mainPanel = new MainPanel(); frame.add(mainPanel); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { new MovingShapeApp(); } }

68 67/106 Process: MovingShape 1.Write top-level class that contains a JFrame and write a constants class to keep track of important values 2.Create JPanel subclass to serve as top-level panel, and add an instance to the JFrame 3.Create another JPanel subclass to serve as the shape panel, and add an instance to the top-level panel 4.Write a ChangeListener that tells the shape panel to move the shape, and add an instance to the JSlider MainPanel ShapePanel JSlider MovingShapeApp JFrameMainPanel ShapePanel Shape JSlider ChangeListener

69 68/106 Finishing ShapePanel ●Now we’ll fill in the class we created earlier as a placeholder ●A ShapePanel “is a” JPanel ●Will keep track of a single instance variable: a Shape named _shape public class ShapePanel extends JPanel { private Shape _shape; public ShapePanel() { Dimension dim = new Dimension(Constants.PANEL_W, Constants.PANEL_H); this.setPreferredSize(dim); this.setBackground(Color.WHITE); _shape = new EllipseShape(); _shape.setSize(Constants.SHAPE_W, Constants.SHAPE_H); _shape.setColor(Color.RED); int startX = (Constants.PANEL_W - Constants.SHAPE_W) / 2; int startY = (Constants.PANEL_H - Constants.SHAPE_H) / 2; _shape.setLocation(startX, startY); } /* Other methods elided for now */ }

70 69/106 ShapePanel ●First, specify preferred size of panel using a Dimension as we saw in last lecture’s examples ●Set background color of panel to WHITE ●Initialize _shape and set its size and color (using constants we defined for size) public class ShapePanel extends JPanel { private Shape _shape; public ShapePanel() { Dimension dim = new Dimension(Constants.PANEL_W, Constants.PANEL_H); this.setPreferredSize(dim); this.setBackground(Color.WHITE); _shape = new EllipseShape(); _shape.setSize(Constants.SHAPE_W, Constants.SHAPE_H); _shape.setSolor(Color.RED); int startX = (Constants.PANEL_W - Constants.SHAPE_W) / 2; int startY = (Constants.PANEL_H - Constants.SHAPE_H) / 2; _shape.setLocation(startX, startY); } } /* Other methods elided for now */

71 70/106 ShapePanel ●Calculate proper starting location for _shape (want its center to start in center of panel) and call setLocation (PANEL_W - SHAPE_W) / 2 (PANEL_H - SHAPE_H) / 2 public class ShapePanel extends JPanel { private Shape _shape; public ShapePanel() { Dimension dim = new Dimension(Constants.PANEL_W, Constants.PANEL_H); this.setPreferredSize(dim); this.setBackground(Color.WHITE); _shape = new EllipseShape(); _shape.setSize(Constants.SHAPE_W, Constants.SHAPE_H); _shape.setSolor(Color.RED); int startX = (Constants.PANEL_W - Constants.SHAPE_W) / 2; int startY = (Constants.PANEL_H - Constants.SHAPE_H) / 2; _shape.setLocation(startX, startY); } /* Other methods elided for now */ }

72 71/106 ShapePanel ●Constructor is done-- still need to take care of painting and moving shape ●Override Jpanel’s paintComponent method to make our Shape show up ●It takes in a parameter of type java.awt.Graphics ●Think of this parameter as a paintbrush-- it’s used to draw things on the panel public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now public void paintComponent(Graphics g) { } }

73 72/106 ShapePanel ●First, we call the superclass’s paintComponent method, passing in the instance of Graphics we received from Swing -- the “paintbrush” ●This takes care of painting any Swing components that may have been added to the panel with the add method public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now public void paintComponent(Graphics g) { super.paintComponent(g); } }

74 73/106 ShapePanel ●Now need to tell _shape to paint itself ●A Shape has all necessary information to paint itself (color, size, etc.) ●To tell _shape to paint itself, we call its paint method, passing in a Graphics, the same “paintbrush” that paintComponent took in public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now public void paintComponent(Graphics g) { super.paintComponent(g); _shape.paint(g); } }

75 74/106 ShapePanel ●We’ve taken care of making sure the shape shows up on the panel ●Now, panel needs to be able to move its shape to a different location when given an int value (x coordinate) ●This method will be called by the slider’s ChangeListener whenever the slider is moved public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now */ public void moveShape(int xPos) { public void paintComponent(Graphics g) { super.paintComponent(g); _shape.paint(g); } }

76 75/106 ShapePanel ●Whenever moveShape is called, first move the shape by calling its setLocation method, which takes in the x and y coordinates of the new location ●Change x-coordinate to value passed in, keep y- coordinate the same ●Next, make sure panel visually updates by calling repaint public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now */ public void moveShape(int xPos) { _shape.setLocation(xPos, _shape.getY()); this.repaint(); public void paintComponent(Graphics g) { super.paintComponent(g); _shape.paint(g); } }

77 76/106 Aside: repaint and paintComponent ●Why do we call repaint and not paintComponent ? ● paintComponent is only the first step in the painting process of a JPanel -- other painting methods take care of painting the border and any Swing components added to the panel ● repaint will call all of these methods in the proper order, to ensure that everything visually updates correctly – see next slide public class ShapePanel extends JPanel { private Shape _shape; /* Constructor elided for now */ public void moveShape(int xPos) { _shape.setLocation(xPos, _shape.getY()); this.repaint(); public void paintComponent(Graphics g) { super.paintComponent(g); _shape.paint(g); } Why not paintComponent ?

78 77/106 Painting Custom Graphics: Summary 2. repaint creates an instance of Graphics (the paintbrush) and calls paintComponent, passing in the paintbrush as an argument 3. paintComponent, which has been partially overridden, calls the paint method on the Shape (s ) we want to display 1. Someone (e.g., moveShape ) calls Swing’s repaint on the JPanel _panel.repaint(); // in JPanel subclass public void paintComponent(Graphics g) { super.paintComponent(g); _shape.paint(g); } // in Shape class public void paint(Graphics g) { // TA code to display shape here } 4. Shape ’s paint uses information about the shape’s size, color, and location to draw shape on screen _panel _shape

79 78/106 Adding a ShapePanel to the MainPanel ●Instantiate a ShapePanel in MainPanel ’s constructor ●Add ShapePanel to MainPanel in CENTER layout position public class MainPanel extends JPanel { public MainPanel() { ShapePanel shapePanel = new ShapePanel(); int sliderMax = Constants.PANEL_W-Constants.SHAPE_W; JSlider slider = new JSlider(0, sliderMax); this.setLayout(new BorderLayout()); this.add(shapePanel, BorderLayout.CENTER); this.add(slider, BorderLayout.SOUTH); } }

80 79/106 Process: MovingShape 1.Write top-level class that contains a JFrame and constants class to keep track of important values 2.Create JPanel subclass to serve as top-level panel, and add an instance to the JFrame 3.Create another JPanel subclass to serve as the shape panel, and add an instance to the top-level panel 4.Write a ChangeListener that tells the shape panel to move the shape, and add an instance to the JSlider MainPanel ShapePanel JSlider MovingShapeApp JFrameMainPanel ShapePanel Shape JSlider ChangeListener

81 80/106 Our ChangeListener : SliderListener ●Our SliderListener ’s job is to tell the ShapePanel to do something whenever its JSlider ’s value changes ●In our case, want ShapePanel to move its shape ●First thing we do is set up an association with a ShapePanel and a JSlider public class SliderListener implements ChangeListener { private ShapePanel _panel; private JSlider _slider; public SliderListener(ShapePanel panel, JSlider slider) { _panel = panel; _slider = slider; } }

82 81/106 Our ChangeListener : SliderListener ● stateChanged method is called whenever slider’s value changes ●Next, call the ShapePanel ’s moveShape method, passing in the JSlider ’s current value as an argument ●Whenever the slider is moved, the ChangeListener calls a method on the ShapePanel, which in turn calls a method on its Shape public class SliderListener implements ChangeListener { private ShapePanel _panel; private JSlider _slider; public SliderListener(ShapePanel panel, JSlider slider) { _panel = panel; _slider = slider; public void stateChanged(ChangeEvent e) { _panel.moveShape(_slider.getValue()); } }

83 82/106 Setting up a SliderListener ●In MainPanel ’s constructor, instantiate a SliderListener, passing in the ShapePanel and the Slider as an argument ●Add the SliderListener to the JSlider public class MainPanel extends JPanel { public MainPanel() { ShapePanel shapePanel = new ShapePanel(); int sliderMax = Constants.PANEL_W-Constants.SHAPE_W; JSlider slider = new JSlider(0, sliderMax); SliderListener listener = new SliderListener(shapePanel, slider); slider.addChangeListener(listener); this.setLayout(new BorderLayout()); this.add(shapePanel, BorderLayout.CENTER); this.add(slider, BorderLayout.SOUTH); } }

84 83/106 Aside: Private Inner Classes ●So far, we’ve operated under the rule that every class we create belongs in its own.java file ●What we didn’t tell you: o Java actually allows you to define a class within another class! o called inner class or nested class ●Often write ActionListener s, ChangeListener s and other listener classes as inner classes, rather than in their own separate files

85 84/106 public class MainPanel { public MainPanel() { // rest of constructor elided... SliderListener listener = new SliderListener(shapePanel); // rest of constructor elided... } private class SliderListener implements ChangeListener { private ShapePanel _panel; private JSlider _slider; public SliderListener(ShapePanel panel, JSlider slider) { _panel = panel; _slider = slider; public void stateChanged(ChangeEvent e) { _panel.moveShape(_slider.getValue()); } inner class outer class

86 85/106 Why Use a Private Inner Class? ●Readability o If a class is only useful to one other class, using a private inner class keeps the code closer to where it is used ●Encapsulation o A private inner class can directly access the outer class’s private members-- stay tuned! o Inner class can be hidden from outside world

87 86/106 Private Inner Classes (Partial Example) ●Here, we demonstrate a private inner class directly accessing a private instance variable of its outer class ●Since _colorPanel is accessible from anywhere within ColorChangingApp, inner class Listener can call methods on it public class ColorChangingApp { private JPanel _colorPanel; public ColorChangingApp() { _colorPanel = new JPanel(); // rest of constructor elided } private class Listener implements ChangeListener { private JSlider _slider; public Listener(JSlider slider) { _slider=slider; } public void stateChanged(ChangeEvent e) { int value = _slider.getValue(); Color c = new Color(value, value, value); _colorPanel.setBackground(c); } } }

88 87/106 Creating Composite Shapes ●What if we want our DrawingPanel to display something more elaborate? ●Can make a composite shape by combining two or more shapes

89 88/106 Spec: MovingAlien ●Transform MovingShape into MovingAlien ●An alien should be displayed on the central panel, and should be moved back and forth by the JSlider

90 89/106 DEMO: MovingAlien

91 90/106 MovingAlien : Design ●We can create a class, Alien, to model our composite shape ●Define composite shape’s capabilities in Alien class ● Alien could have method setLocation that positions each component (face, left eye, right eye) o delegation to the max Alien EllipseShape Ellipse Shapes

92 91/106 Turning MovingShape into MovingAlien 1.Create the Alien class to model our composite shape, making sure it has all the capabilities we need it to have (like setLocation ) 2. Modify ShapePanel to contain an Alien instead of a Shape

93 92/106 Alien ●The Alien class is our composite shape ●It contains three EllipseShape s-- one for the face, and one for each eye ●Constructor (elided) instantiates these ellipses and sets initial sizes/colors public class Alien { private EllipseShape _face; private EllipseShape _leftEye; private EllipseShape _rightEye; // constructor elided- should instantiate the // three ellipses, set their colors/sizes }

94 93/106 Alien ●An Alien ’s location is determined by top left corner of its face’s bounding box ●When setLocation is called, it sets _face to that location, and positions each eye at an appropriate offset public class Alien { private EllipseShape _face; private EllipseShape _leftEye; private EllipseShape _rightEye; // constructor elided- should instantiate the // three ellipses, set their colors/sizes public void setLocation(double x, double y) { _face.setLocation(x, y); _leftEye.setLocation(x + 30, y + 20); _rightEye.setLocation(x + 55, y + 20); } }

95 94/106 Alien ● Alien s need to know how to paint themselves so they can show up on JPanel s ● Alien ’s paint method simply calls paint on each component EllipseShape ●Since our ShapePanel needs Alien ’s current Y coordinate when calling setLocation on it, we define method getY, which returns _face ’s Y coordinate public class Alien { private EllipseShape _face; private EllipseShape _leftEye; private EllipseShape _rightEye; // constructor elided- should instantiate the // three ellipses, set their colors/sizes public void setLocation(double x, double y) { _face.setLocation(x, y); _leftEye.setLocation(x + 30, y + 20); _rightEye.setLocation(x + 55, y + 20); } public void paint(Graphics g) { _face.paint(g); _leftEye.paint(g); _rightEye.paint(g); } public double getY() { return _face.getY(); } }

96 95/106 Turning MovingShape into MovingAlien 1.Create the Alien class to model our composite shape, making sure it has all the capabilities we need it to have (like setLocation ) 2.Modify ShapePanel to contain an Alien instead of a Shape

97 96/106 ShapePanel ●Only have to make a few basic changes to ShapePanel ! ●Instead of knowing about a Shape called _shape, knows about an Alien called _alien ●Since the ShapePanel is the only object that deals directly with the Shape / Alien, don’t need to change any other classes! public class ShapePanel extends JPanel { private Alien _alien; public ShapePanel() { /* set preferred size and color elided */ _alien = new Alien(); int startX = (Constants.PANEL_W-Constants.SHAPE_W)/2; int startY = (Constants.PANEL_H-Constants.SHAPE_H)/2; _alien.setLocation(startX, startY); } public void moveShape(int xPos) { _alien.setLocation(xPos, _alien.getY()); this.repaint(); public void paintComponent(Graphics g) { super.paintComponent(g); _alien.paint(g); }

98 97/106 ●Can easily swap in an Alien for the Shape -- already made sure Alien s can respond to all messages ShapePanel needs to send them ●We implemented setLocation, getY, and paint methods in the Alien class because we knew the ShapePanel would call them public class ShapePanel extends JPanel { private Alien _alien; public ShapePanel() { /* set preferred size and color elided */ _alien = new Alien(); int startX = (Constants.PANEL_W-Constants.SHAPE_W)/2; int startY = (Constants.PANEL_H-Constants.SHAPE_H)/2; _alien.setLocation(startX, startY); } public void moveShape(int xPos) { _alien.setLocation(xPos, _alien.getY()); this.repaint(); public void paintComponent(Graphics g) { super.paintComponent(g); _alien.paint(g); } ShapePanel

99 98/106 Building a Complex GUI ●What if we want to make a more powerful GUI that looks like this? ●Requires a few layers of Swing components and layouts

100 99/106 Graphical Containment ●Graphical containment describes which components visually contain other components ●Not always the same as logical containment

101 100/106 The Big Picture ● JFrame is at the top level ● JPanel s, used with LayoutManager s, arrange graphical components nicely ●Add other components ( JButton s, JSlider s, etc.) to JPanel s ●Can subclass JPanel and override paintComponent to display custom graphics

102 101/106 The Big Picture ●Certain Swing components (like JButton s, JSlider s) and Timer s emit events ●To respond to these events, we use listeners ●A listener always needs to know about the object it’s going to change (for example, a JLabel or a ShapePanel )

103 102/106 Your Next Project: Cartoon! ●You’ll be building a Swing application that displays your own custom “cartoon”, much like the examples in this lecture ●But your cartoon will be animated!

104 103/106 Your Next Project: Cartoon! ●How can we animate our cartoon (e.g. make him move across the screen)? ●As in film and video animation, can create apparent motion with many small changes in position ●If we move fast enough and in small enough increments, we get smooth motion ●Same goes for smoothly changing size, orientation, shape, etc.

105 104/106 Animation in Cartoon ●Use a Timer to create incremental change ●It’ll be up to you to figure out the details… but every time the Timer ticks, your cartoon should move a small amount Tick!

106 105/106 Cartoon Demos

107 106/106 Announcements Cartoon is released today o DQs are due this Sunday (Oct 12) at 2:00 PM o Cartoon help session this Sunday at 6:00 PM, Barus & Holley 166 o Cartoon early hand in is Thursday Oct 16 th (11:59 PM), on time hand in is Saturday Oct 18 th (10:00 PM), late hand in is Monday Oct 20 th (11:59 PM)


Download ppt "0/106 Continuation From Last Time. 1/102 A Third Swing App: ColorPanelApp ●Specification: An app with three sliders that the user can drag to change the."

Similar presentations


Ads by Google