Ack! This site is moving to tomhorsley.com since Comcast's efforts to improve their world's worst customer service ranking apparently include terminating web hosting for their existing customers.

Chapter 3 — Do Something!

Buttons that don't actually do anything are almost as boring as a “Hello World” message. Time to (shudder) venture into hooking up some java code to the buttons.

After consulting my copy of Programming Android, looking at example code that uses buttons, and various google searches, I came up with the following:

src/com/example/myandroid/UmpireAppActivity.java

package com.example.myandroid;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class UmpireAppActivity extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ((Button) findViewById(R.id.strike_button)).setOnClickListener(
            new Button.OnClickListener() {
                @Override public void onClick(View v) {
                    incCount((TextView) findViewById(R.id.strike_count), 3);
                } });

        ((Button) findViewById(R.id.ball_button)).setOnClickListener(
            new Button.OnClickListener() {
                @Override public void onClick(View v) {
                    incCount((TextView) findViewById(R.id.ball_count), 4);
                } });

        ((Button) findViewById(R.id.reset_button)).setOnClickListener(
            new Button.OnClickListener() {
                @Override public void onClick(View v) {
                    resetCounts();
                } });
    }

    void incCount(TextView counter, int max_count) {
        int cur_count = Integer.parseInt(counter.getText().toString());
        if (cur_count < max_count) {
            cur_count = cur_count + 1;
            counter.setText(String.valueOf(cur_count));
        }
    }

    void resetCounts() {
       ((TextView) findViewById(R.id.strike_count)).setText("0");
       ((TextView) findViewById(R.id.ball_count)).setText("0");
    }
}

I had great difficulty figuring out what things to import at the top, and discovering how the heck to turn the contents of a TextView into an integer was a bit of a challenge, but I finally got this all to compile, and it actually works:

So that's it! A successful trip from a program idea to a working program. Do I know what I'm doing now? Not a chance :-). But at least I may have acquired a microscopic fraction of a clue.

Android things I learned:

  • Attribute values in the .xml files that start with an @ are special, and the ones that start with @+ such as android:id="@+id/ball_button" are even more special, because the + means the build tool will manufacture the value for me. In this case, the value that is manufactured is a unique ID number for the widget.
  • To go along with the above example, these unique ID numbers are stashed in a generated class named R, so code like (Button) findViewById(R.id.ball_button) will use that unique ID to lookup the widget so I can refer to it in the code. This is basically how all the magic .xml layout winds up getting connected to the code.
  • One of the standard idioms for hooking code to a widget is to make an anonymous instance of some listener class most widgets expect to talk to. You can override the function in that class that handles a widget action and call your own code from that override. In my case, I'm making instances of Button.OnClickListener and overriding the onClick function to increment a text field or reset the values.

One thing that surprised me was that the app works correctly even when I rotate the screen or use the home button to switch to another app. From my reading, I thought I was responsible for stashing my state somewhere so I could put it back later, but perhaps the TextView already does that for itself, and since my state is saved entirely in the TextView widgets, I get the save and restore for free?

Investigating this puzzlement a bit, I find http://stackoverflow.com/a/5184485 which says editable controls tend to automatically preserve their contents and non-editable ones do not, and since I actually declared the text fields as EditText widgets, that probably explains things. And if I temporarily change the EditText to TextView and rebuild, I find that it not only looks quite a lot different (and not better :-), but the values do indeed get reset to zero when I rotate the screen, so the puzzle seems to be solved.

Incidentally, http://www.eigo.co.uk/Managing-State-in-an-Android-Activity.aspx seems to be a decent description of the different ways (and reasons) to save and restore state in an Android app.

 
[Next] [Top] [Prev]  
Page last modified Wed Jun 20 18:47:44 2012