Chapter 9. Adding Interactivity: Handling UI Events

In this chapter, we will explore how to wire up those super-cool UI designs that you have seen in the previous chapters, so that your UI design becomes highly functional within your Android application. With Android's convenient event listeners, you can easily add in your own custom programming logic. Using the event handling described in this chapter, you'll be able to have your UI and graphical elements actually do something productive or impressive after they are tapped on (touchscreen), navigated to (navigation keypad), or typed into (keyboard).

We'll begin with an overview of how Android listens to its touchscreen and keyboard, and how to harness the power of input devices.

An Overview of UI Events in Android

The way that we talk to all of the input devices in Java, and thus in Android, is via events for each type of input device (touchscreen, keyboard, and navigation keys). Events are actually system-generated messages that are sent to the View object whenever a UI element is accessed in some fashion by a user. Event refers to something that you attend or otherwise recognize as being significant, and thus is the perfect term for these UI occurrences via Android input devices.

Listening for and Handling Events

Handling and handlers are two other terms used in conjunction with events in Java and Android. Once these events are triggered by a user's touch, keystroke, or navigation key, they must be handled within your application. This is accomplished inside a method (such as onClick() or onKeyDown()) that specifies exactly what you want to happen when one of these input events is detected by Android and is sent over to your appropriate event handler for processing.

This concept of handling events is termed listening in Android. You will see the terms event listeners and event handlers throughout this chapter. That's because they are what the chapter is all about: how to put into place the proper event listeners and event handlers to cover your app users' interaction via touchscreen, navigation keys, and keyboard input devices that are part of a smartphone's hardware.

Handling UI Events via the View Class

Each of the UI elements in your application is a View object of one incarnation or another, and each has events that are unique to that element. This is how user interaction with specific UI elements is kept separate and organized. Each of these View objects keeps track of its own user-input events.

The way that a View object within your layout talks with the rest of your application program logic is via a public callback method that is invoked by Android when a given action occurs in that UI View object. For instance, if a Button is touched, an onTouchEvent() method is called on that object, because Android knows to call a method of that name when that event occurs. In other words, Android calls back to the object that received an event so that the object can handle it.

For this callback message to be intercepted by your Java code and program logic, you need to extend your View class and override the method from the View class that your UI widget was spawned (subclassed) from. To override a method means to declare and define that method specifically within your class, and have it do something via your own custom program logic.

Since your UI design is made up of a collection of View objects in one or more ViewGroup layout containers, you can see how this might represent a gaggle of coding just to make sure all of your UI elements are properly listening to the keyboard, touchscreen, and navigation keys. Has Android done anything here to make things easier on us, as it has in other areas of app development?

Yes, Android has provided a way to facilitate event handling. The View class from which all of our UI widgets are subclassed contains a collection of nested interfaces featuring callbacks that are far easier to define, as they are part of the system that makes up the View class and all of its methods.

These nested interfaces that are already a part of all of your View class-based widgets are called event listeners. They provide the easiest way to quickly set in place code that will capture user-input events and allow them to be processed right there in your application program logic.

Event Callback Methods

In the most simple of terms, an event listener is a Java interface in the View class that contains a single callback method to handle that type of user-input event. When you implement a specific event listener interface, you are telling Android that your View class will handle that specific event on that specific View.

These callback methods are called by Android when the View object that the callback method is registered to is triggered by the user-input device used to access that UI interface element. (I like to say the method is wired up to the View object, but then again, I am a programmer and drink far too much coffee.)

The callback methods that we are going to cover in this chapter are the most common ones used in Android application development. They are listed in Table 9-1.

Table 9.1. Common Android Callback Methods

Method

From

Triggered By

onClick()

View.OnCLickListener

Touch of screen or click of navigation keys

onLongClick()

View.OnLongClickListener

Touch or Enter held for 1 second

onKey()

View.OnKeyListener

Press or release of key on phone

onTouch()

View.OnTouchListener

Touch, release, or gesture events

onFocusChange()

View.OnFocusChange

Focus change

onCreateContextMenu()

View.OnTouchListener

Context menu

In the table, two of the methods are not directly triggered by user input, but they are related to input events. These are onFocusChange() and onCreateContextMenu(). onFocusChange() tracks how the user moves from one UI element to the next. The term focus refers to which UI element the user is using or accessing currently. When a user goes from one UI element to another one, the first UI element is said to have "lost focus," and the next element is said to now "have the focus." The onCreateContextMenu() method is related to the onLongClick() callback method, in the sense that context menus in Android are generated via a long-click user action. This is the touchscreen equivalent of a right-click on most computers.

To define one of these callback methods to handle certain types of events for one of your View objects, simply implement the nested interface in your activity or define it as an anonymous class within your application. If you define it as an anonymous class, you pass an instance of your implementation of the listener to the respective set...Listener() method, as you'll see in the next section.

In the rest of this chapter, you'll learn how to leverage the main event listeners in Android so you can make your applications interactive and useful.

Handling onClick Events

The onClick() method is triggered when the user touches a UI element. As you might guess, it's the most commonly used event handler out there. So, it only makes sense to start with handling onClick events.

