CS51 - Spring 2010 - Lecture 10
- CS lunch tomorrow in Frary North
- TP1 posted
- take-home programming exam
- Due Friday at 6pm, Oct. 15 (notice that we don't have a lab next week)
- If there are confusions about specifications come talk to me, but I can't help with the coding (nor can the TAs)
- Recall your overall grade for the course:
- 15% TP1
- 15% midterm
- 3 problems
- 16 points for functionality and 16 points for style
- must do at least 4 points of extra credit to get 100%
- may do up to 8 points of extra credit, i.e maximum possible is 104
- Graphical User Interface
- things like buttons, drop down menus, pull down menus, text fields, scroll bars
- Three basic groups of things for doing GUIs in Java:
- GUI components: these are the actual things (like buttons, text fields, sliders, etc.) that we want to put in our program
- Containers: containers actually house the GUI components. Unlike the other objects we've seen so far, GUI components are NOT displayed when they're created, but need to be added to a container.
- Layout managers: Unlike adding things to DrawingCanvas, we don't add GUI objects with positional information. Instead, all containers have a layout manager associated with them that describe how the GUI components that are added to the container will be positioned.
- Why do you think we do this instead of using exact positioning?
- WindowController is a Container and so if you extend WindowController, then you can add GUI components to it
- The layout manager for WindowController is called BorderLayout
- you can add components to the North, South, East, West and Center
- when you add a component, it fills up the entire space
- For WindowController, the canvas object sits in the Center, so in general, don't add anything there
- extend Controller if you don't need the canvas and want to add components to the center
- We only have one GUI component: JComboBox
- What will this class look like?
- construct the JComboBox
- add the selection items to the box
- add it to the South
- setup the filled rects
- setup a framed rect
- if we clicked inside the "drawing" area
- create the appropriate object
- need to figure out what the settings are from the JComboBox
- if we clicked in any of the colors
- set the color of the last created object
- onMousePress, onMouseRelease, onMouseDrag
- handle the dragging of the current object
- why do you think we can only drag the last created object?
- import java.awt.*;
- import java.swing.*;
- For all of the demo programs, you should ask yourself, why do we need that variable as an instance variable?
- private JComboBox drawingArea
- why do we need an instance variable?
- we're going to need to get attributes from the box when the user clicks
- private DrawableInterface newShape
- why is it DrawableInterface?
- so we can add either FramedRect, FilledRect or FilledOval
- To add a GUI object, there are three steps
1. create the gui component and do any initialization (in this case, adding the menu items)
2. Add it to the Container for the WindowController (Controller)
- Container contentPane = getContentPane(); // gets the Container associated with our WindowController
3. when you're all done adding GUI components, validate the content pane
- validate tells the container you've changed what's inside it and that it should redo its layout
- remember, since we don't do exact positioning, the container has to do some work to position things depending on what is inside the container, its size, etc.
- it does not hurt to call validate, but...
- things may NOT behave appropriately if you don't do this
- BorderLayout.SOUTH adds to the south (similarly, BorderLayout.NORTH, ..., can be used)
- We get the choice of which item is selected:
- Object choice = figureMenu.getSelectedItem();
- it's just a generic "Object" (we'll talk a bit more about the Object class later)
- check to see which of the choices it is using .equals
- for the colors, we're just using FramedRects and checking if our mouse click is in there
- What's changed?
- We've added another JComboBox for color selection
- Where did we add it?
- Notice that the the color of the last created object changes with respect to changes in the menu
- how do you think this happens?
- similar to the KeyListener interface, there is an ActionListener interface
Responding to changes in GUI components: the ActionListener interface
1. implements ActionListener
- make sure to import java.awt.event.*;
2. only one method to implement for the interface
public void actionPerformed(ActionEvent event)
3. Once you've done that, your class is now ready to respond to Action events, but you need to tell it which events it should listen to. To do this, after you've created your JComboBox (say it's named colorMenu) you need to tell our current object to listen for events generated from the JComboBox
- now, everytime a change is made to the color menu, our actionPerformed method will be called
- construct the JComboBox for the color menu
- colorMenu.addActionListener(this) //DON'T FORGET THIS
- add the color menu to BorderLayout.NORTH
- implements ActionListener
- just like mouse and keyboard listening, every time the JCombo box changes value, the actionPerfomed method is called
- Why the check for null?
- if we haven't created any objects yet, newShape will be null
- similar to before, we get the Object associated with the menu:
Object colorChoiceString = colorMenu.getSelectedItem();
- then use .equals to see which option was selected
- We said that when we can only add one thing to a BorderLayout area. If we need to add two things to an area, how do you think we do it?
- We can put containers inside other containers: JPanels!
- A JPanel is another Container that can hold GUI objects
- by default, a new JPanel uses a FlowLayout
- in a FlowLayout, objects are just put next to eachother starting from the left to right
- the "add" method is used to add GUI components to the JPanel
- then add the JPanel to whatever part of the main content pane that you want
- create our JComboBoxes as before
- create a new JPanel
- add the figure and color menus to the JPanel
- add the JPanel to the content pane at BorderLayout.SOUTH
- everything else stays the same. We just moved the around where the different GUI components were within the window
- There is a JButton object that creates a usable button
- Instead of using a dropdown menu and clicking in the canvas, change it so that when we click a button, the object is created
- avoids accidentally creating a new object when we're trying to drag an object
- How do you think the GUI setup was constructed here? Why does it look funny?
- the FlowLayout doesn't wrap around
- splits the panel into a row by col grid
- each component takes up the same amount of space, i.e. the grid perfectly splits up the space
- elements are added in reading order, that is from left to right and then top to bottom
- when creating, specify rows then columns, for example 3, 2 is 3 rows and 2 columns
- first item added goes in upper left, then upper right, then middle left, then middle right, etc
- how do we create this?
- one large JPanel with a one row and two columns
- one JPanel (with the default FlowLayout) where we add the buttons
- add that JPanel to the large JPanel, so it will end up in the first position
- add the JComboBox to the large JPanel, so it will end up in the second, bottom position
- finally, add the large JPanel to the SOUTH of our content pane (and of course, validate :)
- how do you think we handle the button clicking?
- remember the things we need to do to handle GUI events
- implements ActionListener
- add the actionPerformed method
- add "this" object as a listener using addActionListener to the object(s) that we want to respond to
- need to addActionListener for all three of the buttons!
- what complication does this add?
- in actionPerformed, need to figure out which of the buttons caused actionPerformed to be called
- begin method looks like we predicted
- create new JButtons
- addActionListener(this) for all three
- notice that we're also listening to our colorMenu
- bottomPanel.setLayout(new GridLayout(2, 1));
- need to handle 4 different things (the three buttons, the one drop down menu)
- evt.getSource() gives us the object that generated the event
- why can we use '=='?
- we literally want to know if it was exactly that button that generated the event
- what do you think the big box at the top that changes color is?
- it's just a FilledRect
- how are we changing the color?
- we can adjust the sliders and in response to that, our program updates the color
- Another interface ChangeListener
- one method: public void stateChanged(ChangeEvent evt)
- similar to our other listeners, we have an: addChangeListener(this) method
- this pattern should start to look familiar:
- implements ChangeListener
- add the stateChange method
- tell the program that "this" will listen/respond to any changes made by the object (in this case, the sliders)
- why do we have two different interfaces ActionListener and ChangeListener
- action events happen much less frequently, i.e. based on a click
- change events are much more frequent and continuous, i.e. based on a dragging of the mouse
- we wouldn't want the overhead of all the action event code in the stateChanged method
- it's only the slider bar (not the text)
- JLabel for adding text (similar to our "Text" object, but it's a GUI component)
- let's look at the layout, how can we design this?
- two different ways:
- one large JPanel with a 3, 1 GridLayout (3 rows, 1 column)
- for each slider we have a JPanel
- 1, 3 GridLayout (1 row, 3 columns)
- one large JPanel with a 3, 3 GridLayout (3 rows, 3 colummns)
- don't need individual JPanels for each set of slider components
- just add them all to the large JPanel individually
- what information do you think we need to construct a new JSlider (i.e. what's going to go in the constructor)?
- need to know whether it's a vertical or horizonal slider
- range, specified by a minimum value and a maximum value
- initial, starting value
- to handle the changing sliders:
- implements ChangeListener
- stateChanged method: going to be called everytime any of the sliders changes values
- addChangeListener(this) for the three sliders
- what do we want to do when a slider changes value?
- get the value from each of the sliders
- getValue() method returns the value
- set the color of our FilledRect to be the new color indicated by the sliders
- update the text for all of the sliders
- setText(...) (similar to setText for Text)
- why do we do "" + redValue? what does this accomplish?
- we could try and figure out which of the sliders changed using evt.getSource(), but it would just make the code more complicated