Algorithms and OOD (CSC 207 2014S) : Readings

Making Our Apps Do Things


At this point, you know how to set up an Android app and get it onto your device. You've also seen how to add and modify the objects in an app, at least a little bit. Now it's time to see how we can modify the code, add some functionality, and actually make an app that we can interact with.

Prerequisites: Introduction to Android & Lab: Your first android app.

Adding Images to Your App

Yes, we can modify our apps by adding objects in the XML files, but we can also add other resources like images. Continuing with our basic app, we should probably include a representative image, because pictures speak louder than words.

Say we wanted to make our app about an inquisitive baby chameleon called Percy Shabunkins. This guy, right here.

When we have an appropriate picture, we open the res folder to store it in. We can see that there's alredy a bunch of folders created for us to store our images in. Because Android machines come in many different shapes and sizes, its good practice to make our resources in different resolutions. If we have an image that corresponds to the three pre-made drawable folders - drawable hdpi (high density), drawable mdpi (medium density), and drawable ldpi (low density), we can be more confident that the screen will show up the way we intended it to on any machine. The platform will pick the image that best matches the device's density.

We don't need to worry about that now, since this app won't actually be released in the Play Store. It's mostly for class use, and the machines you'll be using are all Nexus 7 tablets.

Because we're using a Nexus 7 device for developing, we want to put our image in the drawable hdpi folder. To do this, we expand the res folder and drag our picture from the it's current location to the drawable-hdpi folder. When prompted, we select to copy the files, instead of just linking to them. This puts the resource in the actual app, so if we were to publish it, the resource would stay there. In the other case, we would only be able to successfully compile the app on our own computer, where the resources are located.

Design is Important

Before we actually use our image, though, we need to decide what we want our app to look like. We can't just throw images and text on the screen with no predefined idea. That's bad design practice.

We want our product to end up looking somewhere along these lines.

Isn't that just wonderful? We can now begin to build our app.

Choosing a Layout Type

Bearing our design idea in mind, we want to be able to create an app whose presentation is similar on all possible Android devices. Our design is also not overly complicated. We need to have three objects one under another, neatly centered in the middle of the screen.

The first thing to do is choose the layout your objects will be put on. You can see there are many different layout types in the Graphical Layout tab. Each of the layouts is useful for a specific type of an app. Feel free to explore all the different layouts on your own, because unfortunately this is out of the scope of this course.

For our app, the default layout is the relative layout. The relative layout is useful when you want to manage your objects (children) relative to one another or the layout(parent) itself. For this project a simple Linear Layout will do the trick.

In a Linear Layout your objects are positioned in a line, one after another, either vertically or horizontally. Because that is exactly what we want form our app, a linear layout makes more sense.

Putting It Together

We need to identify what the parts of our app are. Looking at the screen, it seems like we want

  • a TextView on top,
  • an ImageView beneath it, and
  • a Button Widget on the bottom.

First, we change the RelativeLayout tag in the raw XML to LinearLayout. If we look at our Graphical Layout now, there's nothing really changed. Our string is still the same, and positioned in the upper left corner.

We can now add our image beneath it, by opening the Images & Media folder (in the Palette window in the Graphical Layout interface), and dragging the ImageView on our screen.

Next let's drag a Button Widget beneath the ImageView.

The resulting screen is not quite what we wanted. This is because we haven't set the orientation in our LinearLayout. To do this we have to add a new attribute to our LinearLayout object. In particular, we should add android:orientation="vertical" in the opening LinearLayout tag.

Our App is starting to look better.

But how did that Button end up on top of the ImageView? We need to change that. We can either drag the Button underneath the ImageView in the Graphical Layout, or change the order in which the objects are declared in the raw XML file, so we end up with a screen that's pretty close to what we've envisioned.

We just need to adjust and add some attributes and strings to match it exactly to our original plan. Our layout's raw XML should look like this.

  • android:gravity sets where the children of the parent with that atribute will gravitate towards.
  • android:textSize affects the size of the text.
  • android:layout_width is the width of the object. "wrap_content" means that the width of the object will be just big enough to enclose its contents. The layout_width of the ImageView was set to "match_parent" before, which means that that specific object will be as big as the parent object (minus padding). This was making the Button object act funny. The ImageView itself (that could be larger than the actual image) was trying to fill the entire screen, so there was no room for the Button on the bottom.
  • android:layout_height is the height of the object The layout_height of the ImageView was set to a specific number. This means that the image would always render with that height, but the width would have tried to fill the parent element. In that case, the screen would have rendered differently on different devices, which in our case, would be a sign of bad programming design.
  • android:contentDescription is a neccessary evil we need to add to the ImageView to make Eclipse happy.

Now our product looks just the way we imagined.

And this is how our strings file currently looks like with all the added strings.

Of course we could have done this another way, by simply declaring our XML objects in the raw XML file and giving them their appropriate attributes immediately, instead of changing them later. Feel free to use whichever method you'd like when creating your Android application.