Implementing an onClick Listener for a UI Element

First, let's create an anonymous OnClickListener:

final OnClickListener exampleListener = new OnClickListener()
{
    public void onClick(View v) {
        //Code here that does something upon click event.
    }
};

This is an example of an anonymous class. This line of code sets up a variable called exampleListener as a new OnClickListener object, which listens for onClick events.

Note

Recall from Chapter 7 that a final variable cannot be reassigned a value once it has been set. This ensures that another listener does not get assigned.

It is logical, then that inside this class definition there would be a public onClick(View v) handler to handle the onClick event. The public onClick handler is passed an ID reference to the View object that was clicked on, so that it knows which View object to handle. Note that the View that has been clicked is named v, so if you want to reference the View object in the code inside this method, it is ready to go and must be referenced via a variable "v".

How any given onClick handler handles a click event is up to the code inside the onClick handler. That code basically explains what to do if that UI element was clicked on or touched, or typed with a keystroke.

If you want to come off as really cool right now, simply look up casually from the book and exclaim to your family, "I'm coding an onClick handler in Java right now," and then look back down and continue reading.

We have defined an OnClickListener, but we need to wire it to a UI element (attach it to a UI View object) before that code can be triggered. Usually, this will go inside the onCreate() method (which you have become familiar with in the first two-thirds of this book).

It takes only two lines of code to connect a button to the exampleListener object. The first is simply our Java declaration of the Button UI object in our main.xml UI layout definition:

<Button android:text="First Button"
           android:id="@+id/firstButton"
           android:layout_gravity="center"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>

The second line is where we connect the button construct with the event listener construct, by using the Button widget's setOnClickListener() method, like so:

Button exampleButton = (Button)this.findViewById(R.id.firstButton);
exampleButton.setOnClickListener(exampleListener);

Adding an onClick Listener to an Activity in Android

You will probably not be surprised when I tell you that there is an even sleeker way to define your event listeners for your activities, using even fewer object references and lines of code. This is normally how you will want to do things in your Android applications programming activities.

You can implement an event listener directly inside the declaration of your activity within the actual class declaration. Wow. Event listeners must be muy importante.

Here is a class declaration that uses the implements keyword to embed an OnClickListener directly into the class via its declaration:

public class ActivityExample extends Activity implements OnClickListener() {...}

The previous two lines of code declaring the Button and wiring via setOnCLickListener() would still exist inside the onCreate() code block, but the declaration of the exampleListener object and class would not be necessary.

Now it's time to create our Chapter 9 project folder and implement a button and onClick listener so that you can see event handling in action.

Creating the Event Handling Examples Project in Eclipse

For our first example, we'll set up the button so that when it is clicked, the text on a TextView changes.

In Eclipse, close the Chapter 8 project folder (right-click it in Package Explorer and select Close Project), if it's still open. Also close all the empty tabs at the top of the Eclipse IDE, using the x icons in the top-right side of each tab.

Select File

Creating the Event Handling Examples Project in Eclipse
  • Project name: Name the project Chapter 9.

  • Build Target: Choose Android 1.5.

  • Application name: Name the application Event Handling Examples.

  • Package name: The package name should be event.handling.

  • Create Activity: Check the box and name the activity HandlerExamples.

  • Minimum SDK Version: Set this to 3, which matches our Android 1.5 build target and emulator.

Creating the Chapter 9 Android project

Figure 9.1. Creating the Chapter 9 Android project

Editing the HandlerExamples.java File

