|
~ Java for Web Page Production ~ Session 8 - Building Menus; Planning a Project |
Now that we have covered the principles of animation, and how to move graphical images around the screen, we are just about ready to tackle a larger-scale project.
We will be spending the next two sessions designing and developing a game based on the classic Breakout game - i.e. batting a ball against a brick wall until all of the bricks have been knocked out.
This is a relatively straightforward example, and utilises nearly all of the topics that we have covered in the course so far. So it may be a good idea to refresh your memory on some of the topics in order to get the most out of these two sessions.
We will, however, start by looking at a brief topic that we omitted earlier - you will not be using it in many of your web-based projects, which is why we cover it only briefly. That is namely, setting up menus, and triggering off actions from selecting a menu item...
In Session 7, we covered:-
Using Pictures - Loading in image files, and displaying them
Directory Locations - Recalling images from the HTML document's directory, or the Java bytecode directory
Playing Sound and Music - Loading sound clips and music files, and playing them as one-offs, or in continuous loops
Timers - How to call the actionPerformed method every so many milliseconds
Animation - Some principles of animation, including the tradeoff between steps and ticks
Scrolling Banner Example - Applying animation to text to create a scrolling 'ticker-tape' style banner
Double-Buffering - Drawing a picture off-screen to reduce flicker
In session 8, we will be looking at:-
Introduction
Summary from Session 7
Session 8 overview
Creating Menus
Menu Example - Pairs Game
<<break>>
Breakout game - specification
Breakout game - design objects
Breakout game - The Playing Area
Breakout game - The Ball
Breakout game - The Bat
Summary and Further Sources
Menus can be implemented within a Java applet in quite a straightforward fashion. The menu will automatically appear at the top of the JApplet container, and any layout will be fit in underneath this.
Painting with Swing components
Note that there is a problem when trying to implement Menus with animation - namely, if you drop down a menu, then it is possible that the canvas could be written over by a subsequent call the the Paint() method from a Timer tick's actionPerformed() method. It's a bit messy.
There are a few ways around this. One is to suspend any ticks / paints of the canvas whilst the menu is in use. The other, which we will look at here, is to create ourselves a new class, extending the standard JPanel class. We can then use its canvas to draw on. The difference to note is that because the JPanel class is a Swing component, and its painting mechanism falls within the remit of the Swing drawing architecture, we don't have to worry about refreshing the contents of the canvas. For that matter, we don't need to draw over the image to clear the previous image either.
One other useful side-effect of using a Swing component is that the painting mechanism is double-buffered, so we already get benefits when animating graphics.
Take a look at the anim_menu.qjp QuickCup project in the week08 directory.
Click on the AnimateMenu.java tab. The Applet no longer contains the actionPerformed() method for the Timer, although it does still contain the actual Timer object. If you look on line 43, you can see that a new class called MyPanel is being used, and we are creating an object called myPanel to utilise this new class:-
MyPanel myPanel;
If you take a look at line 86, you can see that this is where the myPanel object is created. The constructor needs some information to create the object - namely, the initial step size for the animation, and the two images (green ball and grey square) to be used in the animation:-
| myPanel = new MyPanel(step,getImage(getCodeBase(),"green_ball.gif"), getImage(getCodeBase(),"grey_square.gif")); |
Line 88 add the myPanel object to the Applet's container in the centre - remember that for a JApplet, the default Layout type is the BorderLayout:-
getContentPane().add(myPanel,BorderLayout.CENTER);
Line 90 starts the timer with the speed passed from the HTML page, but this time rather then passing the this object, the myPanel object is passed to determine where the actionPerformed() method can be found for each tick:-
timer = new Timer(speed, myPanel);
So what is the MyPanel class? Look on line 112, and you can see the declaration of the MyPanel class. Its structure (without source code) is as follows:-
| class MyPanel extends JPanel implements ActionListener { public int xPos=6,step,origStep; Image greenBall,greySquare; public MyPanel(int step, Image image1, Image image2) {} public void actionPerformed(ActionEvent e) {} public void incStep() {} public void decStep() {} public void resetStep() {} public void swapDirection() {} public void debugStep(String msg) {} public void paintComponent (Graphics g) {} } |
You can see that the MyPanel class subclasses (i.e. extend, or is a descendant of) the JPanel class - it's a JPanel plus some! It also defines the ActionListener interface, which means that it has an actionPerformed method. It is this method that is called with each Timer tick.
The class stores its own values for xPos (the horizontal position of the graphic), step (the distance in pixels the graphic travels with each tick), origStep (used by the resetStep() method to put the value of step back to what it was when the object was first created using the MyPanel constructor), and greenBall/greySquare images. These are all set up through the MyPanel constructor when the object is first created.
A Swing component (which this now is, being inherited from the JPanel class) knows when it needs to be repainted - e.g. after a menu, which partially obscured the canvas, has been closed. It also knows when not to write over something - for example, the menu will always appear on top of the panel - it never gets obscured.
Instead of using the paint() method which the JApplet uses to paint graphics onto its canvas, components such as our MyPanel use the paintComponent() method as follows (see line 147 onwards):-
| public void paintComponent (Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; ... } |
Notice that the first instruction in the method must be the command:-
super.paintComponent(g);
This calls the paintComponent() method from the parent class (i.e. from JPanel) to ensure that the background gets painted first - so there's not problem with the animation leaving a trail. Note that the super keyword means 'the parent class'.
The incStep, decStep, resetStep and swapDirection methods allow any program that uses this class to act indirectly on MyPanel's attributes - e.g. by increasing the value of step, decreasing it, resetting it to its original value, or reversing its signage (i.e. making it a negative value to reverse the direction of animation).
Adding a menu bar
In order to add a menu bar, you will need to declare a JMenuBar variable in your class (see line 40):-
JMenuBar menuBar;
You will then need to create a new MenuBar object - you normally do this from the init() method (see line 47):-
menuBar = new JMenuBar();
And finally, you will need to add that menu bar to your JApplet as follows:-
setJMenuBar(menuBar);
This defines a grey rectangle at the top of the screen in which to hold any menus that you wish to set up.
Adding a menu
A menu is a word that appear on the menu bar. When you click on that word, a number of items (menu items) drop down, which are related to the menu - examples of common menus are: File, Edit, View, Insert, and Help.
In our example, two menus are created - fileMenu and directionMenu, labelled File and Direction correspondingly (see line 41):-
JMenu fileMenu,directionMenu;
To create the new menu, with a word to display on the menu bar (see line 50):-
fileMenu = new JMenu("Speed");
In this example, a new menu with the caption Speed is created. We can specify which letter in the word is a mnemonic - i.e. press Alt and that letter on the keyboard to obtain the menu:-
fileMenu.setMnemonic(KeyEvent.VK_S);
See the next section entitled KeyCodes to get a list of keys that can be used - In this example, VK_S refers to Virtual Key S - i.e. the S key on the keyboard. When displayed, the menu appears with a little underline underneath the S of Speed, and pressing Alt+S highlights and drops down the Speed menu.
The menu needs to be added to the menu bar in order for it to be available (line 73):-
menuBar.add(fileMenu);
As an exercise, try adding a new JMenu labelled Help called menuHelp, and assign it with a mnemonic key of VK_H for the H key.
Menu Items
In order to add a menu item to the bottom of a menu, you need to set up a JMenuItem - several are set up on line 42 as follows:-
JMenuItem speedUp,slowDown,resetSpeed,changeDir;
To create a menu item, having declare it as above, you would do the following (see line 50):-
speedUp = new JMenuItem("...More",KeyEvent.VK_M);
In this example, a new menu item is created labelled ...More and created in the speedUp variable. Notice, that one of the constructor parameters is a Mnemonic - in this example, the M in More will be underlined.
To add the menu item to the bottom of a menu (see line 55), you would do the following:-
fileMenu.add(speedUp);
This adds the speedUp menu item to the fileMenu menu (i.e. ...More appears under the Speed menu).
Another command to take note of is the setAccelerator command. This is an alternative key to press in order to perform whatever action the menu takes (see line 52):-
speedUp.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0));
In this example, pressing the upwards arrow key will do the same as selecting the Speed menu and clicking on the ...More option.
If you wish to combine pressing the Ctrl key with the specified key, replace the 0 with ActionEvent.CTRL_MASK - for the Alt key, use ALT_MASK and for the shift key use SHIFT_MASK. To use a combination of these, add them together - e.g. Ctrl+Alt would be ActionEvent.CTRL_MASK+ActionEvent.ALT_MASK
Adding a menu item separator
If you wish to place a grey line at the bottom of a menu in order to separate off a group of menu items for easy visibility, use the following command (see line 63):-
fileMenu.addSeparator();
Perform an action when menu clicked
A menu item isn't much help if, when you click on it, it doesn't do anything. In order to do something, we need to add a link to an ActionListener. The easiest way to do this is as follows (see line 52 for an example):-
| speedUp.addActionListener (new ActionListener() { public void actionPerformed(ActionEvent e) { myPanel.incStep(); } }); |
So, the speedUp menu item (with the label ...More) has a new ActionListener added. This is defined within the code using an Anonymous Inner Class which defines the ActionListener interface by declaring the actionPerformed() method. So the instructions in this method is the action performed when the menu item is clicked on:-
myPanel.incStep();
Thus, if you click on Speed and then ...More, the incStep() method of the new panel which displays the animation is called, to increase the value of its step value, and thus increase the number of pixels that the animation moves by, with each tick of the Timer
When you test this applet, notice that accelerators are defined to increase (up key) and decrease (down key) the speed. The direction can be changed using the accelerator Ctrl+S. Notice that these accelerators are displayed in the menu.
As an exercise, try commenting out (using //) the code in the incStep() method of the MyPanel class, and replace it with a single line:-
step++;
Do the same for the decStep() method, and replace it with:-
step--;
Notice what happens if you reverse the direction. The up and down keys seem to reverse their meanings. Also notice what happens if you get too slow - you eventually reverse direction, and again the keys seem to reverse their meanings.
Debugging technique - using the command window
Also note that in the black command output screen, each time the value of step changes, a line of text is displayed to say what the new value is. This is performed using the System.out.println method, and is a very useful way of debugging your programs, if you are unsure why a program is not behaving as it should. By placing this into another method (called debugStep), this can be used from anywhere within the class, and should you wish to change how the debugging occurs (e.g. send results to a file), you only need to change the debugStep method.
Try changing the debugStep method to show the value of xPos. Run the program to see what happens.
Keycodes
Key codes are static values defined in the KeyEvent class. Each named code is representative of a particular key being pressed on the keyboard. The available list is as follows:-
VK_ENTER, VK_BACK_SPACE, VK_TAB, VK_CANCEL, VK_CLEAR, VK_SHIFT, VK_CONTROL, VK_ALT, VK_PAUSE, VK_CAPS_LOCK, VK_ESCAPE, VK_SPACE, VK_PAGE_UP, VK_PAGE_DOWN, VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_COMMA, VK_MINUS, VK_PERIOD, VK_SLASH, VK_0, VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39), VK_1, VK_2, VK_3, VK_4, VK_5, VK_6, VK_7, VK_8, VK_9, VK_SEMICOLON, VK_EQUALS, VK_A, VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A), VK_B, VK_C, VK_D, VK_E, VK_F, VK_G, VK_H, VK_I, VK_J, VK_K, VK_L, VK_M, VK_N, VK_O, VK_P, VK_Q, VK_R, VK_S, VK_T, VK_U, VK_V, VK_W, VK_X, VK_Y, VK_Z, VK_OPEN_BRACKET, VK_BACK_SLASH, VK_CLOSE_BRACKET, VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_MULTIPLY, VK_ADD, VK_SEPARATER, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE, VK_DELETE, VK_NUM_LOCK, VK_SCROLL_LOCK, VK_F1, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10, VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, VK_PRINTSCREEN, VK_INSERT, VK_HELP, VK_META, VK_BACK_QUOTE, VK_QUOTE, VK_KP_UP, VK_KP_DOWN, VK_KP_LEFT, VK_KP_RIGHT, VK_DEAD_GRAVE, VK_DEAD_ACUTE, VK_DEAD_CIRCUMFLEX, VK_DEAD_TILDE, VK_DEAD_MACRON, VK_DEAD_BREVE, VK_DEAD_ABOVEDOT, VK_DEAD_DIAERESIS, VK_DEAD_ABOVERING, VK_DEAD_DOUBLEACUTE, VK_DEAD_CARON, VK_DEAD_CEDILLA, VK_DEAD_OGONEK, VK_DEAD_IOTA, VK_DEAD_VOICED_SOUND, VK_DEAD_SEMIVOICED_SOUND, VK_AMPERSAND, VK_ASTERISK, VK_QUOTEDBL, VK_LESS, VK_GREATER, VK_BRACELEFT, VK_BRACERIGHT, VK_AT, VK_COLON, VK_CIRCUMFLEX, VK_DOLLAR, VK_EURO_SIGN, VK_EXCLAMATION_MARK, VK_INVERTED_EXCLAMATION_MARK, VK_LEFT_PARENTHESIS, VK_NUMBER_SIGN, VK_PLUS, VK_RIGHT_PARENTHESIS, VK_UNDERSCORE, VK_FINAL, VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE, VK_KANA, VK_KANJI, VK_ALPHANUMERIC, VK_KATAKANA, VK_HIRAGANA, VK_FULL_WIDTH, VK_HALF_WIDTH, VK_ROMAN_CHARACTERS, VK_ALL_CANDIDATES, VK_PREVIOUS_CANDIDATE, VK_CODE_INPUT, VK_JAPANESE_KATAKANA, VK_JAPANESE_HIRAGANA, VK_JAPANESE_ROMAN, VK_KANA_LOCK, VK_INPUT_METHOD_ON_OFF, VK_CUT, VK_COPY, VK_PASTE, VK_UNDO, VK_AGAIN, VK_FIND, VK_PROPS, VK_STOP, VK_COMPOSE, VK_ALT_GRAPH, VK_UNDEFINED
xxx
Breakout Game - Design Objects
xxx
xxx
xxx
xxx
| Complete (rather technical) course on Java from Bruce Eckel. Well-recommended Java seminar CDs also available for a very reasonable price. Also courses on C++ available. | http://www.mindview.net |