34,333
edits
Changes
Created page with "Much has been covered in the previous chapters relating to the design of user interfaces for Android applications. An area that has yet to be covered, however, involves the wa..."
Much has been covered in the previous chapters relating to the design of user interfaces for Android applications. An area that has yet to be covered, however, involves the way in which a user’s interaction with the user interface triggers the underlying activity to perform a task. In other words, we know from the previous chapters how to create a user interface containing a button view, but not how to make something happen within the application when it is touched by the user.
The primary objective of this chapter, therefore, is to provide an overview of event handling in Android applications together with an Android Studio based example project.
== Understanding Android Events ==
Events in Android can take a variety of different 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.
In order to be able to handle the event that it has been passed, the view must have in place an event listener. The Android View class, from which all user interface components are derived, contains a range of event listener interfaces, each of which contains an abstract declaration for a callback method. In order 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 to 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. In the event that 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 should be performed or other methods called in response to the button click.
== 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:
<pre>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="buttonClick"
android:text="Click me" />
</pre>
This provides a simple way to capture click events. It does not, however, provide the range of options offered by event handlers, which are the topic of the rest of this chapter. 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 that are 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 form of 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 as the result of interaction with a track-ball 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. Passed as arguments are 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 work through the creation of a simple 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.
Create a new project in Android Studio with Kotlin support enabled, entering EventExample into the Application name field and ebookfrenzy.com as the Company Domain setting before clicking on the Next button.
On the form factors screen, enable the Phone and Tablet option and set the minimum SDK setting to API 14: Android 4.0 (IceCreamSandwich). Continue to proceed through the screens, requesting the creation of an Empty Activity named EventExampleActivity with a corresponding layout file named activity_event_example.
== Designing the User Interface ==
The user interface layout for the EventExampleActivity class in this example is to consist of a ConstraintLayout, a Button and a TextView as illustrated in Figure 32-1.
[[File:]]
Locate and select the activity_event_example.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.
Make sure 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 appropriate constraints are added by the autoconnect system. Add any missing constraints by clicking on the Infer Constraints button in the layout editor toolbar.
[[File:]]
With the Button widget selected, use the Attributes panel to set the text property to Press Me. Using the yellow warning button located in the top right-hand corner of the Layout Editor (Figure 32-3), display the warnings list and click on the Fix button to extract the text string on the button to a resource named press_me:
[[File:]]
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.
With the user interface layout now completed, the next step is to register the event listener and callback method.
== The Event Listener and Callback Method ==
For the purposes of this example, an onClickListener needs to be registered for the myButton view. This is achieved by making a call to the setOnClickListener() method of the button view, passing through a new onClickListener object as an argument and implementing the onClick() callback method. Since this is a task that only needs to be performed when the activity is created, a good location is the onCreate() method of the EventExampleActivity class.
If the EventExampleActivity.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 -> java -> com.ebookfrenzy.eventexample -> EventExampleActivity) 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:
<pre>
package com.ebookfrenzy.eventexample
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_event_example.*
class EventExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
}
}
</pre>
The above code has now 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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
button.setOnClickListener {
}
}
</pre>
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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener {
statusText.text = "Button clicked"
}
}
</pre>
Complete this phase of the tutorial by compiling and running the application on either an AVD emulator or 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 very simple 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.
The code assigned to the onLongClickListener, on the other hand, 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 event is discarded by the framework. If, on the other hand, the callback returns a false value the Android framework will consider the event still to be active and will consequently pass it along to the next matching event listener that is registered on the same view.
As with many programming concepts this is, perhaps, 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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener {
statusText.text = "Button clicked"
}
button.setOnLongClickListener {
statusText.text = "Long button click"
true
}
}
</pre>
Clearly, when a long click is detected, the lambda code will display “Long button click” on the text view. Note, however, that the callback method also returns a value of true 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 continues to display the “Long button click” text indicating that the onClick listener code was not called.
Next, modify the code such that the onLongClick listener now returns a false value:
<pre>
button.setOnLongClickListener {
statusText.text = "Long button click"
false
}
</pre>
Once again, compile and run the application and perform a long click on the button until the long click message appears. Upon releasing the button this time, however, note that the onClick listener is also triggered and the text changes to “Button click”. 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 any other event listeners registered on the view or discarded by the system.
Having covered the basics of event handling, the next chapter will explore in some depth the topic of touch events with a particular emphasis on handling multiple touches.
The primary objective of this chapter, therefore, is to provide an overview of event handling in Android applications together with an Android Studio based example project.
== Understanding Android Events ==
Events in Android can take a variety of different 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.
In order to be able to handle the event that it has been passed, the view must have in place an event listener. The Android View class, from which all user interface components are derived, contains a range of event listener interfaces, each of which contains an abstract declaration for a callback method. In order 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 to 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. In the event that 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 should be performed or other methods called in response to the button click.
== 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:
<pre>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="buttonClick"
android:text="Click me" />
</pre>
This provides a simple way to capture click events. It does not, however, provide the range of options offered by event handlers, which are the topic of the rest of this chapter. 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 that are 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 form of 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 as the result of interaction with a track-ball 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. Passed as arguments are 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 work through the creation of a simple 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.
Create a new project in Android Studio with Kotlin support enabled, entering EventExample into the Application name field and ebookfrenzy.com as the Company Domain setting before clicking on the Next button.
On the form factors screen, enable the Phone and Tablet option and set the minimum SDK setting to API 14: Android 4.0 (IceCreamSandwich). Continue to proceed through the screens, requesting the creation of an Empty Activity named EventExampleActivity with a corresponding layout file named activity_event_example.
== Designing the User Interface ==
The user interface layout for the EventExampleActivity class in this example is to consist of a ConstraintLayout, a Button and a TextView as illustrated in Figure 32-1.
[[File:]]
Locate and select the activity_event_example.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.
Make sure 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 appropriate constraints are added by the autoconnect system. Add any missing constraints by clicking on the Infer Constraints button in the layout editor toolbar.
[[File:]]
With the Button widget selected, use the Attributes panel to set the text property to Press Me. Using the yellow warning button located in the top right-hand corner of the Layout Editor (Figure 32-3), display the warnings list and click on the Fix button to extract the text string on the button to a resource named press_me:
[[File:]]
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.
With the user interface layout now completed, the next step is to register the event listener and callback method.
== The Event Listener and Callback Method ==
For the purposes of this example, an onClickListener needs to be registered for the myButton view. This is achieved by making a call to the setOnClickListener() method of the button view, passing through a new onClickListener object as an argument and implementing the onClick() callback method. Since this is a task that only needs to be performed when the activity is created, a good location is the onCreate() method of the EventExampleActivity class.
If the EventExampleActivity.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 -> java -> com.ebookfrenzy.eventexample -> EventExampleActivity) 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:
<pre>
package com.ebookfrenzy.eventexample
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_event_example.*
class EventExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
}
}
</pre>
The above code has now 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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
button.setOnClickListener {
}
}
</pre>
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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener {
statusText.text = "Button clicked"
}
}
</pre>
Complete this phase of the tutorial by compiling and running the application on either an AVD emulator or 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 very simple 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.
The code assigned to the onLongClickListener, on the other hand, 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 event is discarded by the framework. If, on the other hand, the callback returns a false value the Android framework will consider the event still to be active and will consequently pass it along to the next matching event listener that is registered on the same view.
As with many programming concepts this is, perhaps, 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:
<pre>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_event_example)
button.setOnClickListener {
statusText.text = "Button clicked"
}
button.setOnLongClickListener {
statusText.text = "Long button click"
true
}
}
</pre>
Clearly, when a long click is detected, the lambda code will display “Long button click” on the text view. Note, however, that the callback method also returns a value of true 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 continues to display the “Long button click” text indicating that the onClick listener code was not called.
Next, modify the code such that the onLongClick listener now returns a false value:
<pre>
button.setOnLongClickListener {
statusText.text = "Long button click"
false
}
</pre>
Once again, compile and run the application and perform a long click on the button until the long click message appears. Upon releasing the button this time, however, note that the onClick listener is also triggered and the text changes to “Button click”. 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 any other event listeners registered on the view or discarded by the system.
Having covered the basics of event handling, the next chapter will explore in some depth the topic of touch events with a particular emphasis on handling multiple touches.