Adding Interactivity: Handling UI Events
In this chapter, we will explore how to wire 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. You worked briefly with event handling in the last section on Dialogs in Chapter Seven, so we’ll give you the foundation in this chapter to be able to handle all types of events within the Android OS.
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 an Android device’s hardware, whether it be a smartphone handset, an iTV set remote control, or a tablet or e-reader touchscreen.
Handling UI Events via the View Class
Each of the UI elements in your application is a View object widget 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 well organized. Each of these View objects keeps track of its own user-input events via event handlers.
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 widget is touched, an onClick() 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 via custom Java code that you write depending on what you want that object to do.
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 exact method name specifically within your class, and have it do something via your own custom program logic.
Because your UI design is made of a collection of View objects (UI widgets) laid out 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 application 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 within your application program logic.
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 object (widget).
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 exotic 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 or longer |
onKey() | View.OnKeyListener | Press/release of key on phone or iTV remote |
onTouch() | View.OnTouchListener | Touch, release, or gesture events |
onFocusChange() | View.OnFocusChange | Focus change |
onCreateContextMenu() | View.OnCreateContextMenuListener | 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(). The onFocusChange() method 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, and is used for context-sensitive menus such as those right-click menus that we have been using frequently in the Eclipse Project Explorer pane.
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 setListener() method, as you’ll see in the next section.
In the rest of this chapter, you’ll learn how to leverage the primary event listeners in Android, so that you can make your application interactive and useful.
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 arg0) {
//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 arg0) 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 arg0, 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 “arg0.” The first argument used in coding terminology would be called argument zero (arg0) as in computers we start counting from zero and not from one like we learned in school. The second argument used would be arg1 and so on.
How any given onClick handler handles a click event is up to the code inside of 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 event 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 XML declaration of the Button UI object in our activity_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 fewer lines of code. This is normally how you will want to do things in your Android applications programming activities, so this is how we are going to do them in all of the examples in this chapter.
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 MainActivity 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 EventHandling project folder and implement a button and an 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 a top-level button so that when it is clicked, the text in a TextView below it changes.
In Eclipse, close the GraphicDesign project folder (right-click on 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. Now we’re ready to create our new Android Application Project.
Figure 9-1 . Creating the EventHandling Android project using the New Android Application series of dialogs
Editing the MainActivity.java File
Now let’s edit the java code:
Figure 9-2 . Editing MainActivity.java to implement the onClickListener functionality
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
}
Note Because the onClick code uses a View object, Eclipse imports android.view.View, which is shown at the top of the file.
Figure 9-3 . Implementing an OnClickListener 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 are inside the development environment. That is why I am showing you some of these automatic coding functions here in this book, rather than writing perfect lines of code every time in exactly the correct order. One of the things you need to master is your process of working within the Eclipse IDE and paying close attention to what it is telling you. If you mouse-over or click on the things it flags or highlights, you will get pop-up windows as well as tool-tips containing additional helpful information.
Button button = (Button)findViewById(R.id.button1);
button.setOnClickListener(this);
Figure 9-4 . Adding the Button widget and setOnClickListener() method in Eclipse
This is all shown in Figure 9-4, along with the import android.widget.Button; statement that we need to use the Button in our code. Next, we will add the Button UI element to our RelativeLayout, using the Eclipse Graphical Layout Editor (GLE), and then hand modify the XML code to place it above our text, so that it’s more intuitive for the end-users. This will remove any flags in our Java code editing pane that tell us that a button1 XML object definition does not yet exist! The first step in this process is shown in Figure 9-5.
Figure 9-5 . Creating a Button in /res/layout/activity_main.xml via the Graphical Layout Editor in Eclipse
Editing the activity_main.xml File
Now it’s time to set up the XML mark-up in our activity_main.xml file in the /res/layout/ folder.
Figure 9-6 . Editing the button_caption string property to read CLICK TO GENERATE EVENT
Figure 9-7 . The strings.xml file after the EventHandling project’s related text string tags have been added
<RelativeLayout 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" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="@string/button_caption" />
<TextView
android:id="@+id/textmessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/text_message"
tools:context=".MainActivity" />
</RelativeLayout>
Figure 9-8 . Editing Button and TextView object parameters in our /res/layout/activity_main.xml file
Now let’s go back into our Java code.
public void onClick(View arg0) {
TextView text = (TextView)findViewById(R.id.textmessage);
text.setText("BUTTON HAS BEEN CLICKED. EVENT PROCESSED.");
}
Figure 9-9 . Defining the onClick() event handler code for a TextView in MainActivity.java
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. Finally, we add an Import android.widget.TextView; statement, or we have Eclipse do it for us, via the error-warning icon mouse-over work process. You’re getting pretty good at this, aren’t you?
Running the Event Handling Examples App in the Emulator
To run this example, right-click your EventHandling folder in the Package Explorer pane and select Run As Android Application. We have our first UI that responds to the most common event handler out there: the onClick handler. Figure 9-10 shows the results.
Figure 9-10 . Running the onClick( ) event handling example in the Android 4.1 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. Gestures became available in Android 1.6, and thus they do work in Android 2.2, which is the recommended minimum SDK version that we are developing for in this book to provide a wider audience of user compatible devices. However, coding to the Gestures API is a bit too advanced for this Absolute Beginner book.
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 well be wise to use the onClick() method for your apps for most UI operations for the widest compatibility, and use onTouch() specifically when working with more advanced touch events such as gestures that involve only the touchscreen.
Because we have already covered implementing onClick(), we’ll continue here with the other important event handlers. These are the ones you will use most frequently in your application design and coding: onLongClick, OnKeyUp, onKeyDown, onCreateContextMenu, and onFocus, along with onClick as the most often used.
Android’s Right-Click Equivalent: onLongClick
After OnClick, OnLongClick is the next most used interface event. It is generated by most input hardware and is also the basis for the context menu in Android.
The onLongClick() method works in the following scenarios:
Any of these will generate an OnLongClick event for whatever UI widget currently has the focus or, in the case of a touchscreen, is underneath the area of the screen being touched (or long-touched, in this instance!).
Because 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 EventHandling example code. This also will 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.
public boolean onLongClick(View arg0) {
// TODO Auto-generated method stub
return false;
}
Figure 9-11 . Implementing an OnLongClick listener in the MainActivity.java class
Figure 9-12 . Attaching an OnLongClick listener to our Button object in MainActivity.java
public boolean onLongClick(View arg0) {
TextView text = (TextView)findViewById(R.id.textmessage);
text.setText("BUTTON HAS BEEN HELD. onLongClick EVENT PROCESSED.");
return true;
}
Figure 9-13 . Returning a true flag from our EventHandling Project’s onLongClick() method
This tells Android that we have handled the event successfully, and sets the text that we wanted.
Some of the event handlers return a Boolean (true or false value) to tell the calling code whether 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 on (up) to other event handlers, even if it has been handled.
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 back again.
Now let’s add an onKeyListener and trap some keystroke events.
Key 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). Key is short for keyboard, but more and more Android devices do not feature a traditional computer keyboard (unless you have a Bluetooth external keyboard as an accessory) but some still do have smaller sets of keys (a mini keyboard) or in the case of GoogleTV possibly some sort of remote control that has keys. Some iTVs may even come with a wireless keyboard, so this is an important event to handle for many Android applications.
These onKey() events are commonly used for games and to implement keyboard or keypad shortcuts in your application, much like the F5 shortcut that we use in Eclipse 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 add a couple more lines (OK, a dozen more) to our Java code to listen for a key event (the center keypad hardware key, found on every Android Device).
Figure 9-14 . Having Eclipse Add our KeyEvent import statement for us
Adding the Java for Keyboard Events
In our MainActivity.java file, we want to add one simple import statement and two basic blocks of code that will allow us to handle keyboard events via the OnKeyDown handler. We will only add about a dozen lines of code to be able to handle key events in our current EventHandling Project. We are using one EventHandling project for all our code in this chapter to show how all the different types of Android Event Handling can coexist peacefully in a single application and so that you get experience writing more complex apps with more than one hundred lines of code.
Here is the code that we will need to add to the bottom of our current EventHandling Project’s Java code (see Figure 9-15):
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
textUpdate();
return true;
}
return false;
}
public void textUpdate() {
TextView text = (TextView)findViewById(R.id.textmessage);
text.setText("CENTER KEYPAD KEY PRESSED");
}
}
Figure 9-15 . Adding an onKeyDown listener and textUpdate( ) method to our MainActivity class Java code
We need to import android.view.KeyEvent for the onKeyDown handler (first code block) or we can let Eclipse do it for us while we are writing the onKeyDown handler method (this is shown in Figure 9-14 ). We don’t need to import android.widget.TextView for the textUpdate() method that we write in our second code block, because we are already using a TextView widget in our current application, and will use that TextView to display the text that our onKeyDown( ) handler sets.
We will leave the class declaration and the onCreate() and onClick event handling blocks of code exactly as they are.
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 first looks at the keyCode passed into the handler. If it is equal to the center keypad hardware keyCode, signified by the KEYCODE_DPAD_CENTER constant, it runs the textUpdate() method, and then returns true to signify that 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, like this:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
TextView text = (TextView)findViewById(R.id.textmessage);
text.setText("CENTER KEYPAD KEY PRESSED");
return true;
}
return false;
}
In actual programming practice the textUpdate() method can (and will) contain all of the various things that you want to do when someone clicks the center keypad hardware key. You can use this method to contain all of your code statements, rather than putting them all inside the onKeyDown handler, where they could be in among actions for other keys. Doing it this way simply makes things more organized and modular, and really comes in very handy when you get into more complex code constructs, so we are teaching you to write your own method (and call it) here for this reason.
Finally, you might be wondering why there is an onKeyUp and onKeyDown, rather than just one onKey listener. As you might imagine, onKeyDown is sent when the key is pressed down, and onKeyUp is sent when the key is released. The reason both are supported is because some applications need to know when a given key is being held down (like the repeating characters feature in a word processor) and for how long, thus in that application the code would look for both the onKeyDown (repeat these functions while onKeyDown) and onKeyUp (stop doing the functions now) events, and handle them with different code.
Now let’s compile and run the application.
You’ll see a text field that says “NO EVENTS RECEIVED YET” that changes after you click down the center keypad hardware key in the emulator (shown on the right in blue in Figure 9-16).
Figure 9-16 . Pressing center keypad Button in 4.1 Emulator to send onKeyDown event
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 in PC, tablet, iTV, e-reader, and smartphone applications. Fortunately, Android provides us all the prebuilt classes we need, as well as a special event, to make implementing context-sensitive menus in our apps as easy as possible to code. Why does Android do this for us? It is because Google has a vested interest in making their Android OS and apps as easy to use and as standardized as possible.
A context menu should provide quick and easy access to all application functions related to a given 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, numbering, styles, hyperlinks, lookup, synonyms, and translate.
The context menu in Android is always accessed by the LongClick event (covered earlier in the chapter), just as on a PC it is always accessed via a right-click of the mouse.
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 take a look at the Android Toast widget, which is very 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 learned about in Chapter 7, and in this way we can learn about a different and new widget at the same time.
Adding the XML for Context Menus
First, let’s add a second Button tag to our activity_main.xmlRelativeLayout so that we have a UI element (button) to use to access our Context Menu.
Here is what your XML tags should look like (see Figure 9-17):
<RelativeLayout
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">
<Button
android:id="@+id/button1"
android:text="@string/button_caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:id="@+id/contextButton" />
<TextView
android:id="@+id/textmessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontaltrue"
android:layout_centerVertical="true"
android:text="@string/text_message"
tools:context=".MainActivity" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textmessage"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="@string/context_button_caption" />
</RelativeLayout>
We will reference the textmessage and button2 inside our Java code.
Figure 9-17 . Adding a second Button object in our activity_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 the 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. This also leaves us a lot less code to write, as none of the code written in (for) the superclass is revisited.
Now let’s add the code for our onContextMenu event handling in MainActivity.java. We’ll add the new code in with the previous code that we wrote, right before the onKey() section we added 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, or we can just write the code into Eclipse and let Eclipse add the import statements. Three of the six relevant import statements for this code 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. Some of the import statements relating to what we are going to do with Context Menus are already in our code so far. The four that will need to be added (or will be added by Eclipse, if you forget to add them) are:
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
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 our UI element 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 Button2 and find by its button2 ID from the activity_main.xml file. The next line of code wires our newly created Button2 Button object to the ContextMenu system in Android.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button Button2 = (Button) findViewById(R.id.button2);
registerForContextMenu(Button2);
}
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 a box full of information about the method in question will pop up in Eclipse including the class that contains the method. In Eclipse you will find that mouse-over and right-click are very helpful in researching what is going on, especially in relation to error flags and warning icons (left-click on these as well to reveal potential solutions).
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 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. The next three lines of code call methods against (or invoke methods 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");
}
Shown in Figure 9-18 is the screenshot of Eclipse after adding the code, and the screenshot shows how Eclipse flags and then suggests and even adds our import statements for the ContextMenu and ContextMenuInfo classes. You should be getting used to this work process by now!
Figure 9-18 . Adding onCreateContextMenu( ) and importing ContextMenu and ContextMenuInfo classes via mouse-over
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().equals("Invoke Context Function 1")) {
contextFunction1(item.getItemId());
}
else if(item.getTitle().equals("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 that this could also be written as: if(item.getTitle( )==“Invoke Content Function 1”); however using the .equals() method is considered a better programming practice when comparing String values.
Note Remember that == (two equal signs) means is equal to, and compares two integer or string values to each other, and = (one equal sign) means to set the value of a variable or constant to another value, so that is setting a value rather than simply comparing two values to see if they are the same or if they are different. What we are using here for ContextMenu is the .equals() method, which is considered “Best Practices” for comparing String values, and can also be chained, so we are using it here to show you both Java comparison operation solutions in a single chapter!
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 also is 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 condition is met, the true, which is under the conditional code block is returned because we have not jumped out of the method by returning a value yet.
Type this code into Eclipse and when Eclipse flags the MenuItem class as needed to be imported, use the usual work process shown in Figure 9-19 to add the import statement, and then we’ll be ready to write our own custom methods that are called in the onContextMenuItemSelected() method that we just wrote.
Figure 9-19 . Adding an onContextItemSelected() method and importing a MenuItem class
Now we need to write our own methods for the two options, which we’ll call contextFunction1() and contextFunction2(). We declare these methods as public and as void, as they do not return any values. They simply carry out a task with no result to report back. We name the first 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 “chaining” lines of code we talked about earlier in the book that does several things within a single code construct. Once you get really good at programming, this type of coding becomes a really great way to write more “compact” or dense code.
So we call the .makeText() method and pass it three parameters:
After the Toast.makeText(), another .show() is appended. This displays (shows onscreen) the message that we just specified with makeText(). One line of code does everything. And the best part is, you can now use this same line of code to pop up little messages to your users whenever you want to do so. Grab that glass of wine on your left, and let’s make a Toast to Toast!
Note that the context menu code that we learned about earlier has nothing to do with this one-line Toast construct, which will send a message to your screen anyplace you put it in your code. Some programmers use this for debugging, with messages like, “Setting X variable to 7 at (System Time)” or similar, so that you can see on the screen a visual progress of your app running through its code logic.
After our contextFunction2 code construct, which is very similar to contextFunction1, we have our key event handlers from the previous section working at the same time as our ContextMenu. Once you write these two methods into Eclipse, be sure to note the flags that tell you that you need an import statement to use the Toast class, and use the usual work process shown in Figure 9-20 to have Eclipse insert the import statements needed for you.
Figure 9-20 . Adding our two custom methods and having Eclipse import the Toast class for us
The entire body of code in MainActivity.java should now look like the following (see Figure 9-21).
package fifth.example.eventhandling;
import android.os.Bundle;
import android.app.Activity;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android. view.View.OnLongClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity Implements OnClickListener, OnLongClickListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
button.setOnLongClickListener(this);
Button button2 = (Button) findViewById(R.id.button2);
registerForContextMenu(button2);
}
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");
}
public boolean onContextItemSelected(MenuItem item) {
if(item.getTitle().equals("Invoke Context Function 1")) {
contextFunction1(item.getItemId());
}
else if(item.getTitle().equals("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 onCreateOptionsMenu (Menu menu) {...}
public void onClick (View arg0) {...}
public boolean onLongClick (View arg0) {...}
public boolean onKeyDown(int keyCode, KeyEvent event) {...}
public void textUpdate() {...}
}
Figure 9-21 . Adding the Java methods needed to implement a context menu in MainActivity.java
Now let’s run our code with Run As Android Application and see how it all works together. A long-click on the button brings up the context menu. A touch or click on one of the buttons highlights it, as shown in Figure 9-22. Once it is clicked, a Toast menu tells us our method has been run. Also notice that our previous section code for onKeyDown() as well as for both onClick() and for onLongClick() all still works perfectly together.
Figure 9-22 . Running our application in the Android 4.1 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, or focusing its attention currently, and this equates to representing which UI element the user is presently dealing with (or is using).
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 currently at within an application. We have all experienced this with our computers at one time or another, most commonly while we are filling out forms, where the active text entry cursor for a field moves or “jumps” from one field to another as the form is filled out, or as the Tab key is used to jump (or progress) the input focus from one field to the next field.
It is even more difficult to control, track, and implement focus from a programming standpoint. Note that focus is typically not something that you need to specifically worry about (Android handles it automatically), unless it is somehow tripping up your application’s user experience, or unless you want to implement finer or tighter control over how your users interface with your UI Design or Application’s UX (user experience).
Android has an internal algorithm that decides how it hops from one UI element (View or Widget) 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 widget to the next with your own custom code. Here we will go over the basics, 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 the focus inside of your application.
First, we will look at how to control focus via XML, as that is easier to understand and implement than the Java coding route. Later, we will go over which Java methods will 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 more buttons to the bottom of the UI we’ve been developing in this chapter, and then we’ll 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 button2 tag in our activity_main.xml file and paste it in twice right more underneath our existing button2 tag markup (see Figure 9-23). You can also use the Eclipse Graphical Layout Editor and drag a couple more button elements out onto the UI layout screen, if you’d rather do it that way.
Figure 9-23 . Adding a third and fourth UI button to the /res/layout/activity_main.xml file
< RelativeLayout
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">
<Button
android:id="@+id/button1"
android:text="@string/button_caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:id="@+id/contextButton" />
<TextView
android:id="@+id/textmessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontaltrue"
android:layout_centerVertical="true"
android:text="@string/text_message"
tools:context=".MainActivity" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textmessage"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="@string/context_button_caption" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/button2"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:text="@string/third_button_caption" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/button3"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:text="@string/fourth_button_caption" />
</RelativeLayout>
To make our Button tags unique, we also need to rename their IDs to button3 and button4, which if you use the Graphical Layout Editor will be done for you automatically, as will the centering and margin spacing of 10dp parameters. This way, we can access the buttons in our Java code and also change their display text to reflect that they are the third and fourth buttons, respectively. Make sure to add their names and values to the /res/values/strings.xml file via the <string> tag, as we did in the previous sections, and as is shown in Figure 9-27 later on in this exercise.
We will leave all of the other Button tag attributes for scaling and centering the same, and use a android:layout_marginTop value of 10dp to keep the bottom three buttons closer together.
Now we will add our android:nextFocus attributes, so that we will have control over which UI elements our focus jumps to and from when the user navigates the UI with the up and down arrow keys on the front of the smartphone, iTV remote, e-reader or tablet.
For the existing button1 tag attributes, we want to add an android:nextFocusUp attribute and point it to the fourth button. Then, if users hit the up arrow on their Android smartphone or tablet when they are on the first button, it will cycle back down to the last button, creating a seamless loop through the button stack.
Because the ID of the fourth button is button4, this tag attribute will read as follows:
android:nextFocusUp="@+id/button4"
This is done to reference the fourth 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 (button1 from our first onClick and onLongClick examples).
To control advancement of focus from the button1 to the button2 button, we add this:
android:nextFocusDown="@+id/button2"
Now we have defined all of the focus movements that can happen for the first (top) Button UI element button1, and we are ready to define the focus movements for the next three user interface 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 first Button (button1) tag and change the ID attributes after you paste them into the next three Button element tags for button2, button3, and button4.
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 and third button, so the middle two buttons are the easiest to code. Their code will look like the following:
android:nextFocusUp="@+id/button1"
android:nextFocusDown="@+id/button3"
android:nextFocusUp="@+id/button2"
android:nextFocusDown="@+id/button4"
For the fourth 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/button3"
android:nextFocusDown="@+id/button1"
The first attribute is pretty straightforward, as the button3 button is above our fourth button. For the nextFocusDown attribute because there is no button underneath the fourth button, we actually want the focus to wrap, or loop back, to our first button1 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, or if you have things arranged from side to side within a RelativeLayout container.
Here are the four blocks of nextFocus attributes that we added to our four buttons (we’ll omit the dozens of other parameters, so as to show only the focus-related coding) so that you can check your work (also see Figure 9-24 for the complete XML code):
<Button
android:id="@+id/button1"
android:nextFocusUp="@+id/button4"
android:nextFocusDown="@+id/button2"
(other button parameters) />
<Button
android:id="@+id/button2"
android:nextFocusUp="@+id/button1"
android:nextFocusDown="@+id/button3"
(other button parameters) />
<Button
android:id="@+id/button3"
android:nextFocusUp="@+id/button2"
android:nextFocusDown="@+id/button4"
(other button parameters) />
<Button
android:id="@+id/button4"
android:nextFocusUp="@+id/button3"
android:nextFocusDown="@+id/button1"
(other button parameters) />
Figure 9-24 . Controlling the focus via XML markup in /res/layout/activity_main.xml
Adding the Java for Focus Control
Now let’s declare the two new buttons we defined in our activity_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, and bring up a Context Menu when we long-click on them.
Here are the four new lines of code that we need to write to support these new buttons (see Figure 9-25), which go in the end part of our onCreate() method as follows:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
button.setOnLongClickListener(this);
Button Button2 = (Button) findViewById(R.id.button2);
registerForContextMenu(Button2);
Button Button3 = (Button) findViewById(R.id.button3);
registerForContextMenu(Button3);
Button Button4 = (Button) findViewById(R.id.button4);
registerForContextMenu(Button4);
}
Figure 9-25 . Registering our bottom two buttons for the context menu in MainActivity.java in Eclipse
To implement this in the quickest fashion, select the two lines of code that define and point our button2 object to the registerForContextMenu() method, and paste them twice below the original two lines of code.
Change the Button2 and button2 references to Button3 and button3 in the first two lines, and to Button4 and button4 in the last two lines. You have now declared all four buttons in Java and set them to actually do something in your code when they are either clicked or long-clicked on.
Now let’s use our familiar Run As Android Application work process to compile and run this application, as shown in Figure 9-6. It now traps or handles onKey events, onClick events, and onLongClick events as well as handling onContextMenu events, and additionally implements control of focus among the usable UI elements, namely the four buttons.
Figure 9-26 . Running our EventHandling project application in the Android 4.1 emulator after adding focus control
You will notice now that when you compile and run this code, all three buttons at the bottom of the app 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 that you would do that using just a very few lines of code.
If your code does not run for some reason, you can use the Eclipse Project Clean … option to “clean” your project. This regenerates the R.java class that lives in the /gen/ folder and that holds the generated (compiled) Java files for the application. Three key commands in Eclipse that you will use frequently are: Refresh, Validate, and Clean, usually in that order, to help Eclipse to troubleshoot code problems that you may be experiencing in the IDE.
The reason I mention this here is because when I first ran the focus code in this chapter, which I know is correct and bug-free, the activity would not run in the emulator, and I was getting an error in LogCat that said that I was “casting” a Button object into a TextView object. For this to have been true, I would have had to switch the FindById references for button1 (or button2, button3, or button4) with the FindById reference for textmessage in my Java code.
As this was not the case, I ran the Eclipse Project Clean … utility, which regenerated my R.java file, and fixed the problem for me. One of the tricky things about programming is that sometimes your code can be correct, and the code in the IDE or the Compiler (SDK) can have bugs in it too! Add to this the quick SDK revision cycles for Java and Android (between Version 1 and Version 2 of this book, Android went from SDK Level 3 to SDK Level 16) and programming becomes a “moving target” significantly more challenging than if it were inherently within a fixed or unchanging programming environment.
If the code still does not compile and run, you may have forgotten to add all of the string tags needed to the /res/values/strings.xml file as shown in Figure 9-27. Also shown at the bottom of the screenshot is the successful running of the app in the emulator under the Console Tab at the bottom of the IDE.
Figure 9-27 . Adding the Button captions in the strings.xml file and view of the Android Console tab
It is important to test your applications vigorously, as some bugs will show up only after all of the features have been used already once or twice. Let’s do that now.
To test the application, long-click on 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 in the middle of the Android smartphone or tablet (in this case, on the 4.1 emulator) to cycle the focus among the buttons (focus is shown in blue). You will notice no matter which direction (up or down arrow button) you choose, the focus cycles or loops through the buttons correctly and seamlessly.
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.
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. To switch Focus On .setFocusable(true); and Off would be .setFocusable(false); as you may have suspected. These focus methods control focus navigation via the smartphone, tablet, e-reader or iTV set 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 when the user is “on” each user interface element using onFocusChanged(true). As you can see, Android gives us a huge dose of control over our application’s focus.
Summary
This chapter has covered some very 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 invoking context-sensitive menus to more ethereal topics such as controlling the focus of your UI design as the user moves through it, all of which represent important parts of your user experience design—interactivity, predictability, and responsiveness.
You now know how to handle clicks via hardware navigation keys or via the touchscreen, as well as handling long-clicks and keyboard use. We even covered more advanced features such as context menus, the Toast system widget for brief user message notifications, and controlling the focus in your XML or Java code, or via both.
You coded an app that used all the primary event handlers in Android all in one codebase (well over a hundred lines of XML and Java code was written—congratulations on coding your first major project) with no errors, warnings, or crashes. You also learned about the Eclipse Project Clean … utility; this can help “Clean” or regenerate generated Java files in the project’s /gen/ folder as a way of “cleaning” (debugging) your code when there are no apparent logic or variable errors visible.
We covered a lot of important material in this chapter, so be sure to review it all again very soon. This chapter 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, so make sure to play around with the different “panes” in Eclipse such as the Properties, Overview, LogCat, and the Graphical Layout Editor. You should be exploring Eclipse (your IDE tool) as much as you are exploring the Java Classes, XML Tags, Android Libraries, and Method Functions.
98.82.120.188