Now let's edit the java code:

  1. In the Package Explorer, open your project tree hierarchy by clicking the arrows next to the /src and /res folders, so that you can see their contents. Select the HandlerExamples.java file under the /src/event.handling folder by clicking once on it (it will turn blue), and then hit the F3 key on your keyboard. This is the keyboard shortcut for the Open option.

  2. Notice that some code has been written for us. The first thing we need to do is to implement OnClickListener. Add implements OnClickListener to the end of the class declaration, as shown in Figure 9-2. (Note there is a deliberate typo here, so I can show off some features of Eclipse. See if you can spot it.)

    Editing HandlerExamples.java

    Figure 9.2. Editing HandlerExamples.java

  3. As you can see in Figure 9-2, Android and Eclipse have alerted us that something is amiss. If you hold the mouse over the red-underlined keywords, Eclipse will tell you what it thinks is wrong. When you mouse-over the HandlerExamples keyword in the class definition, up pops a box (shown in Figure 9-2) saying that Eclipse wants to see an onClick() method. To fix this, click the Add unimplemented methods link (the first one), and Eclipse will add the method for you (see Figure 9-3), as follows:

    @Override
    public void onClick(View v) {
            // TODO Auto-generated method stub
    
    }

    Note

    Since the onClick code uses a View object, Eclipse imports android.view.View, which is shown at the top of the file.

  4. There is nothing better than having our IDE write some code for us. Let's try it again. Mouse-over the OnClickListener keyword, and Eclipse will tell you that you need an import statement. Click the Add import link, and Eclipse will add the import statement (highlighted at the top of Figure 9-3).

    Implementing a listener in our class definition via the implements keyword

    Figure 9.3. Implementing a listener in our class definition via the implements keyword

    Note

    You need to get used to looking at what Eclipse is telling you as you code. This awareness is especially useful while you are learning the programming language and the development environment. That is why I am showing you some mistakes here, rather than writing perfect lines of code every time. One of the things you need to master is your process of working with the Eclipse IDE.

  5. But there is still an error in the class declaration. This is because when you implement an OnClickListener, you do not need to add the () at the end. I removed the typo, and then I got a clean bill of health from Eclipse, as shown in Figure 9-4.

    public class HandlerExamples extends Activity implements OnClickListener {
    A listener implemented correctly in Eclipse

    Figure 9.4. A listener implemented correctly in Eclipse

  6. Now let's define our Button and attach our setOnClickListener() to it. We talked about this earlier in the chapter, but this time, the containing activity is the event listener, so we use this to refer to the containing object.

    Button button = (Button)findViewById(R.id.testButton);
    button.setOnClickListener(this);

This is shown in Figure 9-5, along with the import android.widget.Button; statement that we need in order to use the Button in our code.

Defining a Button in HandlerExamples.java

Figure 9.5. Defining a Button in HandlerExamples.java

Editing the main.xml File

Now it's time to set up the XML mark-up in our main.xml file.

  1. Select the main.xml file under the /res/layout folder and hit F3 to open it in the IDE in its own tab.

  2. Click the Layout tab at the bottom of the IDE to show the layout visually (see Figure 9-6), Then drag the Button widget (shown circled and selected in Figure 9-6) onto the screen to the right, and drop it into place under the TextView widget.

    Adding a Button via a drag-and-drop operation the Eclipse layout editor in our main.xml file

    Figure 9.6. Adding a Button via a drag-and-drop operation the Eclipse layout editor in our main.xml file

  3. Now click the main.xml tab at the bottom of the IDE to switch the view from visual layout to coding view. Cut and paste the Button code so that it comes before the TextView code (but after the LinearLayout tag). The Button should be the first thing at the top of the screen.

  4. Add the "CLICK TO GENERATE EVENT" text, testButton ID, and centering attribute you learned about in the previous chapter. Let's also add a few TextView attributes to improve visibility (see Figure 9-7).

    <?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"
        >
    
    <Button android:text="CLICK TO GENERATE EVENT"
    android:id="@+id/testButton"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    
    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/testText"
        android:text="BEFORE CLICK TEXT!"
        android:textColor="#FFCC99"
       android:textSize="24px"/>
    </LinearLayout>
Adding Button and TextView attributes in our main.xml file

Figure 9.7. Adding Button and TextView attributes in our main.xml file

Updating HandlerExamples.java

Now let's go back into our Java code.

  1. Click the HandlerExamples.java tab at the top of the code editor pane.

  2. Add the code that responds to a click on the button, as follows (see Figure 9-8):

    public void onClick(View v) {
            TextView text = (TextView)findViewById(R.id.testText);
            text.setText("BUTTON HAS BEEN CLICKED. EVENT PROCESSED.");
    }

We add our TextView object declaration into onClick(). We also add a setText("BUTTON HAS BEEN CLICKED. EVENT PROCESSED.") call to the TextView object we named text, created in the previous line.

Defining the onClick() event Handler Code for a TextView in HandlerExamples.java

Figure 9.8. Defining the onClick() event Handler Code for a TextView in HandlerExamples.java

Running the Event Handling Examples App in the Emulator

To run this example, right-click your Chapter 9 folder in the Package Explorer pane and select Run As

Running the Event Handling Examples App in the Emulator
Running the onClick event example in the Android 1.5 emulator

Figure 9.9. Running the onClick event example in the Android 1.5 emulator

Android Touchscreen Events: onTouch

Android handsets that feature touchscreens—the vast majority of them today—can take advantage of advanced touchscreen features, such as gestures.

Note

Gestures are movements with the user's finger across the touchscreen that invoke certain program functions. They are popular for interaction on large screen smartphones and tablets. You will want to learn about implementing gestures when you become a more experienced Android developer. You have already been introduced to the onTouch event handler in the previous chapter, where we used it to trigger the start() method of a frame animation sequence of bitmap images. Gestures became available in Android 1.6 and thus do not work in Android 1.5 which is the version we are developing for in this book to provide the widest audience of user compatible devices.

It is important to note that an onClick event handler also works on a touchscreen, but an onTouch handler does not work with the navigation keys or selector key (the center selector Enter key). Therefore, it may be wise to use the onClick() method for most UI operations, and use onTouch() specifically when working with more advanced touch events such as gestures that involve only the touchscreen.

Since we have already covered implementing onTouch() (you can revisit it in Chapter 8 if you like), we'll continue here with the other important event handlers. These are the ones you will use more frequently in your application's design and coding.

Touchscreen's Right-Click Equivalent: onLongClick

After OnClick, OnLongClick is the next most used interface event. It is generated by the most input hardware and also the basis for the context menu in Android.

The onLongClick() method works with the following:

  • When the user touches and holds on the touchscreen for 1 second

  • When the user holds down the Enter button on the phone

  • When the user holds down the center navigation key for 1 second

Any of these will generate an OnLongClick event for whatever UI widget has the focus.

Since any View object can trap an onLongClick() callback, the most elegant way to show this event handling is to add it to our Button UI object in our current Chapter 9 example code. This will also allow you to see the common scenario of more than one type of handler being used right alongside other types of event handlers in the same View and class.

  1. In HandlerExamples.java, add a comma after OnCLickListener in the public class HandlerExamples definition and add OnLongClickListener, as shown in Figure 9-10. Then mouse-over the red-underlined OnLongClickListener and select to add the import statement (boom bam—there is our import code for this listener). Then mouse-over the red-underlined HandlerExamples class name and select to implement handler code. Voila, we now have the following:

    public boolean onLongClick(View arg0) {
        // TODO Auto-generated method stub
        return false;
    }
    Implementing an OnLongClick listener in HandlerExamples.java

    Figure 9.10. Implementing an OnLongClick listener in HandlerExamples.java

  2. Now copy the text object and text.setText() code from the onClick handler and paste it into the onLongClick handler, where the placeholder comment code is. Change the text message to reflect the hold and long-click, as shown in Figure 9-11. Note that we can use the same object name text in both handlers. Since it is a local variable to each handler, neither text object sees the other reference.

    Attaching an OnLongClick listener to our Button object in HandlerExample.java

    Figure 9.11. Attaching an OnLongClick listener to our Button object in HandlerExample.java

  3. Now try the new functionality. Right-click the Chapter 9 folder and choose Run As

    Attaching an OnLongClick listener to our Button object in HandlerExample.java
  4. Well, we forgot to change the default onLongClick() code, which returns false. This tells Android that nothing has been handled in that code block, so we are happy for Android to pass the event on to any other handlers that might be interested. But we don't want this to happen in our example. Instead, we need to return true when we handle the event, as follows (see Figure 9-12):

    public boolean onLongClick(View arg0) {
        TextView text = (TextView)findViewById(R.id.testText);
        text.setText("BUTTON HAS BEEN HELD. onLongClick EVENT PROCESSED.");
        return true;
    }

This tells Android that we handled the event successfully, and sets the text that we wanted.

Returning a true flag from our handled onLongClick() method

Figure 9.12. Returning a true flag from our handled onLongClick() method

Some of the event handlers return a Boolean (true or false value) to tell the calling code whether or not your listener has handled the code (or consumed the event as the programming terminology goes). So return true if you have handled the event (in our case, setText() has been done) and processing should stop here. Return false if you have not handled it or if you want the event to bubble up—that is, to be passed to other event handlers.

Now compile and run our OnLongClick app version. It works perfectly. A click displays the proper message and stays on the screen, and a long-click displays the proper message that stays on the screen until a short-click changes it.

Now let's add an onKeyListener and trap some keystroke events.

Keyboard Event Listeners: onKeyUp and onKeyDown

Events that will become familiar to you in Android app programming are onKey or onKeyUp (key released) and onKeyDown (key pressed down).

These events are commonly used for games and to implement shortcuts in your application, much like the F5 shortcut we use for Refresh or the F3 shortcut we use for Open.

To show how easy the keyboard event listeners are to implement, we are going to go back to our bootstrap code (the code that Android wrote for us in the beginning of this chapter) and add a couple lines to our main.xml file and our Java code to listen for a key event (the Enter key, of course). In other words, we are starting from scratch with a blank activity.

Adding the XML for Keyboard Events

First, let's go into our TextView object and add in a pre-Enter key "BEFORE KEYSTROKE DETECTED TEXT!" string, as well as a brighter color and larger text size. Here is the XML markup for our main.xml file (see Figure 9-13):

<?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:id="@+id/testText"
                       android:text="BEFORE KEYSTROKE DETECTED TEXT!"
                       android:textColor="#FFDDAA"
                       android:textSize="19px"/>

</LinearLayout>
Adding our TextView attributes in the main.xml file

Figure 9.13. Adding our TextView attributes in the main.xml file

Adding the Java for Keyboard Events

In our HandlerExample.java file, we want to add two simple import statements and two basic blocks of code to allow us to handle keyboard events via the OnKeyDown handler. We will add about a dozen lines of code to be able to handle key events.

Here is the code, including the import statements and onCreate() method that was written for us by Eclipse (see Figure 9-14):

package event.handling;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.TextView;

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

        public boolean onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                            textUpdate();
                            return true;
                    }
                        return false;
                }

        public void textUpdate() {
                TextView text = (TextView)findViewById(R.id.testText);
                text.setText("ENTER KEY PRESSED!");
        }
}
Adding an onKeyDown listener to our Java code

