Skip to content

Most visited

Recently visited


Handling Controller Actions

At the system level, Android reports input event codes from game controllers as Android key codes and axis values. In your game, you can receive these codes and values and convert them to specific in-game actions.

When players physically connect or wirelessly pair a game controller to their Android-powered devices, the system auto-detects the controller as an input device and starts reporting its input events. Your game can receive these input events by implementing the following callback methods in your active Activity or focused View (you should implement the callbacks for either the Activity or View, but not both):

The recommended approach is to capture the events from the specific View object that the user interacts with. Inspect the following objects provided by the callbacks to get information about the type of input event received:

An object that describes directional pad (D-pad) and gamepad button events. Key events are accompanied by a key code that indicates the specific button triggered, such as DPAD_DOWN or BUTTON_A. You can obtain the key code by calling getKeyCode() or from key event callbacks such as onKeyDown().
An object that describes input from joystick and shoulder trigger movements. Motion events are accompanied by an action code and a set of axis values. The action code specifies the state change that occurred such as a joystick being moved. The axis values describe the position and other movement properties for a specific physical control, such as AXIS_X or AXIS_RTRIGGER. You can obtain the action code by calling getAction() and the axis value by calling getAxisValue().

This lesson focuses on how you can handle input from the most common types of physical controls (gamepad buttons, directional pads, and joysticks) in a game screen by implementing the above-mentioned View callback methods and processing KeyEvent and MotionEvent objects.

Verify a Game Controller is Connected

When reporting input events, Android does not distinguish between events that came from a non-game controller device and events that came from a game controller. For example, a touch screen action generates an AXIS_X event that represents the X coordinate of the touch surface, but a joystick generates an AXIS_X event that represents the X position of the joystick. If your game cares about handling game-controller input, you should first check that the input event comes from a relevant source type.

To verify that a connected input device is a game controller, call getSources() to obtain a combined bit field of input source types supported on that device. You can then test to see if the following fields are set:

The following code snippet shows a helper method that lets you check whether the connected input devices are game controllers. If so, the method retrieves the device IDs for the game controllers. You can then associate each device ID with a player in your game, and process game actions for each connected player separately. To learn more about supporting multiple game controllers that are simultaneously connected on the same Android device, see Supporting Multiple Game Controllers.

