Handling focus
The system predicts which views should be in focus. It changes focus depending on the user’s input and as views are removed or displayed.
Setting the focus
It’s a good idea to make sure that all of the views that the user should be able to navigate to are focusable. Some views, like text fields are focusable by default. Those that aren’t, can be made focusable using:
- setFocusable() – in code
- android:focusable – in XML
- calling requestFocus() to request that a view take focus
- using the
element in your XML layout file to request focus for a particular view. You can only use this element once per file
You can set views or widgets to be focusable in touch mode using:
- setFocusableInTouchMode() – in code
- android:focusableInTouchMode – in XML
Check for focus
If you need to see if a view can receive focus, use:
- isFocusable() – it returns true or false
- isFocusableInTouchMode() – checks to see if the view is focusable in touch mode. (A view may be focusable when using a hardware key but not when the device is in touch mode)
Make it happen: Forcing focus
You can force a view to take focus by:
- calling requestFocus() to request that a view take focus
- using the
element in your XML layout file to request focus for a particular view. You can only use this element once per file
Keep your eye on the ball: Looking for a change in focus?
Implement the OnFocusChangeListener interface to listen for when a view’s focus changes. The interface’s onFocusChange() method is triggered as soon as its attached view’s focus changes.
To and fro: Moving in and out of touch mode
Don’t rely on selection (selected items in a list for example) and focus being maintained as you move in and out of touch mode
All selected items are unselected when the device enters touch mode. They’re reselected when the device exits touch mode.
Similarly any views that are in focus lose their focus when entering touch mode unless they’re focusable in touch mode.
Getting down and dirty: Some sample code
An event is triggered when the user interacts with your app by clicking a button or touching the screen. You can listen for these events using listeners and then use their callback methods to run some code.
If there is no view to handle a touch event, then nothing happens. However, you can include the Activity class’s onTouchEvent() method to handle the event. It will handle any event that is not consumed by any of the other views in the layout.
Stay in touch: The onTouchEvent()
A touch event contains information about the touch. It would, for example contain the following information:
- the action performed, like ACTION_DOWN or ACTION_UP
- the X and Y coordinates of where the touch happened on the screen
- information about the pressure of the touch
- information about the size and area of the screen contact
This information is contained in a MotionEvent object.
The onTouchEvent() method handles any touch event that has not been consumed by any other view. It receives a MotionEvent object as a parameter.
Here’s an example:
The Activity’s onTouchEvent() method is triggered when no other view consumes a touch event
A quick explanation
Let’s say the user touches the screen. This triggers a touch event. If there are no views registered to listen for this event then nothing happens. However if you include the onTouchEvent() method, then it will handle the touch event.
Here we’ve used the onTouchEvent() method to capture the touch event. We then use a switch statement to filter the MotionEvent parameter and display an appropriate LogCat message for the touch action.
The onTouchEvent() method will not trigger if the touch event is consumed by any view in the view hierarchy. For example, if we set an on touch listener on the layout container as shown below:
This listener triggers its onTouch() method when the user touches the screen but it does not consume the event as it returns false
A quick explanation
Although this on touch listener’s onTouch() method handles the touch event, it returns false so does not consume the event. In this case the onTouchEvent() method will still be triggered.
Had the onTouch() method returned true, indicating that it had consumed the event, then the onTouchEvent() method will not be triggered.
In the know! Want to know when a view’s focus changes?
Implement the OnFocusChangeListener interface to monitor when a view’s focus changes:
Implement the OnFocusChangeListener interface to notify you when a view’s focus changes
A quick explanation
The EditTextActivity activity implements the OnFocusChangeListener interface which monitors the view it is attached to for focus change. It’s onFocusChange() method is triggered when either of the text field’s focus changes as we’ve registered the focus change listener to all of the EditText views.
Here we display a LogCat message indicating which of the text field’s focus has changed, using the text field’s tag attribute value to identify the text field. The tag is defined in the activity’s layout file.
Listen up: Are you listening?
Implementing the listener interfaces
You use event listeners to listen for user interaction. Their callback methods are triggered when the user touches any of the views on the screen.
We’ve implemented a number of listener interfaces to the ButtonsActivity activity in the example below. These are the:
- OnFocusChangeListener – listens for when a view’s focus changes
- OnClickListener – listens for when a view is clicked
- OnGlobalFocusChangeListener – listens for when the focus of any view in a particular view hierarchy changes
- OnTouchModeChangeListener – listens for when the device’s touch mode changes
Here’s the declaration of the ButtonsActivity activity class:
Implement the various listener interfaces in the activity to monitor any changes
A quick explanation
We’ve implemented a number of interfaces to listen for changes in focus, touch mode and button clicks.
We also need to register their callback methods to the various views that we would like to monitor.
Pairing up: Register the listeners
Register the focus change and on click listeners to the various buttons:
Register the listeners to the various buttons
A quick explanation
We get references to the 4 buttons and then register the OnFocusChangeListener and OnClickListener to these buttons. This will trigger their callback methods when the buttons are clicked or their focus changes.
Under observation: The observers
We need a ViewTreeObserver for two of the interfaces:
- OnGlobalFocusChangeListener
- OnTouchModeChangeListener
Can’t see the wood for the trees: What’s a view tree?
A view tree is a hierarchy of views. For example, a layout file has a single root element which is usually a ViewGroup like a LinearLayout or RelativeLayout. It can contain a number of child views like buttons, text fields and images.
So the ViewGroup can be seen as the trunk of the tree and the child views as the branches. Together they make up the view tree.
Watching the garden grow: What’s a ViewTreeObserver?
We want to listen for changes in a layout (a view tree). To do this, we need to register a listener on the layout or view tree. We use a ViewTreeObserver to do this.
Our layout
Our layout consists of a RelativeLayout containing 4 buttons.
The Buttons
Here’s an example of how we defined one of our buttons in the layout file:
There are four buttons. Each has an ID and a tag
A quick explanation
We define the buttons in the XML layout file. Note these attributes:
- focusable – enables this button to receive focus
- focusableInTouchMode – enables this button to receive focus when the device is in touch mode
- tag – a string identity for this button
Get a reference to the ViewGroup container
Get a reference to the ViewGroup and then call getViewTreeObserver() to get the ViewTreeObserver
A quick explanation
We get a reference to the layout’s root element, the RelativeLayout. Then for interest sake, we check if the device is in touch mode by calling isInTouchMode().
Next, we call getViewTreeObserver() to get an instance of a ViewTreeObserver. Then we call isAlive() to check if the observer is alive. (Calling the add…() methods would throw an exception if the observer was not alive).
If the observer is alive then we add the two listeners.
Callback time
World events: The onGlobalFocusChanged() method
The onGlobalFocusChanged() method is triggered whenever focus changes for any of the views in the view hierarchy.
The onGlobalFocusChanged() method is triggered when any of the view’s focus changes
A quick explanation
The onGlobalFocusChanged() method is triggered when the focus of any of the views in the view tree changes. We display a LogCat message indicating which view has lost focus and which view has gained focus. (We use the view’s tags, which were defined in the layout file, to identify them.)
Touchy, touchy: The onTouchModeChanged() method
The onTouchModeChanged() method is triggered when the device moves in and out of touch mode. (This happens whenever the user touches the screen or a hardware button).
The onTouchModeChanged() method is triggered whenever the device enters or leaves touch mode
A quick explanation
The onTouchModeChanged() method is triggered when the user touches the device ‘s screen or when they touch a hardware key.
Here we simply display an appropriate LogCat message when the touch mode changes.
Click, click: The onClick() method
The onClick() method is triggered when one of the buttons is clicked.
Clicking a button triggers the onClick() method
A quick explanation
Clicking a button triggers its registered OnClickListener interface’s onClick() method. We use a switch statement to filter the clicked button’s ID. If it matches one of the case statements then we display a LogCat message indicating which button was clicked, using the button’s tag to identify it.
We also call the requestFocus() method to place another button in focus.
Touch or not to touch: Is the device in Touch Mode?
You can check at any time to see whether a device is in touch mode.
Call the isInTouchMode() method to check whether a device is in touch mode
A quick explanation
A device enters touch mode when the user touches the screen. It remains in touch mode until the user touches a hardware key.
Here we display a LogCat message indicating whether or not the device is in touch mode.
Doing it all over again: The onResume() method
We also check if the device is in touch mode by calling isInTouchMode() in the onResume() method.
You can check whether a device is in touch mode by calling isInTouchMode() in the activity’s onResume() method as it’s always called just before the activity starts interacting with the user
A quick explanation
It may happen that the activity starts while the device is not in touch mode. The user then navigates to another activity by pressing a virtual button on the screen (putting the device into touch mode). They then return to the previous activity, onResume() is triggered and isInTouchMode() called, showing that the device is now in touch mode.
The LogCat display
A possible display of the logCat
A quick explanation
When onResume() is triggered for the first time, the device is not in touch mode, later when it’s called again (the user has navigated away from the activity and then returned, triggering onResume()), the device is shown to be in touch mode – indicating that the user had touched the screen at some stage before returning to the activity.
Other kids on the block: More listeners
Here are a couple of examples of other listener interfaces and their callback methods.
The OnTouchListener interface
The on touch listener interface listens for when the view that it is attached to is touched. When the view is touched, it triggers the interface’s onTouch() callback method.
You can implement the listener interfaces in your activities and then register their callbacks on the relevant views as we have done in some of the above examples.
Here we define the OnTouchListener interface as an anonymous class which is passed to the setOnTouchListener() method to register the interface on the ImageView:
Implement the OnTouchListener as an inner class which is passed to the setOnTouchListener() method
A quick explanation
The listener triggers its onTouch() method when the ImageView is touched.
We use a switch statement to filter the MotionEvent parameter and then display the appropriate LogCat message.
We also use an intent to start the ButtonsActivity activity.
All the return values are true, indicating that this listener has consumed the touch event.
The OnLongClickListener interface
Use the OnLongClickListener interface to listen for when the user clicks and holds a view.
The OnLongClickListener interface’s onLongClick() method triggers when a user clicks and holds the view
A quick explanation
We implement the OnLongClickListener interface as an inner class, passing it as a parameter to the setOnLongClickListener() method to register the interface of the ImageView.
Each time the ImageView is long-clicked, the onLongClick() method triggers, displaying a message in the LogCat.
The OnClickListener interface
The OnClickListener interface listens for when a view is clicked.
Clicking a view triggers the OnClickListener interface’s onClick() callback method
A quick explanation
The interface’s onClick() method is triggered each time the imageButton is clicked. Here we display a LogCat message and start the EditTextActivity activity.
I hope that you found this tutorial helpful.
Here are a couple of tutorials that you may also find useful:
Android: Using touch gestures
Using Android’s Log class API to debug Android application code