Figure 9.14. Adding an onKeyDown listener to our Java code

We need to import android.view.KeyEvent for the onKeyDown handler (first code block) and import android.widget.TextView for the textUpdate() method that we write in our second code block.

We leave the class declaration and onCreate() block of code (after the import statements) exactly as is.

The first block of code we write is the onKeyDown handler, which is a public method that returns a Boolean value that tells us if the event was handled (true) or not handled and needs to be passed along (false). The onKeyDown() method takes two parameters: the keyCode (the key that was pressed) and details of the event (event).

Our program logic inside the onKeyDown handler looks at the keyCode passed into the handler. If it is equal to the Enter key, signified by the KEYCODE_ENTER constant, it runs the textUpdate() method, and then returns true to signify we handled the event. Otherwise, onKeyDown() returns false to signify that an event was not handled.

This is the first time we have written our own method: the textUpdate() method that is called from inside onKeyDown(). This demonstrates some standard Java programming. The two lines of code that are in the textUpdate() routine could have been written where the textUpdate(); line of code is inside the onKeyDown() handler:

public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    TextView text = (TextView)findViewById(R.id.testText);
                    text.setText("ENTER KEY PRESSED!");
                    return true;
            }
                return false;
        }

This means that the textUpdate() method can contain all the things you want to do when someone clicks the Enter key. You can use this method, rather than putting them all inside the onKeyDown handler, where they could be in among actions for other keys. This makes things more organized and modular, and really comes in handy when you get into more complex code constructs.

