![]() | Monkeys at Keyboards: The Javanomicon © Michael James Heron | ||||
| Topic: Java Programming Level: 2 Version: delta | |||||
6 - Case Study 1 - An Event Driven Calculator | |||||
| Previous | Table of Contents | Next |
| Forum |
| Chapter Objectives |
By the end of this chapter, the reader will be able to:
|
Now that we've spent some time looking at the concepts that go into developing an event driven applet in Java, let's go through an example of how they work together in practise. For this example, we are going to write the code for a simple calculator applet. Calculators are very common consumer appliances, and they all work in much the same way - you have a display that shows the answers, a set of buttons representing each of the numbers, a set of buttons for each of the calculations, and an equals button that places the result of your input into the display. Our calculator is going to work in this same fashion, except we're going to code it ourselves using the Java concepts we've already seen. We'll fashion the buttons with our very own hands, and then lovingly position them on the screen until we create a masterpiece of mathematical perfection.
The first step in designing a Java applet is to draw out the interface so we know exactly what we need to include. For this applet, we are going to need something for displaying the answer - we know that using a calculator involves entering our numbers through the provided buttons, so there will be no need for us to allow the user to enter anything into the display section. We have a component that allows us to display text with no facility for the user to alter it - a JLabel control. However, we may also want to be able to test the numeric input of our calculator without going through the predefined interface. To give us a simple display and the facility for debugging, we'll use a JtextField. We can make sure that the user can't type into later. We also have three particular families of buttons that will need created and placed:
This gives us a total of sixteen buttons to be placed on our applet. Of course, knowing what we need gives us an idea of how to lay out the applet, but we still need to have some idea as to where they are placed using the setBounds method. Working out exactly what numbers correspond to what co-ordinates takes practice, and so it's sometimes helpful to spend some time working out how these relate. This is particularly important for the length and height parameters - the other two are largely dependant on other components. Experiment with drawing various sized components onto the applet until you find a size you like. For this application, 50 by 50 seems to be a good size for each of the buttons, so that's what we'll use. Here's where we have to make a design decision about how the applet is going to look - exactly where the buttons go is a presentation exercise that is individual to the developer. For this example, we're going to set out the applet much like a convention calculator - the display running the length of the top, with the buttons arranged into two clumps - one containing the numbers and the utility buttons, and the other containing the mathematical operations. The overall size of our applet (in terms of what are valid co-ordinates) is held in the HTML file for our project. The standard dimensions are 500 by 300. We want the display component to run the length of the top of the applet, which would give it a length of 500. We want it to start a reasonable distance from the edges of the applet, so we'll start drawing it at position 10, 10. This means we need a length of 490 to run the whole length of the applet. We don't want it to draw quite to the edge - we want it to be cantered in the applet. It starts 10 spaces from the left edge, so it must also end ten spaces from the right edge. This gives us an overall length of 480. We want it to be tall enough to hold numbers, but not much taller. Experimenting gives an acceptable value of 30 for the height of our display. Now that we know how big each component will be and where the display label is located , we can design our storyboard for the applet. The location of each button will be a function of the buttons beside it. For example, we draw button 7 at the top left part of the block of numbers. If we draw it at position 10, 60, then we know that the 7 button underneath must be 50 units further on in the Y axis (because the height of each button is 50), and that the 8 button to the right of it must be 50 along in the X axis (because the length of each button is 50):
All of the number buttons in our calculator application can be placed in this fashion:
The second block of buttons are not directly related to the other family, but they should be placed at an equal distance from the display component in the Y axis (which would place the first one at position 60 in Y), and some distance from the right hand side of the applet (position 500). Since the setBounds is from the top left corner, the start point of the component will be 450 if we want it to draw right at the very edge. Let's place the first position at 430, 60, and base the rest of them on this starting position in the same way as we did for the first block of buttons (in other words, each one is 50 further down in the Y axis):
Now that we have all the numbers we're looking for, we can put them together into the storyboard for this application:
And that's our storyboard! Although working out the relationship of components against other components can be time consuming, it is much quicker to plan this out before you begin placing the elements of your user interface - all you have to do at this point is translate these numbers into a setBounds call in your applet.
Now that we have the numbers, let's start setting up our application. The first thing we'll do is take a copy of our standard Swing applet framework, as we saw in chapter four. We'll use this as the basis for developing our applet since all we have to do then is slot in the components we wish to be displayed:
We should start off slowly, and we'll start by placing our text component onto the applet. The component we need for this is called JTextField, and we're going to refer to it within code by the name display. The first thing we need to do is create a class-wide variable that can hold a JTextField:
Once we've got this, we can set up the component in the init method:
By default, the applet is still using the BorderLayout manager to place components - obviously since we've spent all this time working out co-ordinates we are going to want to switch this off in favour of our own layout:
Now that we've indicated to Java that we don't want to use the standard layout manager, we can add our display component to the applet:
And finally, we set the bounds of our component according to the values we worked out for our storyboard:
And that's our JTextField sorted out. By default, we're not going to do anything in our applet until the buttons are pressed, so we don't add a listener object for this component. Next, let's add the first of the number buttons. We start off by placing the button 7, since that appears at the top left corner of a calculator. We follow exactly the same procedure as before. First we create a class wide variable that will hold the button:
Then we put something into the variable within our init method:
Next, we add it to the applet - note that we don't need to call setLayout(null) a second time. We only need do that once:
And then we set the bounds of the control. As before, we get the numbers for this from the storyboard we developed:
Since this is a button and we want something to happen when the button is pressed, we must register an ActionListener for this component:
And that's our first button (and second component) set up. We follow exactly this same procedure for each of the buttons. For example, when it comes time for us to add in button eight, we add a class wide variable, put something into the variable, add the component to the applet, set the bounds on the component and then register an action listener. This is a fairly cumbersome process - when we talk about arrays in the next chapter, we'll see a way by which we can make Java do most of this hard work for us. Until then, we're restricted to the tools we've already covered. We'll see that the interface takes up by far the largest proportion of the code for this applet - as more techniques become available to us, we can begin to make our code much more compact and easy to maintain. For now though, we must suffer in silence.
The procedure of declaring a class-wide variable, instantiating it and setting its bounds should be repeated until all the buttons are placed on the applet. Once we've done this, we'll want to take a look at our applet and make sure everything is positioned as it should be... but when we compile, it gives an error. This is because each of our buttons has an action listener registered, but we haven't implemented the ActionListener class or included the actionPerformed method in our applet. We must add these before our applet will compile:
Now when we compile and execute the applet, we get to see our storyboard brought to life:
That's a lot of space between the two families of buttons, but we are implementing only a simple calculator. If desired, we could later add buttons for square roots, logarithms and other more complex functionality, so this empty space can later be put to good use. But we've completed the hardest part of this application - setting up the interface. Unlike the simple examples we discussed in chapters four and five, this is more than a basic proof of concept - despite their rather simple function, calculators have a relatively complex interface that requires a degree of precision in setup. Now that we have our interface, we need to do something with it.
The functionality for this particular calculator is simple. Whenever a number is typed, it should be appended to the text component. If we press an operation button (like addition or subtraction) it should store the number that is currently in the text field and then clear the display, ready for another number to be entered. If we press equals, it should take the number from the text component and perform the last selected operation on that and the number we previously stored. The first thing we're going to need is a temporary variable to hold the last number typed. We're only implementing a simple calculator here, so we'll stick purely to whole numbers. We add a class wide int variable called lastNumber to our applet. We also need a variable to hold what the last operation selected was. Let's also use an integer for this. We will avoid the use of magic numbers by making variables to hold which integer corresponds to which operation:
Every time an operation button is pressed, we're going to take the current contents of the text component and place them into our lastNumber variable, overwriting any previous contents. We also want to store information on what the operation button pressed actually was (for when we press equals). Since we want this to happen when a button is pressed, we place the code for this in the event handler method for the event type (in this case, actionPerformed). Let's start by adding this functionality for the addition button. The only difficulty in this is that the text field stores its contents as a string, and not as a number - if we try and assign the return value of the getText method to an integer we will get an error. We can turn a string containing an integer (for example, "10") into the actual integer value using the following method call:
So, for our addition button:
When we press the addition button, we store the current contents of the display text component in a temporary string variable called temp. We pass this temporary variable to the Integer.parseInt code to turn it into an integer variable, and place the result in out lastNumber class variable. We then make our lastOperation variable equal the value of the ADDITION_OPERATION variable (we set this to 1 in the init method above). Finally, we call setText with an empty string on the display text component to empty it. And that's the functionality we need for all of our operation buttons, we just need to create a variable for each kind of operation for storing in lastOperation:
We can expand our actionPerformed method to cope with all our operations using the same structure:
Now we have the operation buttons working correctly, we need to write the code for dealing with the number buttons - since we have a JTextField for display we can actually type numbers directly into the display. This is very useful for testing purposes, but it doesn't properly model the way a calculator behaves. Dealing with the number buttons is easy - all they are going to do is add a number to the end of the display. We know that the JTextField class has a setText method, and we know it has a getText method. First, we'll call getText to get the current contents, and then call setText with our new number added to the end. This is the system we'll use to add each of the numbers. Within our actionPerformed, we need some code. The following code is for the number 1 button:
We repeat this code for each button:
And when we compile and execute our code, we can run our applet and enter all the numbers via the buttons. Neato! Again, this is a lot of repetition - it would be much more effective if there was a way we could simplify the setup and make the code more concise. There is, but it's outside what we've discussed at the moment. We still need to add the functionality for the clear button - this is simple. All we do is call setText("") on the display:
Finally, we need to write the code for dealing with the equals button. All this is going to do is check to see which was the last operation selected, and then perform the selected operation on the value stored in the variable lastNumber and the current contents of the JTextField, and display the result in display. We'll use an if-else structure to determine the proper course of action. We're going to need a temporary integer variable to hold the answer before it is displayed on the display text component. We will need another to store the result of the Integer.parseInt call. We'll set them at the top of the actionPerformed method along with the String temp declaration.
And that's it, we've created ourselves a calculator applet!
Obviously there is more work to be done - it doesn't meet many of the hallmarks of good code discussed in the third chapter. There are many gains to be made in efficiency, readability, maintainability and robustness. These are left as an exercise for the reader. In the process of this chapter we have seen all of the aspects required for building an event driven program. We have designed a storyboard and then implemented a substantial interface based on our plan. Calculators are interesting from a event-drive perspective. They are functionally simple (mostly), in that the code required to implement the calculator logic is not syntactically complex. However, the interface, as intuitive as it is, requires a large amount of planning to implement and lay out correctly. Further ReadingThe following table details further reading on the topic in this chapter, and also any external resources that you may find useful.
|
| Previous | Table of Contents | Next |
© 2004-2006 Michael James Heron