Android Calculator

From Imperial College Robotics Society
Jump to: navigation, search


Division by 0 is supported

1. File > New > Project > Android Application Project

2. Pick a name

3. Pick the highest build sdk available, and picking minimum sdk as 2.2 is probably a good idea as that includes nearly all phones, without sacrificing too many features which aren't supported in earlier versions

4. Uncheck "create custom launcher icon"

5. Press next, create BlankActivity, press next.

6. Rename the Activity Name and Title to "Calculator", and the layout to "activity_calculator". Press finish.

7. The layout of the calculator will be under Calculator > res > layout > activity_calculator. Android uses xml files to define the layout. Because apps need to be supported on many different resolutions and aspect ratios, the layout can be quite tricky to get right. There is a graphical layout you can use, or by switching the tabs at the bottom of the window you can change to activity_calculator.xml to view the raw xml file. An xml file defines the UI using a series of nested elements. These elements have this form;

<ElementType parameters> stuff inside the element </ElementType>

Or, if there are no elements nested inside it, simply

<ElementType parameters/>

For example, here is a TableRow element, with two TextView elements nested inside it;

<TableRow
	android:layout_width="wrap_content"
	android:layout_height="wrap_content">
	<TextView
		android:text="Cell 1"
	/>
        <TextView
		android:text="Cell 2"
	/>
</TableRow>

There are several different overall layouts you can use to design a UI. LinearLayout defines the positions of elements by linearly defining one after the other. RelativeLayout defines their positions by relations between them (A good explaination of different layout schemes can be found here http://www.learn-android.com/2010/01/05/android-layout-tutorial/). We are going to use TableLayout, and put buttons in all the cells.

8. We are going to use a TableLayout scheme, to lay out all the buttons and the results text at the top. A TableLayout has nested TableRow elements, which in turn contains elements for each induvidual cell. Unless specified otherwise, one element = one cell.

Replace the existing layout in activity_calculator.xml with this;

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/table" >
 
</TableLayout>

The two lines that say xmlns:android = "..." and xmlns:tools = "..." are defining a namespace for the xml file. We only need these once. android:layout_width="match_parent" and android:layout_height="match_parent" are layout parameters for the element, and "match_parent" means that the table should be able to fill up the parent that contains it, which since it is the upper most element, is the whole screen (NOTE: This doesn't mean it will actually scale to fill up the screen, or any of that fancy business).

android:id="@+id/table" allocates the table an ID so it can be referenced in the code. This is needed to get the table to be laid out correctly later on.

There are two basic elements we are going to use; TextView and Button. TextView is basically a text box, and Button is ... a button. TextView is used to display the numbers at the top of the screen, and the Buttons for everything else. Each needs to have two properties; an id and some text to display. For example, button 9;

<Button
    android:id="@+id/b9"
    android:text="9" />

See if you can layout a calculator using nested TableLayout, TableRow, TextView and Button elements. Note that you need all of the following buttons; 0-9, +, -, /, *, clear and =. So that your layout works properly with the code later on, give them these IDs; "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b0", "bdot", "plus", "minus", "multiply", "divide", "clear", "equals".

When done, or if you want to cheat, it should look something like this;

If you compile and run your app now (done by going to Run > run) it should at least look like a calculator, but it wont do anything and will be a bit too small.

9. Now we can do the code. Navigate to Calculator > src > com.example.calculator > Calculator.java. This is where the main activity class for the app is. There will only be two little bits of code already; onCreate(), and one other I can't remember which has something to do with the menu - delete that one, we don't need it.

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState); // Restores a previous inscance of the app if it is
						//destroyed and restarted for some reason
	setContentView(R.layout.activity_calculator); // sets the layout to activity_calculator.xml
}

super.onCreate(savedInstanceState); is needed to restore a previous instance of an app if the current app is destroyed. For example, if you rotate your screen, the app will restart, and we don't want to lose the state of the app. Because the app will restart if the layout is changed, we can put our code defining the layout of the table in this method, and not worry about the size changing later.

