| page.title=Handling Controller Actions | |
| trainingnavtop=true | |
| @jd:body | |
| <!-- This is the training bar --> | |
| <div id="tb-wrapper"> | |
| <div id="tb"> | |
| <h2>This lesson teaches you to</h2> | |
| <ol> | |
| <li><a href="#input">Verify a Game Controller is Connected</a></li> | |
| <li><a href="#button">Process Gamepad Button Presses</a> | |
| </li> | |
| <li><a href="#dpad">Process Directional Pad Input</a> | |
| </li> | |
| <li><a href="#joystick">Process Joystick Movements</a> | |
| </li> | |
| </ol> | |
| <h2>Try it out</h2> | |
| <div class="download-box"> | |
| <a href="http://developer.android.com/shareables/training/ControllerSample.zip" | |
| class="button">Download the sample</a> | |
| <p class="filename">ControllerSample.zip</p> | |
| </div> | |
| </div> | |
| </div> | |
| <p>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.</p> | |
| <p>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 | |
| {@link android.app.Activity} or focused {@link android.view.View} (you should | |
| implement the callbacks for either the {@link android.app.Activity} or | |
| {@link android.view.View}, but not both): </p> | |
| <ul> | |
| <li>From {@link android.app.Activity}: | |
| <ul> | |
| <li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent) | |
| dispatchGenericMotionEvent(android.view. MotionEvent)} | |
| <p>Called to process generic motion events such as joystick movements.</p> | |
| </li> | |
| <li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) | |
| dispatchKeyEvent(android.view.KeyEvent)} | |
| <p>Called to process key events such as a press or release of a | |
| gamepad or D-pad button.</p> | |
| </li> | |
| </ul> | |
| </li> | |
| <li>From {@link android.view.View}: | |
| <ul> | |
| <li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) | |
| onGenericMotionEvent(android.view.MotionEvent)} | |
| <p>Called to process generic motion events such as joystick movements.</p> | |
| </li> | |
| <li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)} | |
| <p>Called to process a press of a physical key such as a gamepad or | |
| D-pad button.</p> | |
| </li> | |
| <li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)} | |
| <p>Called to process a release of a physical key such as a gamepad or | |
| D-pad button.</p> | |
| </li> | |
| </ul> | |
| </li> | |
| </ul> | |
| <p>The recommended approach is to capture the events from the | |
| specific {@link android.view.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:</p> | |
| <dl> | |
| <dt>{@link android.view.KeyEvent}</dt> | |
| <dd>An object that describes directional | |
| pad</a> (D-pad) and gamepad button events. Key events are accompanied by a | |
| <em>key code</em> that indicates the specific button triggered, such as | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN} | |
| or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the | |
| key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key | |
| event callbacks such as | |
| {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}. | |
| <dd> | |
| <dt>{@link android.view.MotionEvent}</dt> | |
| <dd>An object that describes input from joystick and shoulder trigger | |
| movements. Motion events are accompanied by an action code and a set of | |
| <em>axis values</em>. 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 | |
| {@link android.view.MotionEvent#AXIS_X} or | |
| {@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code | |
| by calling {@link android.view.MotionEvent#getAction()} and the axis value by | |
| calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}. | |
| <dd> | |
| </dl> | |
| <p>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 | |
| {@link android.view.View} callback methods and processing | |
| {@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p> | |
| <h2 id="input">Verify a Game Controller is Connected</h2> | |
| <p>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 | |
| {@link android.view.MotionEvent#AXIS_X} event that represents the X | |
| coordinate of the touch surface, but a joystick generates an {@link android.view.MotionEvent#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.</p> | |
| <p>To verify that a connected input device is a game controller, call | |
| {@link android.view.InputDevice#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:</p> | |
| <ul> | |
| <li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates | |
| that the input device has gamepad buttons (for example, | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source | |
| type does not strictly indicate if the game controller has D-pad buttons, | |
| although most gamepads typically have directional controls.</li> | |
| <li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that | |
| the input device has D-pad buttons (for example, | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li> | |
| <li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK} | |
| indicates that the input device has analog control sticks (for example, a | |
| joystick that records movements along {@link android.view.MotionEvent#AXIS_X} | |
| and {@link android.view.MotionEvent#AXIS_Y}).</li> | |
| </ul> | |
| <p>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 | |
| <a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p> | |
| <pre> | |
| public ArrayList<Integer> getGameControllerIds() { | |
| ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>(); | |
| 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)) { | |
| gameControllerDeviceIds.add(deviceId); | |
| } | |
| } | |
| } | |
| return gameControllerDeviceIds; | |
| } | |
| </pre> | |
| <p>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.</p> | |
| <p>To detect if a specific key code or axis code is supported by a connected | |
| game controller, use these techniques:</p> | |
| <ul> | |
| <li>In Android 4.4 (API level 19) or higher, you can determine if a key code is | |
| supported on a connected game controller by calling | |
| {@link android.view.InputDevice#hasKeys(int...)}.</li> | |
| <li>In Android 3.1 (API level 12) or higher, you can find all available axes | |
| supported on a connected game controller by first calling | |
| {@link android.view.InputDevice#getMotionRanges()}. Then, on each | |
| {@link android.view.InputDevice.MotionRange} object returned, call | |
| {@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li> | |
| </ul> | |
| <h2 id="button">Process Gamepad Button Presses</h2> | |
| <p>Figure 1 shows how Android maps key codes and axis values to the physical | |
| controls on most game controllers.</p> | |
| <img src="{@docRoot}images/training/game-controller-profiles.png" alt="" | |
| id="figure1" /> | |
| <p class="img-caption"> | |
| <strong>Figure 1.</strong> Profile for a generic game controller. | |
| </p> | |
| <p>The callouts in the figure refer to the following:</p> | |
| <div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;"> | |
| <ol style="margin-left:30px;list-style:decimal;"> | |
| <li>{@link android.view.MotionEvent#AXIS_HAT_X}, | |
| {@link android.view.MotionEvent#AXIS_HAT_Y}, | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}, | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}, | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT}, | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT} | |
| </li> | |
| <li>{@link android.view.MotionEvent#AXIS_X}, | |
| {@link android.view.MotionEvent#AXIS_Y}, | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li> | |
| <li>{@link android.view.MotionEvent#AXIS_Z}, | |
| {@link android.view.MotionEvent#AXIS_RZ}, | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li> | |
| <li>{@link android.view.MotionEvent#AXIS_RTRIGGER}, | |
| {@link android.view.MotionEvent#AXIS_THROTTLE}</li> | |
| <li>{@link android.view.MotionEvent#AXIS_LTRIGGER}, | |
| {@link android.view.MotionEvent#AXIS_BRAKE}</li> | |
| <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li> | |
| </ol> | |
| </div> | |
| <p>Common key codes generated by gamepad button presses include | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}, | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}, | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}, | |
| and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game | |
| controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER | |
| DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your | |
| game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()} | |
| or from key event callbacks such as | |
| {@link android.view.View#onKeyDown(int, android.view.KeyEvent) 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. | |
| </p> | |
| <p class="table-caption" id="table1"> | |
| <strong>Table 1.</strong> Recommended game actions for gamepad | |
| buttons.</p> | |
| <table> | |
| <tr> | |
| <th scope="col">Game Action</th> | |
| <th scope="col">Button Key Code</th> | |
| </tr> | |
| <tr> | |
| <td>Start game in main menu, or pause/unpause during game</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}<sup>*</sup></td> | |
| </tr> | |
| <tr> | |
| <td>Display menu</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}<sup>*</sup> | |
| and {@link android.view.KeyEvent#KEYCODE_MENU}<sup>*</sup></td> | |
| </tr> | |
| <tr> | |
| <td>Same as Android <em>Back</em> navigation behavior described in the | |
| <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> design | |
| guide.</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td> | |
| </tr> | |
| <tr> | |
| <td>Navigate back to a previous item in a menu</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</td> | |
| </tr> | |
| <tr> | |
| <td>Confirm selection, or perform primary game action</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td> | |
| </tr> | |
| </table> | |
| <p> | |
| <em>* Your game should not rely on the presence of the Start, Select, or Menu | |
| buttons.</em> | |
| </p> | |
| <p class="note"><strong>Tip: </strong>Consider providing a configuration screen | |
| in your game to allow users to personalize their own game controller mappings for | |
| game actions.</p> | |
| <p>The following snippet shows how you might override | |
| {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to | |
| associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and | |
| {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses | |
| with a game action. | |
| </p> | |
| <pre> | |
| public class GameView extends View { | |
| ... | |
| @Override | |
| 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 | |
| ... | |
| default: | |
| if (isFireKey(keyCode)) { | |
| // Update the ship object to fire lasers | |
| ... | |
| handled = true; | |
| } | |
| break; | |
| } | |
| } | |
| 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; | |
| } | |
| } | |
| </pre> | |
| <p class="note"><strong>Note: </strong>On Android 4.2 (API | |
| level 17) and lower, the system treats | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android | |
| <em>Back</em> key by default. If your app supports these Android | |
| versions, make sure to treat | |
| {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game | |
| action. To determine the current Android SDK | |
| version on the device, refer to the | |
| {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value.</p> | |
| <h2 id="dpad">Process Directional Pad Input</h2> | |
| <p>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 | |
| {@link android.view.MotionEvent#AXIS_HAT_Y} events with a range | |
| from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as | |
| {@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0 | |
| (left) to 1.0 (right).</p> | |
| <p>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.</p> | |
| <p class="table-caption" id="table2"> | |
| <strong>Table 2.</strong> Recommended default game actions for D-pad key | |
| codes and hat axis values.</p> | |
| <table> | |
| <tr> | |
| <th scope="col">Game Action</th> | |
| <th scope="col">D-pad Key Code</th> | |
| <th scope="col">Hat Axis Code</th> | |
| </tr> | |
| <tr> | |
| <td>Move Up</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td> | |
| <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td> | |
| </tr> | |
| <tr> | |
| <td>Move Down</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td> | |
| <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td> | |
| </tr> | |
| <tr> | |
| <td>Move Left</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td> | |
| <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td> | |
| </tr> | |
| <tr> | |
| <td>Move Right</td> | |
| <td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td> | |
| <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td> | |
| </tr> | |
| </table> | |
| <p>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. | |
| </p> | |
| <pre> | |
| 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 (Float.compare(xaxis, -1.0f) == 0) { | |
| directionPressed = Dpad.LEFT; | |
| } else if (Float.compare(xaxis, 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 (Float.compare(yaxis, -1.0f) == 0) { | |
| directionPressed = Dpad.UP; | |
| } else if (Float.compare(yaxis, 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; | |
| } | |
| } | |
| } | |
| </pre> | |
| <p>You can use this helper class in your game wherever you want to process | |
| D-pad input (for example, in the | |
| {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) | |
| onGenericMotionEvent()} or | |
| {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} | |
| callbacks).</p> | |
| <p>For example:</p> | |
| <pre> | |
| Dpad mDpad = new Dpad(); | |
| ... | |
| @Override | |
| 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. | |
| ... | |
| } | |
| </pre> | |
| <h2 id="joystick">Process Joystick Movements</h2> | |
| <p>When players move a joystick on their game controllers, Android reports a | |
| {@link android.view.MotionEvent} that contains the | |
| {@link android.view.MotionEvent#ACTION_MOVE} action code and the updated | |
| positions of the joystick's axes. Your game can use the data provided by | |
| the {@link android.view.MotionEvent} to determine if a joystick movement it | |
| cares about happened. | |
| </p> | |
| <p>Note that joystick motion events may batch multiple movement samples together | |
| within a single object. The {@link android.view.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 {@link android.view.MotionEvent#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 | |
| {@link android.view.MotionEvent} reference for details.</p> | |
| <p>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 | |
| {@link android.view.MotionEvent#getAxisValue(int) | |
| getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int, | |
| int) getHistoricalAxisValue()}. You can also find the number of historical | |
| points in the joystick event by calling | |
| {@link android.view.MotionEvent#getHistorySize()}.</p> | |
| <p>The following snippet shows how you might override the | |
| {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) | |
| onGenericMotionEvent()} callback to process joystick input. You should first | |
| process the historical values for an axis, then process its current position. | |
| </p> | |
| <pre> | |
| public class GameView extends View { | |
| @Override | |
| 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); | |
| } | |
| } | |
| </pre> | |
| <p>Before using joystick input, you need to determine if the joystick is | |
| centered, then calculate its axis movements accordingly. Joysticks typically | |
| have a <em>flat</em> 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).</p> | |
| <p>The snippet below shows a helper method that calculates the movement along | |
| each axis. You invoke this helper in the {@code processJoystickInput()} method | |
| described further below. | |
| </p> | |
| <pre> | |
| 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; | |
| } | |
| </pre> | |
| <p>Putting it all together, here is how you might process joystick movements in | |
| your game:</p> | |
| <pre> | |
| 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 | |
| } | |
| </pre> | |
| <p>To support game controllers that have more sophisticated | |
| features beyond a single joystick, follow these best practices: </p> | |
| <ul> | |
| <li><strong>Handle dual controller sticks.</strong> Many game controllers have | |
| both a left and right joystick. For the left stick, Android | |
| reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events | |
| and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events. | |
| For the right stick, Android reports horizontal movements as | |
| {@link android.view.MotionEvent#AXIS_Z} events and vertical movements as | |
| {@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle | |
| both controller sticks in your code.</li> | |
| <li><strong>Handle shoulder trigger presses (but provide alternative input | |
| methods).</strong> Some controllers have left and right shoulder | |
| triggers. If these triggers are present, Android reports a left trigger press | |
| as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a | |
| right trigger press as an | |
| {@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android | |
| 4.3 (API level 18), a controller that produces a | |
| {@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an | |
| identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The | |
| same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and | |
| {@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger | |
| presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not | |
| all controllers have triggers, so consider allowing players to perform those | |
| game actions with other buttons. | |
| </li> | |
| </ul> |