Now compile and run the application.

You'll see a text field that says "BEFORE KEYSTROKE DETECTED TEXT!" that changes after you click the Enter key in the emulator.

Tip

If you want to detect a range of keystrokes and send them to different custom methods, a good programming construct to use is the switch construct, which allows you to outline different cases from which to select. We used switch in Chapter 7's examples.

Context Menus in Android: onCreateContextMenu

The concept of the context menu is a very clever one. Unfortunately, it is often underutilized both in PC and smartphone applications.

A context menu provides quick and easy access to all methods related to a UI object.

For instance, when I right-click here in my word processor, I get a context-sensitive menu with options for cut, copy, paste, font, paragraph, bullets, hyperlink, lookup, synonyms, and translate.

The context menu in Android is always accessed by a LongClick event (covered earlier in the chapter), just as on a PC it is accessed via a right-click.

To demonstrate, we will add context menus to this chapter's example project. We'll add two classes, along with two custom methods, to implement our context menus. We'll are take a look at the Android Toast widget, which is handy to use to blast quick little messages to the user. This way, you don't need to use a full-blown dialog implementation, as we did in Chapter 7.

Adding the XML for Context Menus

First, let's add a Button tag to our main.xmlLinearLayout so that we have a UI element (button) to long-click on.

  1. Click the main.xml tab, and then click the Layout tab at the bottom of that pane. Now add a Button view to the pane under the TextView.

  2. Once the button appears under your text, switch back into XML editing mode via the main.xml tab at the bottom of the pane. Now we'll edit our Button tag attributes. The first one is android:text. Let's change that to "Long-Click Here to Access Context Menu" and change our ID from Button01 to contextButton.

  3. Let's also center our button using the android:layout_gravity = "center" attribute, as we have done previously. But let's do it a different way this time. Put your cursor at the end of the android:id tag after the end quote and hit Return to put the attribute on its own line. Type in android:, and then wait.

  4. Up will pop a little dialog listing every attribute that can be used in the Button tag. This represents more work being done for us. Double-click android:layout_gravity to select it. Then type = and wait again.

  5. Again, a little dialog pops up, showing every value that can be used with android:layout_gravity. Double-click center, and you have the tag attribute written for you. (Make sure to use android:layout_gravity and not android:gravity, or it will not work.)

Here is what your XML tags should look like (see Figure 9-15):

<?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:id="@+id/testText"
             android:text="BEFORE KEYSTROKE DETECTED TEXT!"
             android:textColor="#FFDDAA"
             android:textSize="19px"/>

           <Button android:text="Long-Click Here to Access ContextMenu"
                      android:id="@+id/contextButton"
                      android:layout_gravity="center"
                      android:layout_width="wrap_content"
                      android:layout_height="wrap_content"/>
</LinearLayout>

We will reference the testText and contextButton inside our Java code.

Adding a Button object in our main.xml file to receive the long-click event

Figure 9.15. Adding a Button object in our main.xml file to receive the long-click event

Adding the Java for Context Menus

The main two Java methods that we override are onCreateContextMenu() and onContextItemSelected(), which replace Android's default methods of this same name. The use of the super object in the first one allows us to reference a method in the parent class that we are overriding. Note that overriding does not replace a class; it just allows us to customize it, leaving the original class that was extended intact and usable.

Now let's add the code for our onContextMenu event handling in HandlerExamples.java. We'll add the new code in with the previous code that we wrote in the onKey() section in order to handle onKeyDown events.

First, we need to use an import statement to import the Java classes that we are going to reference in the code we are about to write. Three of the six are related to our UI elements (android.view.View, android.widget.Button, and android.widget.Toast), and the other three are related to our implementation of our LongClick context menu.

import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.Button;
import android.widget.Toast;

ContextMenu contains the methods that are related to the top level of the menu, such as what it is called, how it looks, and so forth. ContextMenuInfo relates to the information about any one given ContextMenu, which is really a collection of options. Within that container or level, we have the MenuItems, which are their own level of objects. Each MenuItem can have a name and styling, and can call methods once it is selected.

Now, let's see how Android attaches to a ContextMenu.

