Much has been covered in the previous chapters relating to the design of user interfaces for Android applications. However, an area that has yet to be covered involves how a user’s interaction with the user interface triggers the underlying activity to perform a task. In other words, from the previous chapters, we know how to create a user interface containing a button view but not how to make something happen within the application when the user touches it.
Therefore, this chapter’s primary objective is to provide an overview of event handling in Android applications together with an Android Studio-based example project.
Understanding Android Events
Android events can take various forms but are usually generated in response to an external action. The most common form of events, particularly for devices such as tablets and smartphones, involve some form of interaction with the touch screen. Such events fall into the category of input events.
The Android framework maintains an event queue into which events are placed as they occur. Events are then removed from the queue on a first-in, first-out (FIFO) basis. In the case of an input event, such as a touch on the screen, the event is passed to the view positioned at the location on the screen where the touch took place. In addition to the event notification, the view is also passed a range of information (depending on the event type) about the nature of the event, such as the coordinates of the point of contact between the user’s fingertip and the screen.
To handle an event that has been passed, the view must have an event listener in place. The Android View class, from which all user interface components are derived, contains a range of event listener interfaces, each containing an abstract declaration for a callback method. To be able to respond to an event of a particular type, a view must register the appropriate event listener and implement the corresponding callback. For example, if a button is to respond to a click event (the equivalent of the user touching and releasing the button view as though clicking on a physical button), it must both register the View.onClickListener event listener (via a call to the target view’s setOnClickListener() method) and implement the corresponding onClick() callback method. If a “click” event is detected on the screen at the location of the button view, the Android framework will call the onClick() method of that view when that event is removed from the event queue. It is, of course, within the implementation of the onClick() callback method that any tasks or other methods called in response to the button click should be performed.
Using the android:onClick Resource
Before exploring event listeners in more detail, it is worth noting that a shortcut is available when all that is required is for a callback method to be called when a user “clicks” on a button view in the user interface. Consider a user interface layout containing a button view named button1 with the requirement that when the user touches the button, a method called buttonClick() declared in the activity class is called. All that is required to implement this behavior is to write the buttonClick() method (which takes as an argument a reference to the view that triggered the click event) and add a single line to the declaration of the button view in the XML file. For example:
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="buttonClick"
android:text="Click me" />
Code language: HTML, XML (xml)
This provides a simple way to capture click events. It does not, however, provide the range of options offered by event handlers, which is the topic of the rest of this chapter. As outlined in later chapters, the onClick property also has limitations in layouts involving fragments. When working within Android Studio Layout Editor, the onClick property can be found and configured in the Attributes panel when a suitable view type is selected in the device screen layout.
Event Listeners and Callback Methods
In the example activity outlined later in this chapter, the steps involved in registering an event listener and implementing the callback method will be covered in detail. Before doing so, however, it is worth taking some time to outline the event listeners available in the Android framework and the callback methods associated with each one.
- onClickListener – Used to detect click style events whereby the user touches and then releases an area of the device display occupied by a view. Corresponds to the onClick() callback method, which is passed a reference to the view that received the event as an argument.
- onLongClickListener – Used to detect when the user maintains the touch over a view for an extended period. Corresponds to the onLongClick() callback method, which is passed as an argument the view that received the event.
- onTouchListener – Used to detect any contact with the touch screen, including individual or multiple touches and gesture motions. Corresponding with the onTouch() callback, this topic will be covered in greater detail in the chapter entitled “Android Touch and Multi-touch Event Handling”. The callback method is passed as arguments the view that received the event and a MotionEvent object.
- onCreateContextMenuListener – Listens for the creation of a context menu as the result of a long click. Corresponds to the onCreateContextMenu() callback method. The callback is passed the menu, the view that received the event and a menu context object.
- onFocusChangeListener – Detects when focus moves away from the current view due to interaction with a trackball or navigation key. Corresponds to the onFocusChange() callback method, which is passed the view that received the event and a Boolean value to indicate whether focus was gained or lost.
- onKeyListener – Used to detect when a key on a device is pressed while a view has focus. Corresponds to the onKey() callback method. It is passed as arguments the view that received the event, the KeyCode of the physical key that was pressed, and a KeyEvent object.
An Event Handling Example
In the remainder of this chapter, we will create an Android Studio project designed to demonstrate the implementation of an event listener and corresponding callback method to detect when the user has clicked on a button. The code within the callback method will update a text view to indicate that the event has been processed.
Select the New Project option from the welcome screen and, within the resulting new project dialog, choose the Empty Views Activity template before clicking the Next button.
An Overview and Example of Android Event Handling Enter EventExample into the Name field and specify com.ebookfrenzy.eventexample as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo) and the Language menu to Kotlin. Using the steps outlined in An Overview of Android View Binding, convert the project to use view binding.
Designing the User Interface
The user interface layout for the MainActivity class in this example will consist of a ConstraintLayout, a Button, and a TextView, as illustrated in Figure 33-1.
Locate and select the activity_main.xml file created by Android Studio (located in the Project tool window under app -> res -> layouts) and double-click on it to load it into the Layout Editor tool.
Ensure that Autoconnect is enabled, then drag a Button widget from the palette and move it so that it is positioned in the horizontal center of the layout and beneath the existing TextView widget. When correctly positioned, drop the widget into place so that the autoconnect system adds appropriate constraints.
Select the “Hello World!” TextView widget and use the Attributes panel to set the ID to statusText. Repeat this step to change the ID of the Button widget to myButton.
Add any missing constraints by clicking on the Infer Constraints button in the layout editor toolbar.
With the Button widget selected, use the Attributes panel to set the text property to Press Me. Extract the text string on the button to a resource named press_me. With the user interface layout completed, the next step is registering the event listener and callback method.
The Event Listener and Callback Method
For this example, an onClickListener needs to be registered for the myButton view. This is achieved by calling the setOnClickListener() method of the button view, passing through a new onClickListener object as an argument, and implementing the onClick() callback method. Since this task only needs to be performed when the activity is created, a good location is the onCreate() method of the MainActivity class.
If the MainActivity.kt file is already open within an editor session, select it by clicking on the tab in the editor panel. Alternatively, locate it within the Project tool window by navigating to (app -> kotlin+java -> com.ebookfrenzy. eventexample -> MainActivity) and double-click on it to load it into the code editor. Once loaded, locate the template onCreate() method and modify it to obtain a reference to the button view, register the event listener, and implement the onClick() callback method:
package com.ebookfrenzy.eventexample
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import com.ebookfrenzy.eventexample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.myButton.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
}
}
Code language: Kotlin (kotlin)
The above code has registered the event listener on the button and implemented the onClick() method. In fact, the code to configure the listener can be made more efficient by using a lambda as follows:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// binding.myButton.setOnClickListener(object : View.OnClickListener {
// override fun onClick(v: View?) {
//
// }
// })
binding.myButton.setOnClickListener {
}
}
Code language: Kotlin (kotlin)
If the application were to be run at this point, however, there would be no indication that the event listener installed on the button was working since there is, as yet, no code implemented within the body of the lambda. The goal for the example is to have a message appear on the TextView when the button is clicked, so some further code changes need to be made:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
.
.
binding.myButton.setOnClickListener {
binding.statusText.text = "Button clicked"
}
}
Code language: Kotlin (kotlin)
Complete this tutorial phase by compiling and running the application on either an AVD emulator or a physical Android device. On touching and releasing the button view (otherwise known as “clicking”), the text view should change to display the “Button clicked” text.
Consuming Events
The detection of standard clicks (as opposed to long clicks) on views is a straightforward case of event handling. The example will now be extended to include the detection of long click events, which occur when the user clicks and holds a view on the screen and, in doing so, cover the topic of event consumption.
Consider the code for the onClick listener code in the above section of this chapter. The lambda code assigned to the listener does not return any value and is not required to do so.
On the other hand, the code assigned to the onLongClickListener is required to return a Boolean value to the Android framework. The purpose of this return value is to indicate to the Android runtime whether or not the callback has consumed the event. If the callback returns a true value, the framework discards the event. If, on the other hand, the callback returns a false value, the Android framework will consider the event still to be active and pass it along to the next matching event listener registered on the same view.
As with many programming concepts, this is best demonstrated with an example. The first step is to add an event listener for long clicks to the button view in the example activity:
override fun onCreate(savedInstanceState: Bundle?) {
.
.
binding.myButton.setOnClickListener {
binding.statusText.text = "Button clicked"
}
binding.myButton.setOnLongClickListener {
binding.statusText.text = "Long button click"
true
}
}
Code language: Kotlin (kotlin)
When a long click is detected, the lambda code will display “Long button click” on the text view. Note, however, that the callback method returns a true value to indicate that it has consumed the event. Run the application and press and hold the Button view until the “Long button click” text appears in the text view. On releasing the button, the text view displays the “Long button click” text indicating that the onClick listener code was not called.
Next, modify the code so that the onLongClick listener now returns a false value:
binding.myButton.setOnLongClickListener {
statusText.text = "Long button click"
false
}
Code language: Kotlin (kotlin)
Once again, compile and run the application and perform a long click on the button until the long click message appears. However, after releasing the button this time, note that the onClick listener is also triggered, and the text changes to “Button clicked”. This is because the false value returned by the onLongClick listener code indicated to the Android framework that the event was not consumed by the method and was eligible to be passed on to the next registered listener on the view. In this case, the runtime ascertained that the onClickListener on the button was also interested in events of this type and subsequently called the onClick listener code.
Summary
A user interface is of little practical use if the views it contains do not do anything in response to user interaction. Android bridges the gap between the user interface and the back-end code of the application through the concepts of event listeners and callback methods. The Android View class defines a set of event listeners which can be registered on view objects. Each event listener also has associated with it a callback method.
When an event takes place on a view in a user interface, that event is placed into an event queue and handled on a first-in, first-out basis by the Android runtime. If the view on which the event took place has registered a listener that matches the type of event, the corresponding callback method or lambda expression is called. This code then performs any tasks required by the activity before returning. Some callback methods are required to return a Boolean value to indicate whether the event needs to be passed on to other event listeners registered on the view or discarded by the system.
Now that the basics of event handling have been covered, the next chapter will explore touch events with a particular emphasis on handling multiple touches.