1.09k likes | 1.31k Views
Continuation From L ast T ime. A Third Swing App: ColorPanelApp. JFrame. 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. JSlider. JPanel. DEMO: ColorPanelApp.
E N D
A Third Swing App: ColorPanelApp JFrame • 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 JSlider JPanel
Using JSliders • A JSliderlets the user graphically select a value by sliding an arrow within a bounded interval • One way to construct is by passing two ints (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 JSlideremits ChangeEvents when value changes-- to listen for them, we use a ChangeListener
ChangeListeners ChangeListener ChangeEvent e JSlider ChangeEvent e public void stateChanged( ) { // Respond to ChangeEvent here! // (print something to the console, tell // a JLabel to update its text, etc.) }
Process: ColorPanelApp JFrame • Create top-level class that contains a JFrame • Write a subclass of JPanelthat contains three JSliders, add an instance of it to the JFrame • Write a ChangeListenerthat changes a JPanel’s color based on value of a JSlider • Add ChangeListeners to the JSliders JSlider JPanel
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 packand 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(); } }
Process: ColorPanelApp JFrame • Create top-level class that contains a JFrame • Write a subclass of JPanel that contains three JSliders, add an instance of it to the JFrame • Write a ChangeListenerthat changes a JPanel’s color based on value of a JSlider • Add ChangeListeners to the JSliders JSlider JPanel
JPanelSubclass: 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); } }
JPanelSubclass: ColorPanel public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlidersliderRed = new JSlider(0, 255); JSlidersliderGreen = new JSlider(0, 255); JSlidersliderBlue = new JSlider(0, 255); this.add(sliderRed); this.add(sliderGreen); this.add(sliderBlue); } } • Instantiate three JSliders: one to control each channel (red, green, and blue) • Arguments are beginning/end of slider’s range: we give each range 0-255 (one byte/RGB channel) • Finally, add each slider to panel
Adding a ColorPanelAppto theJFrame 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(); } } • Just as we did in previous examples, first instantiate a panel, then call addto add it to the JFrame
Process: ColorPanelApp JFrame • Create top-level class that contains a JFrame • Write a subclass of JPanel that contains three JSliders, add an instance of it to the JFrame • Write a ChangeListener that changes a JPanel’s color based on value of a JSlider • Add ChangeListeners to the JSliders JSlider JPanel
Changing a JPanel’s Color • Every JPanelhas a pair of accessor and mutator methods, setBackgroundand getBackground • We have three JSliders-- one to control each color channel of the Jpanel.EachJslider has its own listener • When red slider’s listener detects that its slider has been moved: • it calls getBackgroundto ask the panel for its current color • 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) • it calls setBackground, passing in the new Color
Our ChangeListener: SliderListener • A SliderListener’s job is to change the color of a JPanelbased on the value of a JSlider • Since JSliders emit ChangeEvents whenever their value changes, SliderListenerimplements 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; } @Override public void stateChanged(ChangeEvent e) { // implementation elided for now } }
Our ChangeListener: SliderListener • To do its job, it needs to know three things: • The JPanelwhose color it should change • Which channel (red, green, or blue) to modify • 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; } @Override public void stateChanged(ChangeEvent e) { // implementation elided for now } }
The stateChangedmethod // rest of class SliderListenerelided for now @Override 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); } } • Method is called whenever value of the listener’s JSliderchanges • 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
The stateChangedmethod // rest of class SliderListenerelided for now @Override 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); } } • Need to determine new value of JSlider • Since we know the source is the JSlider, we can retrieve value by calling getValueon _slider
The stateChangedmethod // rest of class SliderListenerelided for now @Override 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); } } • 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
The stateChangedmethod // rest of class SliderListenerelided for now @Override 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); } } • Depending on the value of _channel, we set newBgto a different color • In each case, only the specified color channel changes; we use the gets for the values of the unchanged channels • Once we’ve changed the appropriate channel, we set the JPanel’s background color to newBg
Process: ColorPanelApp JFrame • Create top-level class that contains a JFrame • Write a subclass of JPanel that contains three JSliders, add an instance of it to the JFrame • Write a ChangeListener that changes a JPanel’s color based on value of a JSlider • Add ChangeListeners to the JSliders JSlider JPanel
Setting up our SliderListeners public class ColorPanel extends JPanel { public ColorPanel() { Dimension panelSize = new Dimension(300, 150); this.setPreferredSize(panelSize); this.setBackground(Color.GRAY); JSlidersliderRed = new JSlider(0, 255); JSlidersliderGreen = new JSlider(0, 255); JSlidersliderBlue = 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); } } • Back in ColorPanel class • Call method addChangeListeneron each slider, passing in a new instance of SliderListener • Pass in ColorPanel, appropriate channel number and slider as arguments to each SliderListener
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); JSlidersliderRed = new JSlider(0, 255); JSlidersliderGreen = new JSlider(0, 255); JSlidersliderBlue = 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(); } }
The Whole App (2/2) @Override 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);} } public class SliderListener implements ChangeListener { private JPanel _panel; private int _channel; public SliderListener(JPanel panel, int channel){ _panel = panel; _channel = channel; }
Putting It All Together: ColorPanelApp ColorPanelApp JFrame ColorPanel JSlider SliderListener
Buttons in Swing Some buttons in Swing: • javax.swing.JButton • javax.swing.JToggleButton • javax.swing.JCheckBox • javax.swing.JRadioButton
ButtonGroups • How do we make options mutually exclusive (i.e. you can only click one button at a time)? • put them in a button group • javax.swing.ButtonGroup • Buttons that can be added to ButtonGroup: JRadioButton JToggleButton JMenuItem (for advanced users)
JRadioButtons & ButtonGroups • How to use a ButtonGroup: • create buttons JRadioButton rB1 = new JRadioButton(“Green, not Red”); JRadioButton rB2 = new JRadioButton(“Red, not Green”); • create ButtonGroup ButtonGroup bGroup = new ButtonGroup(); • add buttons to ButtonGroup bGroup.add(rB1); bGroup.add(rB2);
Modified ColorTextApp: RadioColorListener public class RadioColorListener implements ActionListener { private JLabel _label; private Color _currentColor public RadioColorListener(JLabel label, Color color) { _label = label; _currentColor = color; } @Override public void actionPerformed(ActionEvent e) { _label.setForeground(_currentColor); } }
Modified ColorTextApp: ColorTextPanel Assume all the swing components are imported! 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); } } For more complex projects you might want to give the Buttons a separate JPanel and use LayoutManagers (stay tuned!)
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 LayoutManagers to arrange components nicely within JPanels
Using java.awt.LayoutManagers • Each JPanelcontains a LayoutManagerto automatically arrange its sub-components • LayoutManagerinterface implemented by classes: • java.awt.FlowLayout • java.awt.GridLayout • java.awt.BorderLayout • To specify the LayoutManagerfor a JPanel, either pass an instance of one of these classes to the panel’s constructor or to its setLayoutmethod later
FlowLayout • Default layout used by all JPanels • Arranges components from left to right, top to bottom • When it reaches the edge of the panel, moves to next row
FlowLayout 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”); JPanelmainPanel = new JPanel(); FlowLayoutfl= 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); } }
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
GridLayout 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); } }
BorderLayout • Splits JPanelinto 5 regions: NORTH, SOUTH, EAST, WEST, CENTER • These are all static constants • Must specify region when adding component to its container
BorderLayout • Don’t have to fill all regions-- default size of a region is (0, 0) • BorderLayoutwill adjust dynamically depending on what has been added • Typically add to CENTERfirst-- it should be largest
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); } }
LayoutManagers • You’ll be using layouts a lot in CS15-- practice playing around with them! • Try taking the previous example and calling setPreferredSizeon 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:
Swing: Summary • Swing has a variety of tools to help you make GUIs: • JFrames, JPanels, JButtons, and JSliders, arranged using LayoutManagers • ActionListeners and ChangeListeners to respond to user input • 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!
Lecture 10 Build Your Own Custom Graphics
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 Shapepackage and work them into Swing applications
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: • e.g., set and get their current size and graphical position
The Problem • We need way more functionality than built-in RectangularShapes provide! • Want all shapes to be able to: • Set and get current color • Have a border of user-specified width and color • Rotate • Know how to paint themselves on the screen!
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 subclassesto create beautiful graphics for the rest of the semester
Shape Package • To use the TAs’ Shapepackage, import it using • import cs015.prj.Shape.*; • Make your own subclass by extending the Shapeclass in the Shape package and passing a java.awt.geom.RectuangularShapeinto super’s constructor.
Shape Class Hierarchy Shape • Shape package also contains specific shapes: e.g., RectangleShape (don’t confuse with RectangularShape) and EllipseShape • Subclasses of abstract Shape class • All Shapeskeep track of a basic set of properties for which they have accessors and mutators RectangleShape EllipseShape
Accessors and mutators of Shapes Location public double getX(); public double getY(); public void setLocation(double x, double y); Border Size public int getBorderWidth(); public void setBorderWidth(int width); public double getWidth(); public double getHeight(); public void setSize(double width, double height); Visibility Color public boolean getVisible(); public void setVisible(boolean visible); 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); Rotation public double getRotation(); public void setRotation(double degrees);