First, we need to add two key lines of code to our onCreate() method for our activity. The first declares and establishes a Button object, which we call contextButton and which we find by its contextButton ID from the main.xml file. The next line of code wires our newly created contextButtonButton object to the ContextMenu system in Android.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Button contextButton = (Button) findViewById(R.id.contextButton);
    registerForContextMenu(contextButton);
}

Tip

When I first started working with Android, I wondered which class contained the registerForContextMenu() method. To again demonstrate how to use Eclipse as a learning tool, I'll tell you how to answer a question like that. Place your cursor over the method you are interested in, and Eclipse will pop up a box full of information about the method in question, which includes the class that contains the method.

Now let's get into our custom logic for creating our ContextMenu and its content. The first of the two menu methods is onCreateContextMenu(), which takes three objects as parameters:

  • The ContextMenu object named menu

  • The View object that called it

  • The ContextMenuInfo object named menuInfo, which contains information about the menu configuration

The first line of code inside this code block simply passes our three parameters up to the parent class, which is referenced via the super keyword.

public void onCreateContextMenu(ContextMenu menu, View view,
                                ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, view, menuInfo);

The next three lines of code call methods against or on the menu object, which is of type ContextMenu. This code is configuring our top-level ContextMenu object by giving it a title using menu.setHeaderTitle() and adding two menu items via the two menu.add() methods.

public void onCreateContextMenu(ContextMenu menu, View view,
                                ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, view, menuInfo);
    menu.setHeaderTitle("Android Context Menu");
    menu.add(0, view.getId(), 0, "Invoke Context Function 1");
    menu.add(0, view.getId(), 0, "Invoke Context Function 2");
}

The second context menu method is onContextItemSelected(), which is passed a single parameter of type MenuItem named item. Note that this method has a Boolean return type, which means we need to return a true (handled) or false (not done yet) reply.

To start with, we have an if-then-else loop that compares the title of each MenuItem to a string. If the title matches, it runs the appropriate contextFunction1 or contextFunction2 (which we will code next).

public boolean onContextItemSelected(MenuItem item) {
    if(item.getTitle()=="Invoke Context Function 1") {
            contextFunction1(item.getItemId());
    }
    else if(item.getTitle()=="Invoke Context Function 2"){
                                   contextFunction2(item.getItemId());
    }
    else {
                return false;
}
return true;
}

Recall that the first code after the if in parentheses is the condition. It reads, "If the title that we are getting from the item object is equal to the text string "Invoke Context Function 1", then perform the statements in the curly braces that follow this conditional statement."

Note

Remember that == means is equal to, and = means set the value of a variable or constant.

If this does not equate to true for the first if condition, then the next else block is encountered, along with a second (nested) if statement that is almost completely identical to the first, except that it is looking for the 2 option rather than 1. If this is also not satisfied or matching, the second else returns a false from the method to the calling code, telling it, "Sorry, no menu options here that match that!" If one of the if conditions is met, the true that is under the conditional code block is returned, because we have not jumped out of the method by returning a value yet.

Now we need to write our own methods for the two options, which we'll call contextFunction1() and contextFunction2(). We declare the first method as public and as void, as it does not return any values. It simply carries out a task with no result to report back. We name the method contextFunction1() and define one integer data parameter to pass, in this case an ID.

public void contextFunction1(int id){

Inside this method, we make a call to the Toast widget, which allows us to send brief messages to our end users during their use of the application. To do this, we use the makeText() method and access it directly from the Toast class via the following one (admittedly dense) line of code:

Toast.makeText(this, "function 1 invoked!", Toast.LENGTH_SHORT).show();

This is another one of those lines of code that does several things with a single construct. Once you get really good at programming, this type of coding becomes a really nice thing.

So we call the makeText() method and pass it three parameters:

  • The activity that is running this Toast alert

  • What the message should be

  • How long to show the Toast pop-up

After the Toast.makeText(), another show() is appended. This displays the message we just specified with makeText(). One line of code does everything. And the best part is you can now use this code to pop up little messages to your users whenever you want to do that.

No, the context menu stuff that we did earlier has nothing to do with this one-line Toast construct, which will send a message to your screen anyplace in your code. Some people use this for debugging, with messages like, "Setting X variable to 7" or similar, so that you can see on the screen a visual progress through the code logic.

After our contextFunction2 code construct, which is similar to contextFunction1, we have our key event handlers from the previous section working at the same time as our ContextMenu.

The entire body of code in HandlerExamples.java should now look like the following (see Figure 9-16).

package event.handling;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.TextView;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.Button;
import android.widget.Toast;

public class HandlerExamples extends Activity {

    @Override  /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button contextButton = (Button) findViewById(R.id.contextButton);
        registerForContextMenu(contextButton);
    }

    @Override  /** Override Parent Class for this Application */
    public void onCreateContextMenu(ContextMenu menu, View view,
                                    ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, view, menuInfo);
        menu.setHeaderTitle("Android Context Menu");
        menu.add(0, view.getId(), 0, "Invoke Context Function 1");
        menu.add(0, view.getId(), 0, "Invoke Context Function 2");
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if(item.getTitle()=="Invoke Context Function 1") {
                contextFunction1(item.getItemId());
        }
        else if(item.getTitle()=="Invoke Context Function 2"){
                contextFunction2(item.getItemId());
        }
        else {
                return false;
        }
        return true;
   }

   public void contextFunction1(int id){
       Toast.makeText(this, "function 1 invoked!", Toast.LENGTH_SHORT).show();
   }

   public void contextFunction2(int id){
       Toast.makeText(this, "function 2 invoked!", Toast.LENGTH_SHORT).show();
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER) {
                textUpdate();
                return true;
    }
    return false;
}

