blob: 2c50ae1a67fb4f23d860c8a9c1be3c533c25e1a7 [file] [log] [blame]
quddusc1dff7462013-07-01 16:57:26 -07001page.title=Handling Controller Actions
2trainingnavtop=true
3
4@jd:body
5
6<!-- This is the training bar -->
7<div id="tb-wrapper">
8<div id="tb">
9
10<h2>This lesson teaches you to</h2>
11<ol>
12 <li><a href="#input">Verify a Game Controller is Connected</a></li>
13 <li><a href="#button">Process Gamepad Button Presses</a>
14 </li>
15 <li><a href="#dpad">Process Directional Pad Input</a>
16 </li>
17 <li><a href="#joystick">Process Joystick Movements</a>
18 </li>
19</ol>
20
21<h2>Try it out</h2>
22<div class="download-box">
23 <a href="http://developer.android.com/shareables/training/ControllerSample.zip"
24class="button">Download the sample</a>
25 <p class="filename">ControllerSample.zip</p>
26</div>
27
28</div>
29</div>
30
31<p>At the system level, Android reports input event codes from game controllers
32as Android key codes and axis values. In your game, you can receive these codes
33and values and convert them to specific in-game actions.</p>
34
35<p>When players physically connect or wirelessly pair a game controller to
36their Android-powered devices, the system auto-detects the controller
37as an input device and starts reporting its input events. Your game can receive
38these input events by implementing the following callback methods in your active
39{@link android.app.Activity} or focused {@link android.view.View} (you should
40implement the callbacks for either the {@link android.app.Activity} or
41{@link android.view.View}, but not both): </p>
42
43<ul>
44<li>From {@link android.app.Activity}:
45 <ul>
46 <li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent) dispatchGenericMotionEvent(android.view.MotionEvent)}
47 <p>Called to process generic motion events such as joystick movements.</p>
48 </li>
49 <li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) dispatchKeyEvent(android.view.KeyEvent)}
50 <p>Called to process key events such as a press or release of a
51 gamepad or D-pad button.</p>
52 </li>
53 </ul>
54</li>
55<li>From {@link android.view.View}:
56 <ul>
57 <li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
58onGenericMotionEvent(android.view.MotionEvent)}
59 <p>Called to process generic motion events such as joystick movements.</p>
60 </li>
61 <li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)}
62 <p>Called to process a press of a physical key such as a gamepad or
63 D-pad button.</p>
64 </li>
65 <li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)}
66 <p>Called to process a release of a physical key such as a gamepad or
67 D-pad button.</p>
68 </li>
69 </ul>
70</li>
71</ul>
72
73<p>The recommended approach is to capture the events from the
74 specific {@link android.view.View} object that the user interacts with.
75 Inspect the following objects provided by the callbacks to get information
76 about the type of input event received:</p>
77
78<dl>
79<dt>{@link android.view.KeyEvent}</dt>
80 <dd>An object that describes directional
81pad</a> (D-pad) and gamepad button events. Key events are accompanied by a
82<em>key code</em> that indicates the specific button triggered, such as
83{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}
84or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the
85key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key
86event callbacks such as
87{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}.
88<dd>
89<dt>{@link android.view.MotionEvent}</dt>
90 <dd>An object that describes input from joystick and shoulder trigger
91 movements. Motion events are accompanied by an action code and a set of
92<em>axis values</em>. The action code specifies the state change that occurred
93such as a joystick being moved. The axis values describe the position and other
94movement properties for a specific physical control, such as
95{@link android.view.MotionEvent#AXIS_X} or
96{@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code
97by calling {@link android.view.MotionEvent#getAction()} and the axis value by
98calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}.
99<dd>
100</dl>
101<p>This lesson focuses on how you can handle input from the most common types of
102physical controls (gamepad buttons, directional pads, and
103joysticks) in a game screen by implementing the above-mentioned
104{@link android.view.View} callback methods and processing
105{@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p>
106
107<h2 id="input">Verify a Game Controller is Connected</h2>
108<p>When reporting input events, Android does not distinguish
109between events that came from a non-game controller device and events that came
110from a game controller. For example, a touch screen action generates an
111{@link android.view.MotionEvent#AXIS_X} event that represents the X
112coordinate 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
113your game cares about handling game-controller input, you should first check
114that the input event comes from a relevant source type.</p>
115<p>To verify that a connected input device is a game controller, call
116{@link android.view.InputDevice#getSources()} to obtain a combined bit field of
117input source types supported on that device. You can then test to see if
118the following fields are set:</p>
119<ul>
120<li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates
121that the input device has gamepad buttons (for example,
122{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source
123type does not strictly indicate if the game controller has D-pad buttons,
124although most gamepads typically have directional controls.</li>
125<li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that
126the input device has D-pad buttons (for example,
127{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li>
128<li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK}
129indicates that the input device has analog control sticks (for example, a
130joystick that records movements along {@link android.view.MotionEvent#AXIS_X}
131and {@link android.view.MotionEvent#AXIS_Y}).</li>
132</ul>
133<p>The following code snippet shows a helper method that lets you check whether
134 the connected input devices are game controllers. If so, the method retrieves
135 the device IDs for the game controllers. You can then associate each device
136 ID with a player in your game, and process game actions for each connected
137 player separately. To learn more about supporting multiple game controllers
138 that are simultaneously connected on the same Android device, see
139 <a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p>
140<pre>
141public ArrayList<Integer> getGameControllerIds() {
142 ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
143 int[] deviceIds = InputDevice.getDeviceIds();
144 for (int deviceId : deviceIds) {
145 InputDevice dev = InputDevice.getDevice(deviceId);
146 int sources = dev.getSources();
147
148 // Verify that the device has gamepad buttons, control sticks, or both.
149 if (((sources &amp; InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
150 || ((sources &amp; InputDevice.SOURCE_JOYSTICK)
151 == InputDevice.SOURCE_JOYSTICK)) {
152 // This device is a game controller. Store its device ID.
153 if (!gameControllerDeviceIds.contains(deviceId)) {
154 gameControllerDeviceIds.add(deviceId);
155 }
156 }
157 }
158 return gameControllerDeviceIds;
159}
160</pre>
161<p>Additionally, you might want to check for individual input capabilities
162supported by a connected game controller. This might be useful, for example, if
163you want your game to use only input from the set of physical controls it
164understands.</p>
165<p>To detect if a specific key code or axis code is supported by a connected
166game controller, use these techniques:</p>
167<ul>
168<li>In Android 4.4 (API level 19) or higher, you can determine if a key code is
169supported on a connected game controller by calling
170{@link android.view.InputDevice#hasKeys(int...)}.</li>
171<li>In Android 3.1 (API level 12) or higher, you can find all available axes
172supported on a connected game controller by first calling
173{@link android.view.InputDevice#getMotionRanges()}. Then, on each
174{@link android.view.InputDevice.MotionRange} object returned, call
175{@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li>
176</ul>
177
178<h2 id="button">Process Gamepad Button Presses</h2>
179<p>Figure 1 shows how Android maps key codes and axis values to the physical
180controls on most game controllers.</p>
181<img src="{@docRoot}images/training/game-controller-profiles.png" alt=""
182id="figure1" />
183<p class="img-caption">
184 <strong>Figure 1.</strong> Profile for a generic game controller.
185</p>
186<p>The callouts in the figure refer to the following:</p>
187<div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;">
188<ol style="margin-left:30px;list-style:decimal;">
189<li>{@link android.view.MotionEvent#AXIS_HAT_X},
190{@link android.view.MotionEvent#AXIS_HAT_Y},
191{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP},
192{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN},
193{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT},
194{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT}
195</li>
196<li>{@link android.view.MotionEvent#AXIS_X},
197{@link android.view.MotionEvent#AXIS_Y},
198{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li>
199<li>{@link android.view.MotionEvent#AXIS_Z},
200{@link android.view.MotionEvent#AXIS_RZ},
201{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li>
202<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li>
203<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li>
204<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li>
205<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li>
206<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li>
207<li>{@link android.view.MotionEvent#AXIS_RTRIGGER},
208{@link android.view.MotionEvent#AXIS_THROTTLE}</li>
209<li>{@link android.view.MotionEvent#AXIS_LTRIGGER},
210{@link android.view.MotionEvent#AXIS_BRAKE}</li>
211<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li>
212</ol>
213</div>
214<p>Common key codes generated by gamepad button presses include
215 {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
216{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
217{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
218and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game
219controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER
220DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your
221game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()}
222or from key event callbacks such as
223{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()},
224and if it represents an event that is relevant to your game, process it as a
225game action. Table 1 lists the recommended game actions for the most common
226gamepad buttons.
227</p>
228
229<p class="table-caption" id="table1">
230 <strong>Table 1.</strong> Recommended game actions for gamepad
231buttons.</p>
232<table>
233 <tr>
234 <th scope="col">Game Action</th>
235 <th scope="col">Button Key Code</th>
236 </tr>
237 <tr>
238 <td>Start game in main menu, or pause/unpause during game</td>
239 <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}</td>
240 </tr>
241 <tr>
242 <td>Display menu</td>
243 <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT} and
244{@link android.view.KeyEvent#KEYCODE_MENU}</td>
245 </tr>
246 <tr>
247 <td>Same as Android <em>Back</em></td>
248 <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>*</sup> and
249{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td>
250 </tr>
251 <tr>
252 <td>Confirm selection, or perform primary game action</td>
253 <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>*</sup> and
254{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td>
255 </tr>
256</table>
257<p>
258<em>* This could be the opposite button (A/B), depending on the locale that
259you are supporting.</em>
260</p>
261
262<p class="note"><strong>Tip: </strong>Consider providing a configuration screen
263in your game to allow users to personalize their own game controller mappings for
264game actions.</p>
265
266<p>The following snippet shows how you might override
267{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to
268associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and
269{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses
270with a game action.
271</p>
272<pre>
273public class GameView extends View {
274 ...
275
276 &#64;Override
277 public boolean onKeyDown(int keyCode, KeyEvent event) {
278 boolean handled = false;
279 if ((event.getSource() &amp; InputDevice.SOURCE_GAMEPAD)
280 == InputDevice.SOURCE_GAMEPAD) {
281 if (event.getRepeatCount() == 0) {
282 switch (keyCode) {
283 // Handle gamepad and D-pad button presses to
284 // navigate the ship
285 ...
286
287 default:
288 if (isFireKey(keyCode)) {
289 // Update the ship object to fire lasers
290 ...
291 handled = true;
292 }
293 break;
294 }
295 }
296 if (handled) {
297 return true;
298 }
299 }
300 return super.onKeyDown(keyCode, event);
301 }
302
303 private static boolean isFireKey(int keyCode) {
304 // Here we treat Button_A and DPAD_CENTER as the primary action
305 // keys for the game. You may need to switch this to Button_B and
306 // DPAD_CENTER depending on the user expectations for the locale
307 // in which your game runs.
308 return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
309 || keyCode == KeyEvent.KEYCODE_BUTTON_A;
310 }
311}
312</pre>
313
314<p>Follow these best practices when handling button presses:</p>
315<ul>
316<li><strong>Provide localized button mappings.</strong> Generally, if your game
317has a primary gameplay action (for example, it fires lasers, lets your avatar
318do a high jump, or confirms an item selection), you should map
319both {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} and
320{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to this action. However,
321in some locales, users may expect
322{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B} to be the confirm
323button and {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to be the
324back button instead. If you are supporting these locales, make sure to treat
325the A and B buttons accordingly in your game. To determine the user's locale,
326call the {@link java.util.Locale#getDefault()} method.
327<li><strong>Map {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}
328consistently across different Android versions.</strong> On Android 4.2 (API
329level 17) and lower, the system treats
330{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android
331<em>Back</em> key by default. If your app supports these Android
332versions, make sure to treat
333{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game
334action (except in the localization case mentioned
335above). To determine the current Android SDK
336version on the device, refer to the
337{@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value.
338</li>
339</ul>
340
341<h2 id="dpad">Process Directional Pad Input</h2>
342<p>The 4-way directional pad (D-pad) is a common physical control in many game
343controllers. Android reports D-pad UP and DOWN presses as
344{@link android.view.MotionEvent#AXIS_HAT_Y} events with a range
345from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as
346{@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0
347(left) to 1.0 (right).</p>
348<p>Some controllers instead report D-pad presses with a key code. If your game
349cares about D-pad presses, you should treat the hat axis events and the D-pad
350key codes as the same input events, as recommended in table 2.</p>
351<p class="table-caption" id="table2">
352 <strong>Table 2.</strong> Recommended default game actions for D-pad key
353 codes and hat axis values.</p>
354<table>
355 <tr>
356 <th scope="col">Game Action</th>
357 <th scope="col">D-pad Key Code</th>
358 <th scope="col">Hat Axis Code</th>
359 </tr>
360 <tr>
361 <td>Move Up</td>
362 <td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td>
363 <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td>
364 </tr>
365 <tr>
366 <td>Move Down</td>
367 <td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td>
368 <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td>
369 </tr>
370 <tr>
371 <td>Move Left</td>
372 <td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td>
373 <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td>
374 </tr>
375 <tr>
376 <td>Move Right</td>
377 <td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td>
378 <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td>
379 </tr>
380</table>
381
382
383<p>The following code snippet shows a helper class that lets you check the hat
384axis and key code values from an input event to determine the D-pad direction.
385</p>
386<pre>
387public class Dpad {
388 final static int UP = 0;
389 final static int LEFT = 1;
390 final static int RIGHT = 2;
391 final static int DOWN = 3;
392 final static int CENTER = 4;
393
394 int directionPressed = -1; // initialized to -1
395
396 public int getDirectionPressed(InputEvent event) {
397 if (!isDpadDevice(event)) {
398 return -1;
399 }
400
401 // If the input event is a MotionEvent, check its hat axis values.
402 if (event instanceof MotionEvent) {
403
404 // Use the hat axis value to find the D-pad direction
405 MotionEvent motionEvent = (MotionEvent) event;
406 float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
407 float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);
408
409 // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
410 // LEFT and RIGHT direction accordingly.
411 if (Float.compare(xaxis, -1.0f) == 0) {
412 directionPressed = Dpad.LEFT;
413 } else if (Float.compare(xaxis, 1.0f) == 0) {
414 directionPressed = Dpad.RIGHT;
415 }
416 // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
417 // UP and DOWN direction accordingly.
418 else if (Float.compare(yaxis, -1.0f) == 0) {
419 directionPressed = Dpad.UP;
420 } else if (Float.compare(yaxis, -1.0f) == 0) {
421 directionPressed = Dpad.DOWN;
422 }
423 }
424
425 // If the input event is a KeyEvent, check its key code.
426 else if (event instanceof KeyEvent) {
427
428 // Use the key code to find the D-pad direction.
429 KeyEvent keyEvent = (KeyEvent) event;
430 if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
431 directionPressed = Dpad.LEFT;
432 } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
433 directionPressed = Dpad.RIGHT;
434 } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
435 directionPressed = Dpad.UP;
436 } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
437 directionPressed = Dpad.DOWN;
438 } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
439 directionPressed = Dpad.CENTER;
440 }
441 }
442 return directionPressed;
443 }
444
445 public static boolean isDpadDevice(InputEvent event) {
446 // Check that input comes from a device with directional pads.
447 if ((event.getSource() &amp; InputDevice.SOURCE_DPAD)
448 != InputDevice.SOURCE_DPAD) {
449 return true;
450 } else {
451 return false;
452 }
453 }
454}
455</pre>
456
457<p>You can use this helper class in your game wherever you want to process
458 D-pad input (for example, in the
459{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
460onGenericMotionEvent()} or
461{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}
462callbacks).</p>
463<p>For example:</p>
464<pre>
465Dpad mDpad = new Dpad();
466...
467&#64;Override
468public boolean onGenericMotionEvent(MotionEvent event) {
469
470 // Check if this event if from a D-pad and process accordingly.
471 if (Dpad.isDpadDevice(event)) {
472
473 int press = mDpad.getDirectionPressed(event);
474 switch (press) {
475 case LEFT:
476 // Do something for LEFT direction press
477 ...
478 return true;
479 case RIGHT:
480 // Do something for RIGHT direction press
481 ...
482 return true;
483 case UP:
484 // Do something for UP direction press
485 ...
486 return true;
487 ...
488 }
489 }
490
491 // Check if this event is from a joystick movement and process accordingly.
492 ...
493}
494</pre>
495
496<h2 id="joystick">Process Joystick Movements</h2>
497<p>When players move a joystick on their game controllers, Android reports a
498{@link android.view.MotionEvent} that contains the
499{@link android.view.MotionEvent#ACTION_MOVE} action code and the updated
500positions of the joystick's axes. Your game can use the data provided by
501the {@link android.view.MotionEvent} to determine if a joystick movement it
502cares about happened.
503</p>
504<p>Note that joystick motion events may batch multiple movement samples together
505within a single object. The {@link android.view.MotionEvent} object contains
506the current position for each joystick axis as well as multiple historical
507positions for each axis. When reporting motion events with action code {@link android.view.MotionEvent#ACTION_MOVE} (such as joystick movements), Android batches up the
508axis values for efficiency. The historical values for an axis consists of the
509set of distinct values older than the current axis value, and more recent than
510values reported in any previous motion events. See the
511{@link android.view.MotionEvent} reference for details.</p>
512<p>You can use the historical information to more accurately render a game
513object's movement based on the joystick input. To
514retrieve the current and historical values, call
515{@link android.view.MotionEvent#getAxisValue(int)
516getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int,
517int) getHistoricalAxisValue()}. You can also find the number of historical
518points in the joystick event by calling
519{@link android.view.MotionEvent#getHistorySize()}.</p>
520<p>The following snippet shows how you might override the
521{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
522onGenericMotionEvent()} callback to process joystick input. You should first
523process the historical values for an axis, then process its current position.
524</p>
525<pre>
526public class GameView extends View {
527
528 &#64;Override
529 public boolean onGenericMotionEvent(MotionEvent event) {
530
531 // Check that the event came from a game controller
532 if ((event.getSource() &amp; InputDevice.SOURCE_JOYSTICK) ==
533 InputDevice.SOURCE_JOYSTICK &amp;&amp;
534 event.getAction() == MotionEvent.ACTION_MOVE)
535
536 // Process all historical movement samples in the batch
537 final int historySize = event.getHistorySize();
538
539 // Process the movements starting from the
540 // earliest historical position in the batch
541 for (int i = 0; i &lt; historySize; i++) {
542 // Process the event at historical position i
543 processJoystickInput(event, i);
544 }
545
546 // Process the current movement sample in the batch (position -1)
547 processJoystickInput(event, -1);
548 return true;
549 }
550 return super.onGenericMotionEvent(event);
551 }
552}
553</pre>
554<p>Before using joystick input, you need to determine if the joystick is
555centered, then calculate its axis movements accordingly. Joysticks typically
556have a <em>flat</em> area, that is, a range of values near the (0,0) coordinate
557at which the axis is considered to be centered. If the axis value reported by
558Android falls within the flat area, you should treat the controller to be at
559rest (that is, motionless along both axes).</p>
560<p>The snippet below shows a helper method that calculates the movement along
561each axis. You invoke this helper in the {@code processJoystickInput()} method
562described further below.
563</p>
564<pre>
565private static float getCenteredAxis(MotionEvent event,
566 InputDevice device, int axis, int historyPos) {
567 final InputDevice.MotionRange range =
568 device.getMotionRange(axis, event.getSource());
569
570 // A joystick at rest does not always report an absolute position of
571 // (0,0). Use the getFlat() method to determine the range of values
572 // bounding the joystick axis center.
573 if (range != null) {
574 final float flat = range.getFlat();
575 final float value =
576 historyPos &lt; 0 ? event.getAxisValue(axis):
577 event.getHistoricalAxisValue(axis, historyPos);
578
579 // Ignore axis values that are within the 'flat' region of the
580 // joystick axis center.
581 if (Math.abs(value) > flat) {
582 return value;
583 }
584 }
585 return 0;
586}
587</pre>
588<p>Putting it all together, here is how you might process joystick movements in
589your game:</p>
590<pre>
591private void processJoystickInput(MotionEvent event,
592 int historyPos) {
593
594 InputDevice mInputDevice = event.getDevice();
595
596 // Calculate the horizontal distance to move by
597 // using the input value from one of these physical controls:
598 // the left control stick, hat axis, or the right control stick.
599 float x = getCenteredAxis(event, mInputDevice,
600 MotionEvent.AXIS_X, historyPos);
601 if (x == 0) {
602 x = getCenteredAxis(event, mInputDevice,
603 MotionEvent.AXIS_HAT_X, historyPos);
604 }
605 if (x == 0) {
606 x = getCenteredAxis(event, mInputDevice,
607 MotionEvent.AXIS_Z, historyPos);
608 }
609
610 // Calculate the vertical distance to move by
611 // using the input value from one of these physical controls:
612 // the left control stick, hat switch, or the right control stick.
613 float y = getCenteredAxis(event, mInputDevice,
614 MotionEvent.AXIS_Y, historyPos);
615 if (y == 0) {
616 y = getCenteredAxis(event, mInputDevice,
617 MotionEvent.AXIS_HAT_Y, historyPos);
618 }
619 if (y == 0) {
620 y = getCenteredAxis(event, mInputDevice,
621 MotionEvent.AXIS_RZ, historyPos);
622 }
623
624 // Update the ship object based on the new x and y values
625 ...
626
627 return true;
628}
629</pre>
630<p>To support game controllers that have more sophisticated
631features beyond a single joystick, follow these best practices: </p>
632<ul>
633<li><strong>Handle dual controller sticks.</strong> Many game controllers have
634both a left and right joystick. For the left stick, Android
635reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events
636and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events.
637For the right stick, Android reports horizontal movements as
638{@link android.view.MotionEvent#AXIS_Z} events and vertical movements as
639{@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle
640both controller sticks in your code.</li>
641<li><strong>Handle shoulder trigger presses (but provide alternative input
642methods).</strong> Some controllers have left and right shoulder
643triggers. If these triggers are present, Android reports a left trigger press
644as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a
645right trigger press as an
646{@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android
6474.3 (API level 18), a controller that produces a
648{@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an
649identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The
650same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and
651{@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger
652presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not
653all controllers have triggers, so consider allowing players to perform those
654game actions with other buttons.
655</li>
656</ul>