setContentView(R.layout.activity_calculator); sets the current layout to the activity_calculator.xml, which we were just working on. Resources you have for your app under the "res" folder gets added to a large enum in a class called "R". This includes individual elements of the layout which appear under R.id e.g. putting android:id="@+id/table" in the layout means we can refer to it using R.id.table.

Now we want to set out the code which expands the table to the size of the screen. This seems a little unnecessary, why can't we just set this in the layout? I don't know, but it's useful to see how it's done anyway. You can obtain the width and height of the screen by calling an Activity method;

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
int width = metrics.widthPixels;
int height = metrics.heightPixels;

(NOTE: To ever find out how to do specific things like this, find a resolution, turn on the torch, vibrate the phone, e.t.c. just google it, android has a huge developer community, there's literally nothing out there that someone hasn't asked a question about on StackOverflow. I'm only really going to try and cover the common stuff)

Now we need to find the table. All layout elements have equivalent classes in android. You obtain a reference to the element by using Activity.findViewById(R.id.whatever) and casting it to what you want. So to find the table, use;

TableLayout table = (TableLayout) findViewById(R.id.table);

Now we need to change the size of each button in the table. To do this we cycle through the TableRows nested in the TableLayout, then check which elements in the TableRows are Buttons, then set their parameters. We also want to enlarge the text, and register a callback for when the button is clicked.

for (int i = 0; i < table.getChildCount(); ++i) {
	TableRow row = (TableRow) table.getChildAt(i);
	for (int j = 0; j < row.getChildCount(); ++j)
		if (row.getChildAt(j) instanceof Button) {
			Button button = (Button) row.getChildAt(j);
			button.setHeight(height / 6); // theres 5 rows, but the action bar takes up a bit of room.
							// Including the size of the action bar properly is ever so slightly more involved, so not covered here
			button.setWidth(width / 4); // since 4 columns
			button.setTextSize(TypedValue.COMPLEX_UNIT_PX, height / 10);
			button.setOnClickListener(new Button_Clicker());
		}
}

This sets the sizes to what we want. Note that a more common way to add a button callback is to add the android:onClick="click_method" property to the button element in the layout; then this will call the method in the class that created the layout when it is clickde. So for the equals button, we could have used;

<Button
      android:id="@+id/equals"
      android:text="="
      android:onClick="equals" />

Then added a method in Calculator.java that looked like this;

public void equals(View view) {
      ///blah blah blah
}

However, since we are going through all the buttons anyway, its less effort to do it all in the code.

Now we need to set the layout properties of the TextView. Since we will refer to this TextView later on too (to display results, e.t.c.) add it at the top of the class as a local variable. The rest is done the same way as before;

resultsBox = (TextView) findViewById(R.id.resultsBox);
resultsBox.setHeight(height / 6);
resultsBox.setTextSize(TypedValue.COMPLEX_UNIT_PX, height / 10);

Now if you compile and run your app (after commenting out the reference to the button listener), the calculator should fill the screen, more or less.

10. Now lets add the button listener. Add it inside the Calculator class to make things easier. Lets call it Button_Clicker and it needs to implement the Button.OnClickListener interface. The only method we need to use is the onClick(View v) method;

class Button_Clicker implements Button.OnClickListener {
      public void onClick(View v) {
            // stuff
      }
}

You can see that onClick takes the parameter (View v). View is the parent class of all layout elements. This means when onClick is called, we can check which the button that called it by looking at this parameter and checking it's id. We do that like this;

class Button_Clicker implements Button.OnClickListener {
      public void onClick(View v) {
            switch (v.getId()) {
            case R.id.b0:/*button 0 pressed*/ break;
            case R.id.b1:/*button 1 pressed*/ break;
            case R.id.b2:/*button 2 pressed*/ break;
                  /*...*/
            }
      }
}

We will also need to find and change the TextView at some point, to display what is being typed, and the results. To do this use the self-explanatory method resultsBox.setText("string");

10. The rest of the programming is not android-specific, its just a coding exercise. Java has classes called Integer, Float, String, e.t.c. which all have methods to convert back and forth between each other, which are what I used. My full code is below. Unfortunately it suffers from horrendous floating-point errors, so don't actually use it for maths, you won't always get the answer you were expecting.