public ArrayList getGameControllerIds() {
    ArrayList gameControllerDeviceIds = new ArrayList();
    int[] deviceIds = InputDevice.getDeviceIds();
    for (int deviceId : deviceIds) {
        InputDevice dev = InputDevice.getDevice(deviceId);
        int sources = dev.getSources();

        // Verify that the device has gamepad buttons, control sticks, or both.
        if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                || ((sources & InputDevice.SOURCE_JOYSTICK)
                == InputDevice.SOURCE_JOYSTICK)) {
            // This device is a game controller. Store its device ID.
            if (!gameControllerDeviceIds.contains(deviceId)) {
    return gameControllerDeviceIds;

Additionally, you might want to check for individual input capabilities supported by a connected game controller. This might be useful, for example, if you want your game to use only input from the set of physical controls it understands.

To detect if a specific key code or axis code is supported by a connected game controller, use these techniques:

Process Gamepad Button Presses

Figure 1 shows how Android maps key codes and axis values to the physical controls on most game controllers.

Figure 1. Profile for a generic game controller.

The callouts in the figure refer to the following:

  8. BUTTON_R1
  11. BUTTON_L1

Common key codes generated by gamepad button presses include BUTTON_A, BUTTON_B, BUTTON_SELECT, and BUTTON_START. Some game controllers also trigger the DPAD_CENTER key code when the center of the D-pad crossbar is pressed. Your game can inspect the key code by calling getKeyCode() or from key event callbacks such as onKeyDown(), and if it represents an event that is relevant to your game, process it as a game action. Table 1 lists the recommended game actions for the most common gamepad buttons.

Table 1. Recommended game actions for gamepad buttons.

Game Action Button Key Code
Start game in main menu, or pause/unpause during game BUTTON_START*
Same as Android Back navigation behavior described in the Navigation design guide. KEYCODE_BACK
Navigate back to a previous item in a menu BUTTON_B
Confirm selection, or perform primary game action BUTTON_A and DPAD_CENTER

* Your game should not rely on the presence of the Start, Select, or Menu buttons.

Tip: Consider providing a configuration screen in your game to allow users to personalize their own game controller mappings for game actions.

The following snippet shows how you might override onKeyDown() to associate the BUTTON_A and DPAD_CENTER button presses with a game action.

public class GameView extends View {

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship

                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             handled = true;
            if (handled) {
                return true;
        return super.onKeyDown(keyCode, event);

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;

Note: On Android 4.2 (API level 17) and lower, the system treats BUTTON_A as the Android Back key by default. If your app supports these Android versions, make sure to treat BUTTON_A as the primary game action. To determine the current Android SDK version on the device, refer to the Build.VERSION.SDK_INT value.

Process Directional Pad Input

The 4-way directional pad (D-pad) is a common physical control in many game controllers. Android reports D-pad UP and DOWN presses as AXIS_HAT_Y events with a range from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as AXIS_HAT_X events with a range from -1.0 (left) to 1.0 (right).

Some controllers instead report D-pad presses with a key code. If your game cares about D-pad presses, you should treat the hat axis events and the D-pad key codes as the same input events, as recommended in table 2.

Table 2. Recommended default game actions for D-pad key codes and hat axis values.

Game Action D-pad Key Code Hat Axis Code
Move Up KEYCODE_DPAD_UP AXIS_HAT_Y (for values 0 to -1.0)
Move Down KEYCODE_DPAD_DOWN AXIS_HAT_Y (for values 0 to 1.0)
Move Left KEYCODE_DPAD_LEFT AXIS_HAT_X (for values 0 to -1.0)
Move Right KEYCODE_DPAD_RIGHT AXIS_HAT_X (for values 0 to 1.0)

The following code snippet shows a helper class that lets you check the hat axis and key code values from an input event to determine the D-pad direction.

public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (, 1.0f) == 0) {
                directionPressed =  Dpad.DOWN;

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
        return directionPressed;

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        if ((event.getSource() & InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;

You can use this helper class in your game wherever you want to process D-pad input (for example, in the onGenericMotionEvent() or onKeyDown() callbacks).

For example:

Dpad mDpad = new Dpad();
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = mDpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                return true;
            case UP:
                // Do something for UP direction press
                return true;

    // Check if this event is from a joystick movement and process accordingly.

Process Joystick Movements

When players move a joystick on their game controllers, Android reports a MotionEvent that contains the ACTION_MOVE action code and the updated positions of the joystick's axes. Your game can use the data provided by the MotionEvent to determine if a joystick movement it cares about happened.

Note that joystick motion events may batch multiple movement samples together within a single object. The MotionEvent object contains the current position for each joystick axis as well as multiple historical positions for each axis. When reporting motion events with action code ACTION_MOVE (such as joystick movements), Android batches up the axis values for efficiency. The historical values for an axis consists of the set of distinct values older than the current axis value, and more recent than values reported in any previous motion events. See the MotionEvent reference for details.

You can use the historical information to more accurately render a game object's movement based on the joystick input. To retrieve the current and historical values, call getAxisValue() or getHistoricalAxisValue(). You can also find the number of historical points in the joystick event by calling getHistorySize().

The following snippet shows how you might override the onGenericMotionEvent() callback to process joystick input. You should first process the historical values for an axis, then process its current position.

public class GameView extends View {

    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &&
                event.getAction() == MotionEvent.ACTION_MOVE) {

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i < historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        return super.onGenericMotionEvent(event);

Before using joystick input, you need to determine if the joystick is centered, then calculate its axis movements accordingly. Joysticks typically have a flat area, that is, a range of values near the (0,0) coordinate at which the axis is considered to be centered. If the axis value reported by Android falls within the flat area, you should treat the controller to be at rest (that is, motionless along both axes).

The snippet below shows a helper method that calculates the movement along each axis. You invoke this helper in the processJoystickInput() method described further below.

private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos < 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
    return 0;

Putting it all together, here is how you might process joystick movements in your game:

private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice mInputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, mInputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_Z, historyPos);

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, mInputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, mInputDevice,
                MotionEvent.AXIS_RZ, historyPos);

    // Update the ship object based on the new x and y values

To support game controllers that have more sophisticated features beyond a single joystick, follow these best practices:

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields


Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience. (April 2018 — Developer Survey)