dcsimg

Wiring Up Android Buttons

Three ways to configure click handlers in Android UI Code

Quick tests

Sometimes a project demands that we code in a thorough, “belt and suspenders” fashion.

However many times it just isn’t necessary to go through all of the trouble — particularly when you’re trying out a new API for the first time and you really don’t need to build a production-ready project.

I find myself often throwing together a quick app to test something — in most cases I create a single button to initiate the test.

This article takes a look at three ways to accomplish this task. Pick the one which is right for you and the project you are working on.

Generally speaking an Android application’s UI is defined within an XML layout file. When you create a new project in Eclipse, the Android Developer Tools provide a layout (main.xml) that looks like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
</LinearLayout>

Now, let’s add a Button widget to the mix,named “Hit Me”.

<Button
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:text="Hit Me"
	andorid:id="@+id/btnHitMe"
	/>

This Button needs to trigger an action in our code, so let’s have a look at the Java code behind this user interface.

Note that in order to work with a Button in code we need to import android.widget.Button.

import android.widget.Button;

And to wire up the handlers we define an instance of the android.widget.Button at the class level:

private Button btnHitMe = null;

Now, let’s get a reference to the widget within the onCreate method of our Activity class:

    btnHitMe = (Button) findViewById(R.id.btnHitMe);

The R.id.btnHitMe enumeration is automatically generated by the Android Developer Tools when the main.xml file is modified and saved. When the optional android:id attribute is included in the definition of the widget as shown in the earlier listing, the ADT automatically creates the enumerations to identify this widget throughout the project. These values are stored in R.java. Never modify this file by hand — it is a fool’s errand as it is constantly being re-written by the tools.

Once we have a valid reference to the widget from findViewById, we need to set up the click handler to process Button interaction:

        bHitMe.setOnClickListener(new Button.OnClickListener(){

        	public void onClick(View v){
                // process the button tap
        	}
        });

Within this anonymous class we implement whatever code is relevant for our test.

Here is the complete code for this anonymous handler approach, choosing to display a Toast notification when the Button is tapped, or “clicked”.

package com.msi.linuxmagazine.hotwiregui;

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

public class HotWireGui extends Activity {

	private Button btnHitMe;

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

        btnHitMe = (Button) findViewById(R.id.btnHitMe);
        btnHitMe.setOnClickListener(new Button.OnClickListener(){

        	public void onClick(View v){
                // process the button tap
        		(Toast.makeText(HotWireGui.this,"Clicked Me!",Toast.LENGTH_LONG)).show();
        	}
        });
     }
}

This code works fine though some Java folks might argue that anonymous classes are evil or some such other religious view about this code. Perhaps it is a bad habit, but not a topic for today’s discussion.

Evil or not, I prefer to not write all of this code so I tend to “cut-n-paste” this code from one project to the next. Let’s look at another approach — having the class itself be the “listener”.

Class level listener

The next approach to look at is where we have the Activity class implement the OnClickListener rather than employing an anonymous class.

To do this we need to import the Interface:

import android.view.View.OnClickListener;

Then we indicate that the class implements the Interface:

public class HotWireGui extends Activity implements OnClickListener

Of course, then we must actually implement this interface which means that we need a method named onClick which takes a single View argument. The full code is shown here:

package com.msi.linuxmagazine.hotwiregui;

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

public class HotWireGui extends Activity implements OnClickListener{

	private Button btnHitMe;

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btnHitMe = (Button) findViewById(R.id.btnHitMe);
        btnHitMe.setOnClickListener(this);
     }

	 public void onClick (View v) {
         // process the button tap
		 if (v.getId() == R.id.btnHitMe) {
			 (Toast.makeText(HotWireGui.this,"Clicked Me in onClick method!",Toast.LENGTH_LONG)).show();
		 }
	 }
}

Things to note about this code:

  • We still have to define the Button and get a reference to it with the findViewById() method.
  • We still need to setup the “OnClickListener” — though this time we simply pass in this.
  • The OnClick method can either make an assumption about the View it is receiving if there is only one we are working with, or we need to do some sort of check on the passed-in View instance to determine which code to run. This is of course only relevant if the Activity has to respond to more than one view that can raise the onClick event.

From a read-ability perspective, this approach may be easier as we’re not dealing with the nameless anonymous class. When there are many Buttons in play a bunch of calls to findByView followed by setOnClickListeners and the accompanying enclosed methods can be a bit tedious to sift through in the code — let alone write in the first place. With this single click handler and comparisons the code can be a bit easier to read and maintain. One downside however is that this one method is likely going to handle ALL clicks in the Activity.

OK, so let’s say you’re super lazy and you just don’t even want to do this modest amount of coding?

What can we do to wire up a Button without:

  • Implementing the OnClickListener interface, or
  • Without importing the Button class, or
  • Without finding the view and assigning the listener for each button in Java code?

Can this really be done? Yes — let’s look at a really quick and dirty way to implement a click handler for an Android Button to help you build simple apps without the fuss of all of that boiler-plate code.

Cheating?

We start by looking at the code — there’s just not much to it so we’ll just walk through it quickly.

We start out with the Button definintion in the layout file — we need to make a small change there. Don’t worry, it’s worth it.


<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Hit Me”
android:id=”@+id/btnHitMe”
android:onClick=”HandleButton”
/>

This looks identical to the version we saw earlier with the exception that there is a single addition of a new attribute, namely android:onClick.

The value assigned to this attribute, HandleButton, refers to a developer-supplied method with the “onClick” signature of (View v).

Let’s have a look at the Java code implementing this Activity.

package com.msi.linuxmagazine.hotwiregui;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class HotWireGui extends Activity {

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
	}

	public void HandleButton(View v) {
        if (v.getId() == R.id.btnHitMe) {
			(Toast.makeText(HotWireGui.this,"Hot wired Button!",Toast.LENGTH_LONG)).show();
		}
	}
}

Note that the Button class is not imported. There is no definition of a Button instance. No calls to findViewById. No calls to setOnClickListener. No extra sets of curly braces to match up on the anonymous class’ onClick handler, etc.

We must however provide a method with the correct signature. In this case we’ve called it HandleButton.

In the code we evaluate the passed-in View instance and check it’s Id. If it matches the one we’re looking for we implement our code. This same routine can handle an arbitrary number of Button’s on the screen.

The other neat thing here is that we can have an arbitrary number of these methods, named however we like, to further segregate the code for the Buttons. Want a different click handler name for a set of Buttons? No problem, just reference it appropriately in the XML layout file for the android:onClick attribute.

I stumbled upon this approach when writing a remote control for a robot with a bunch of buttons. Coding all of that boiler-plate code just bothered me and I was pleased to settle upon this approach.

Is it right for you? Perhaps. Let me know what you think.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62