Linking Objects and Methods

Now when we run our app, it gives us a nice picture of Percy. This is pretty boring, so we should really make our button do something.

For example, we can make use of the Toast class. A Toast that is a view that contains a quick little message for the user, for example "Hey, I'm a chameleon.".

To make this happen, we need to create another method in our activity class. (Finally, we get to write some Java!)

Let's call our method onClick, because it's what happens when the button is clicked. It needs to be public, void and take a View parameter. When Eclipse complains that View is not imported, we go ahead and import it.

public void onClick(View v) 
{
	
}

Because we want to manipulate the button object, we have to somehow get it in our Java code. We do this by declaring a private Button variable.

private Button button;

Eclipse will again complain about the Button class not being imported. Import it.

After that we have to set the button object to the button in our XML. We do this by refering to it via a resource ID in the R file.

  button = (Button) findViewById(R.id.button1);

findViewById returns a view (a somewhat generic class). But we want a button (which, believe it or not, is a type of view), so we cast it to a Button. Such casting gives us access to its "buttonisity".

A common aspect of app development is using listeners to enable reaction in objects. To make out button react we call the setOnClickListener method on our Button object, with this as a parameter. And, as you know, “this” refers to the object that we're building to react to button presses.

To make our object respond to button clicks, we write

button.setOnClickListener(this);

We declared the button outside a method, but we set it up in the onCreate() method. Why? Because it's the method that's called automatically, and it's the method that's called first. If we had set it up in a new method, the button's set up and it's activity would have to be called simultaniously, ultimately causing our application to fail to execute properly. It will be able to set up the button, but not execute the rest of the code.

Eclipse has underlined the setOnClickListener(this), though, and it's not giving us a good reason why. It suggests changing the method to another one, or casting (OnClickListner) before this.

Neither of these suggestions is valid. The correct answer is that our Activity class has to implement the OnClickListener interface. You'll learn more about implementing interfaces, and interfaces themselves and in the future, but for now, just now that this make our activity both an Activity and takes on the behavious of an OnClickListener, so we can pass this as a parameter when calling methods from both the Activity class and the OnClickListener interface.

Now that we've set up our Button, we can finally make it to something in the onClick method. Remember the Toast class we've talked about before that displays a simple message for the user? We're going to use that.

We declare a Toast variable

Toast chameleonGreeting;

and we call a makeText method on it. The makeText method is a static method that takes three parameters, the Context, the Text we want it to display, and the duration of how long the Toast is displayed.

makeText(Context context, CharSequence text, int duration)

For the context, we'll use the application context, by calling the no parameter method getApplicationContext(). We'll hardcode a string for the CharSequence, and use one of the two predefined durations - LENGTH_SHORT (the other being LENGTH_LONG).

So the full method we want to invoke on our chameleonGreeting will be

  makeText(getApplicationContext(), "Hey, I'm a chameleon!", Toast.LENGTH_SHORT)

After that we need to call the show() method on our parameter to actually show it on the screen. We do that by simply stating

chameleonGreeting.show()

Our full method should then look like this:

public void onClick(View v) 
{	
  Toast chameleonGreeting = Toast.makeText(getApplicationContext(), 
      "Hey, I am a chameleon", Toast.LENGTH_SHORT);
  chameleonGreeting.show();
} // onClick(View)

If we run our app now, we can see our button in action.

Because we've called our method onClick, while implementing the OnClickListener, we've implemented an OnClickListener method that connects to the element we've set the listener for (in this case our button). Buttons have an onClick attribute in the XML, that we've now set up for the button via the Java file, instead of setting it through the XML.

There's another, possibly simpler, way to do this, though. We can simply tell the button what to do when the onClick method is called by giving our button another attribute in the XML

android:onClick="clickThis"

Here we basically tell the onClick method that it should do whatever we say it should in a method called clickThis().

With this implemented, we can scratch all we did in our activity class, until it looks as it did before.

In this case we don't need to create a button that refers to the button we already have in the XML and add to it, because it's all done in the XML. We don't need to implement the interface, and the only thing we need to do is create a new method clickThis().

The method basically looks the same as the onClick method we did previously, without all the overhead:

public void clickThis(View v) 
{
  Toast chameleonGreeting = Toast.makeText(getApplicationContext(), 
          "Hey, I'm a chameleon", Toast.LENGTH_LONG);
  chameleonGreeting.show();
} // clickThis

The only change we've made here is the duration in the makeText method. Running our app now, makes the Toast message appear on the screen for longer, but the general effect is the same.

And we've succeeded adding a bit of functionality to our app. That wasn't that hard, was it?

In the future, it's technically “better”, or at least more in the spirit of developing Android apps, to use the second method. The XML is supposed to control what happens to the objects, and the Java files tell them what to execute.

Copyright (c) 2013-14 Samuel A. Rebelsky.

Creative Commons License

This work is licensed under a Creative Commons Attribution 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.