public void textUpdate() {
        TextView text = (TextView)findViewById(R.id.testText);
        text.setText("ENTER KEY PRESSED!");
  }
}
Adding the Java code to implement a context menu in HandlerExamples.java

Figure 9.16. Adding the Java code to implement a context menu in HandlerExamples.java

Now let's run our code with Run As

Adding the Java code to implement a context menu in HandlerExamples.java
Running our application in the Android 1.5 emulator after adding a context menu

Figure 9.17. Running our application in the Android 1.5 emulator after adding a context menu

Controlling the Focus in Android

One of the most challenging aspects of UI design and programming is tracking and controlling the focus of your application. The focus is where the UI is paying attention, representing which UI element the user is presently dealing with.

The tough part about focus is that you can't always see it visually. Even as an end user, it is sometimes difficult to see where the focus is within an application. We have all experienced this with our computers at one time or another, most commonly in forms where the active cursor for a field moves from one field to another as the form is filled out or the Tab key is used to jump the focus from field to field.

It is even more difficult to control, track, and implement focus from a programming standpoint. Note that focus is not something that you need to specifically worry about (Android handles it automatically), unless it is somehow tripping up your application's user experience.

Android has an internal algorithm that decides how to hop from one UI element (View) to another based on which View is closest to the previous View, but you can also control how the focus moves from one UI element to the next with your own custom code. Here, we will go over the basics in order to get you started and familiar with the concepts, in case you need to intervene with your own XML or Java code to manually control focus.

First, we will look at how to control focus via XML, as it is easier to understand and implement than the Java route. Later, we will go over Java methods that allow you to take focus or otherwise control the focus based on what the user is doing in the application.

Adding the XML for Focus Control

To start, let's add a couple buttons to the UI we've been developing in this chapter and set the focus to do something that is not standard focus procedure in Android.

The easiest way to do this is to copy our existing Button tag in our main.xml file and paste it in twice right underneath our existing Button tag markup (see Figure 9-18).

<?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:id="@+id/testText"
             android:text="BEFORE KEYSTROKE DETECTED TEXT!"
             android:textColor="#FFDDAA"
             android:textSize="19px"/>

        <Button android:text="Long-Click Here to Access ContextMenu"
                   android:id="@+id/contextButton"
                   android:layout_gravity="center"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"/>

       <Button android:text="Second Button"
                   android:id="@+id/secondButton"
                   android:layout_gravity="center"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"/>

       <Button android:text="Third Button"
                   android:id="@+id/thirdButton"
                   android:layout_gravity="center"
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"/>
</LinearLayout>
Adding UI buttons to the main.xml file

Figure 9.18. Adding UI buttons to the main.xml file

To make our Button tags unique, we also need to rename their IDs to secondButton and thirdButton. This way, we can access them in our Java code and also change their display text to reflect that they are the second and third buttons, respectively.

We will leave all of the other Button tag attributes for scaling and centering the same.

Now we will add our android:nextFocus attributes, so that we have control over which UI elements our focus jumps to and from when the user navigates the UI with the arrow keys on the front of the smartphone.

For the existing contextButton tag attributes, we want to add an android:nextFocusUp attribute and point it to the third button. Then, if users hit the up arrow on their Android smartphone when they are on the first button, it will cycle back down to the last button.

Since the ID of the third button is thirdButton, this tag attribute will read as follows:

android:nextFocusUp="@+id/thirdButton"

This is done in order to reference the third button tag we have defined in our XML markup here as the destination UI element for the up arrow focus to go to if users hit the up navigation arrow when they are on (have focus on) the first UI button (contextButton from our prior example).

To control advancement of focus from the contextButton to the secondButton button, we add this:

android:nextFocusDown="@+id/secondButton"

Now we have defined all of the focus movements that can happen for the contextButton, and we are ready to define the focus movements for the next two buttons.

This will be a very similar process. In fact, you can simply cut and paste the two lines of code that you wrote for the contextButton tag and change the ID attributes after you paste them into the two new Button tags.

For the second Button tag, we will add in another two android:nextFocus attributes. This time, these point to the buttons immediately above and below the second button, so this one is the easiest. The code looks as follows:

android:nextFocusUp="@+id/contextButton"
android:nextFocusDown="@+id/thirdButton"

For the third Button tag, we will add in another two android:nextFocus attributes, which finally point to the buttons immediately above and back up to the top button in our loop of buttons, as follows:

android:nextFocusUp="@+id/secondButton"
android:nextFocusDown="@+id/contextButton"

