The three classes that that allow us to instantiate a Balloon object from the information provided by the user contain the following two methods:
/** * Request the values for the fields of a new <code>Balloon</code> * instance from the BalloonInputView. Report errors and re-try. * Throw <code>CancelledException</code> if no data is given. */ public Balloon requestBalloon() throws CancelledException; /** * Demand the values for the fields of a new <code>Balloon</code> * instance from the BalloonInputView. * Report errors and re-try till successful. */ public Balloon demandBalloon();
We could define an interface BalloonInput to make it clear to the programmer that works with our code that these three classes behave in a uniform manner.
class ConsoleBalloonInput implements the
BalloonInput
interface used for reading
the input from the console.
class BalloonInputView defines a GUI to request the user input for the data needed to initialize one Balloon instance. It contains two TextFieldViews, one SliderView, and one ColorView. It also allows us to display the data that represents an instance of a Balloon.
class GUIBalloonInput implements the BalloonInput interface for extracting the user input from the BalloonInputView GUI.
class BalloonControl adds to the GUI Actions. These are buttons that allow the user to choose an action, such as read the Balloon data from a GUI and display the Balloon in the given canvas. (Our canvas is a window – a buffered panel.)
Build a Configuration with main method in the Interactions class and run the code. Note the behavior in response to the various buttons.
The model
Read the code for the class Balloon. Add the method
eraseBalloon which will paint the balloon in a white
color
(Color.white). Make sure you have the examples and tests for
this method.
The console input
Read the code for the method runConsoleInput in the
class Interactions. Describe to your partner what the
method
does. Look at the
ConsoleBalloonInput class and see how the methods
demandBalloon and requestBalloon are implemented.
Run the code and see what happens if you type in a wrong data, or
when you do not provide any input.
The actions
Find the
code for the action for the New button in the class
BalloonControl. Currently, it only reads the data from
the bView, constructs a new Balloon with
the fields given by the GUI and makes it the new value of the
Balloon b.
Add to this action a call to the method which paints the balloon, from the class Balloon. Make sure it works.
Text input from a GUI
Find all places where the xTFV is defined or used. It is constructed in the class BalloonInputView. This class also defines the methods demandBalloon and requestBalloon, each of them produces a new instance of a Balloon from the user inputs.
In the class BalloonControl user input to the BalloonView initializes the value of a Balloon object that represents our model.
Extend the model in the class BalloonControl with one more balloon. Define a new actions b2Action that initializes and paints the second balloon.
Connecting slider with a text field
Look at the class BalloonInputView.
Look how the two TextFieldViews that represent the x and y inputs are defined.
Define a new TextFieldView named rTFV, to represent the numerical value of the Balloon radius. Give it the default value 20.
Test the behavior of the slider. Does it have any effect on the balloon? Does it have any effect on the value displayed in the rTFV field? Change the value of the rTFV field. Does it affect the slider? Does it affect the balloon?
The two views represent the same value and so should be designed to mimic each other. The slider has to act by changing its position whenever a new value is typed into the text field. The value in the text field has to change when the slider is moved, so it reflects its current position.
In the class BalloonInputView define two new SimpleActions and the corresponding methods — an rTFVaction and a SliderAction. It does not matter what you choose for the label, because we are not going to use the actions with a button.
The first one void rTFVaction will be invoked when the value in the field rTFV changes. It should set the value of rSlider to the value displayed in the rTFV. To set the state of the rSlider use the method
rSlider.setViewState("" + rTFV.getViewState());
The second method void rSliderAction() will be invoked every
time the location of the slider (and the value it represents)
changes. It must then
set the view state of the rTFV calling the method
setViewState in a manner similar to the above. If you run the
program now, you may be surprised to see that these changes have no
effect. Can you think of the way to test that the methods work
correctly?
Listening to changes in the values
Now you have to tell the rSlider and the rTFV to perform this action when their values change. The following two statements have to be added at the end of the method void createViews():
rTFV.addActionListener(rTFVaction); rSlider.addSlidingAction(sliderAction);
The first one tells the rTFV to perform the
rTFVaction whenever its value changes. The second one tells
the rSlider to perform the
sliderAction whenever the
position of the slider (and thus the value it represents) changes.
Test that this works. When entering an new value in the rTFV you need to hit return before the new value registers and affects the slider.
Reporting changes in the model to the view
Now that you have seen the method setViewState, add
such
method to the class BalloonInputView. Here the
method setViewState should take as input an instance of the
Balloon and set the GUI display values to the values
of the fields of the given Balloon. To see that is
works, we need to modify some of the fields of a Balloon
instance and invoke the method. Try it.
Adding mouse actions
In the last part you will control the balloon with the mouse. You need to define what should happen when the mouse is clicked (or dragged, or released, etc.). You need to specify which GUI component should listen to the mouse and the user mouse actions. You then need to connect the MouseListener with the action it should trigger.
Build a separate frame
The first thing you need to do is to change the manner in
which the GUI is displayed. Look at the code in the class
Interactions for the method runBalloonControl(). Copy
the method and rename it runBalloonControlMouse(). Replace
the line which calls the method
showOKDialog with the
following:
JPTFrame.createQuickJPTFrame("Balloon Control", bc);
This places the BalloonControl GUI into a window that runs in its own thread, i.e. independently of the rest of the application. That allows the rest of the application to watch out for the mouse movement and clicks inside of the graphics window.
Run the program and you will see that while the BalloonControl GUI is open, you can use all the other buttons in the GUI built by the Interactions application.
Define a mouse action
Start by adding the following import statement at the top of the
BalloonControl.java file:
import java.awt.event.*;
The first mouse action you will build will increase the radius
of the balloon by ten, every time you click the mouse. All of this
is in the class BalloonControl. Start by defining the
method
protected void click(MouseEvent mevt) which does the
following:
Print into the console a message that the mouse was clicked.
Erase the balloon
Increase the balloon radius by 10
Set the view state of the BalloonInputView bView to the current values of the balloon. (Only the radius has changed, but it is easier to let the BalloonView do the whole job by invoking the method setViewState.
Finally, paint the changed balloon.
Defining and installing Mouse action adapter
Install a MouseActionAdapter for the BufferedPanelas follows:
After the definition of the BufferedPanel, add the definition:
public MouseActionAdapter mouseAdapter;
Inside of the constructor for the class BalloonControl first initialize the mouseAdapter as follows:
mouseAdapter = window.getMouseActionAdapter();
Add the action to perform when the mouse is clicked as follows:
// respond to mouse clicks mouseAdapter.addMouseClickedAction( new MouseAction() { public void mouseActionPerformed(MouseEvent mevt){ click(mevt); } });
At this point you should test that your program runs as you expected.
Tracking the mouse movement
Finally, you will make the balloon move when the mouse moves. Do all the steps you have done for the clicked action, but do not get a new mouseAdapter. The following code will add the action:
// track mouse motions mouseAdapter.addMouseMovedAction( new MouseAction() { public void mouseActionPerformed(MouseEvent mevt){ track(mevt); } });
Inside of the track method get the coordinates of the mouse as follows:
b.x = mevt.getX(); b.y = mevt.getY();
and see what your program does. (Probably nothing - you still have to erase the old balloon, before you make the changes, paint the new balloon, and as a courtesy, set the view state for the view.) Now you should have fun.