The first attribute is pretty straightforward, as the secondButton button is above our third button. For the nextFocusDown attribute, since there is no button underneath the third button, we actually want the focus to wrap, or loop back, to our first contextButton button, so that is the ID we use in the android:nextFocusDown attribute that we add to the final Button tag.

Note

There are nextFocusLeft and nextFocusRight attributes available (one for each arrow key) if you are using a horizontal LinearLayout tag attribute, for instance.

Here are the three blocks of nextFocus attributes that we have added to our three buttons so that you can check your work (see Figure 9-19):

<Button android:text="Long-Click Here to Access ContextMenu"
           android:id="@+id/contextButton"
           android:nextFocusUp="@+id/thirdButton"
           android:nextFocusDown="@+id/secondButton"
           android:layout_gravity="center"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>

<Button android:text="Second Button"
           android:id="@+id/secondButton"
android:nextFocusUp="@+id/contextButton"
           android:nextFocusDown="@+id/thirdButton"

           android:layout_gravity="center"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>

<Button android:text="Third Button"
           android:id="@+id/thirdButton"
           android:nextFocusUp="@+id/secondButton"
           android:nextFocusDown="@+id/contextButton"
           android:layout_gravity="center"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>
Controlling the focus via XML mark-up in main.xml

Figure 9.19. Controlling the focus via XML mark-up in main.xml

Adding the Java for Focus Control

Now let's declare the two new buttons we defined in our main.xml markup in our Java code, and point them toward our ContextMenu code that we wrote in the previous section, so that they actually do something useful.

Here are the four new lines of code that we need to write to support these new buttons (see Figure 9-20):

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Button secondButton = (Button) findViewById(R.id.secondButton);
    registerForContextMenu(secondButton);
    Button thirdButton = (Button) findViewById(R.id.thirdButton);
    registerForContextMenu(thirdButton);
    Button contextButton = (Button) findViewById(R.id.contextButton);
    registerForContextMenu(contextButton);
}
Registering our buttons for the context menu in HandlerExamples.java

Figure 9.20. Registering our buttons for the context menu in HandlerExamples.java

To implement this in the quickest fashion, select the two lines of code that define and point our contextButton object to the registerForContextMenu() method, and paste them twice above or below the original two lines of code.

Change the contextButton reference to secondButton in the first two lines, and to thirdButton in the last two lines. You have now declared all three buttons and set them to actually do something in your code.

Now let's use our familiar Run As

Registering our buttons for the context menu in HandlerExamples.java
Running our sample application in the Android 1.5 emulator after adding focus control

Figure 9.21. Running our sample application in the Android 1.5 emulator after adding focus control

You will notice now that when you compile and run this code, all three buttons will call up a ContextMenu. In your own apps, you may want all (or many) of your UI elements to bring up the same context menu selections (say the application default context menu), and this is the way to do that using very few lines of code.

It is important to test your applications vigorously, as some bugs will show up only after the features have been used already once or twice.

To test this application, long-click each of the buttons and select either option. Everything should work as expected and pull up the context menu. To see the cycling focus that we have implemented, use the up or down arrow/keys on the bottom of the Android smartphone (in this case, on the emulator) to cycle the focus among the buttons (focus is shown in orange). You will notice no matter which direction you choose, the focus cycles or loops through the buttons correctly.

Note

Remember that Android will handle focus for you as a matter of routine. This includes jumping between UI elements on the screen and even jumping to the next logical UI element if a UI element (a View object) is hidden (or shown) or removed (or added) as a matter of the application programming logic.

Setting Focus Availability

View objects can be defined (in XML or Java) to be able to accept (or deny) focus using the isFocusable() method or the android:focusable (XML) attribute. If you define a View (UI object) to be focusable (or not focusable) in XML, and then want to change this later at runtime (while your application is running), there is also a setFocusable() method that can flip this (Boolean) switch. These focus methods control focus navigation via the smartphone navigation key hardware.

There are separate methods to control the focus in relation to the touchscreen, and these are named very similarly: isFocusableInTouchMode() and setFocusableInTouchMode(). For XML markup coding, you would use the format android:focusableInTouchMode, similar to nontouch focus.

Finally, if you simply want to ascertain if there has been a change of focus on a UI object, you can use the onFocusChanged() method. This method can be called to find out if there is a change in state from true to false, or focused to not focused, that you can use in more advanced programming endeavors that watch focus even more closely. With this method, your software can essentially watch what the user is doing with your UI and respond accordingly. As you can see, Android gives us a huge dose of control over our application's focus.

Summary

This chapter has covered some important and advanced concepts in Java programming, as well as in Android app development. The topics ranged from setting up event listeners and event handlers to controlling the focus of your UI design as the user moves through it, which is a part of your user experience design.

You now know how to handle clicks via navigation keys or touchscreen, long-clicks, and keyboard use. We even covered some advanced features like context menus, the Toast system for user message notifications, and controlling the focus in your XML or Java code, or via both.

We covered a lot of important material in this chapter, so be sure to review it. It includes some clever and new ways to use the Eclipse IDE as well, and that is also important to master by the time you are finished with this book.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
98.82.120.188