Nếu đang hỗ trợ tay điều khiển trò chơi trong trò chơi của mình, bạn có trách nhiệm đảm bảo rằng trò chơi của bạn phản hồi tay điều khiển một cách nhất quán trên các thiết bị chạy trên các phiên bản Android khác nhau. Điều này giúp trò chơi của bạn tiếp cận được nhiều đối tượng hơn và người chơi có thể tận hưởng trải nghiệm chơi liền mạch bằng tay điều khiển ngay cả khi họ chuyển đổi hoặc nâng cấp thiết bị Android.
Bài học này minh hoạ cách sử dụng các API có trong Android 4.1 trở lên theo cách tương thích ngược, cho phép trò chơi của bạn hỗ trợ các tính năng sau trên các thiết bị chạy Android 3.1 trở lên:
- Trò chơi có thể phát hiện xem có tay điều khiển trò chơi mới được thêm, thay đổi hay xoá hay không.
- Trò chơi có thể truy vấn các chức năng của tay điều khiển trò chơi.
- Trò chơi có thể nhận ra các sự kiện chuyển động đến từ tay điều khiển trò chơi.
Chuẩn bị để trừu tượng hoá các API hỗ trợ tay điều khiển trò chơi
Giả sử bạn muốn xác định xem trạng thái kết nối của bộ điều khiển trò chơi có thay đổi trên các thiết bị chạy Android 3.1 (API cấp 12) hay không. Tuy nhiên, các API này chỉ có trong Android 4.1 (API cấp 16) trở lên, vì vậy, bạn cần cung cấp một phương thức triển khai hỗ trợ Android 4.1 trở lên, đồng thời cung cấp một cơ chế dự phòng hỗ trợ Android 3.1 đến Android 4.0.
Để giúp bạn xác định những tính năng nào cần cơ chế dự phòng cho các phiên bản thấp hơn, Bảng 1 liệt kê sự khác biệt về khả năng hỗ trợ bộ điều khiển trò chơi giữa Android 3.1 (API cấp 12) và 4.1 (API cấp 16).
Bảng 1. API để hỗ trợ tay điều khiển trò chơi trên nhiều phiên bản Android.
| Thông tin về tay điều khiển | Controller API | API cấp 12 | API cấp 16 |
|---|---|---|---|
| Thông tin nhận dạng thiết bị | getInputDeviceIds() |
• | |
getInputDevice() |
• | ||
getVibrator() |
• | ||
SOURCE_JOYSTICK |
• | • | |
SOURCE_GAMEPAD |
• | • | |
| Trạng thái kết nối | onInputDeviceAdded() |
• | |
onInputDeviceChanged() |
• | ||
onInputDeviceRemoved() |
• | ||
| Thông tin nhận dạng sự kiện đầu vào | Nhấn phím định hướng (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_CENTER) |
• | • |
Thao tác nhấn nút trên tay cầm chơi game (
BUTTON_A,
BUTTON_B,
BUTTON_THUMBL,
BUTTON_THUMBR,
BUTTON_SELECT,
BUTTON_START,
BUTTON_R1,
BUTTON_L1,
BUTTON_R2,
BUTTON_L2) |
• | • | |
Chuyển động của cần điều khiển và công tắc mũ (
AXIS_X,
AXIS_Y,
AXIS_Z,
AXIS_RZ,
AXIS_HAT_X,
AXIS_HAT_Y) |
• | • | |
Nhấn nút kích hoạt tương tự (
AXIS_LTRIGGER,
AXIS_RTRIGGER) |
• | • |
Bạn có thể sử dụng tính năng trừu tượng hoá để tạo tính năng hỗ trợ tay điều khiển trò chơi nhận biết phiên bản và hoạt động trên nhiều nền tảng. Phương pháp này bao gồm các bước sau:
- Xác định một giao diện Java trung gian để trừu tượng hoá việc triển khai các tính năng của tay điều khiển trò chơi mà trò chơi của bạn yêu cầu.
- Tạo một quy trình triển khai proxy cho giao diện của bạn bằng cách sử dụng các API trong Android 4.1 trở lên.
- Tạo một chế độ triển khai tuỳ chỉnh cho giao diện của bạn bằng cách sử dụng các API có sẵn từ Android 3.1 đến Android 4.0.
- Tạo logic để chuyển đổi giữa các quy trình triển khai này trong thời gian chạy và bắt đầu sử dụng giao diện trong trò chơi của bạn.
Để biết thông tin tổng quan về cách dùng phương pháp trừu tượng hoá để xác minh rằng các ứng dụng có thể hoạt động theo cách tương thích ngược trên nhiều phiên bản Android, hãy xem phần Tạo giao diện người dùng có khả năng tương thích ngược.
Thêm một giao diện để tương thích ngược
Để cung cấp khả năng tương thích ngược, bạn có thể tạo một giao diện tuỳ chỉnh rồi thêm các triển khai dành riêng cho phiên bản. Một lợi thế của phương pháp này là cho phép bạn phản chiếu các giao diện công khai trên Android 4.1 (API cấp 16) hỗ trợ tay điều khiển trò chơi.
Kotlin
// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
interface InputManagerCompat {
val inputDeviceIds: IntArray
fun getInputDevice(id: Int): InputDevice
fun registerInputDeviceListener(
listener: InputManager.InputDeviceListener,
handler: Handler?
)
fun unregisterInputDeviceListener(listener:InputManager.InputDeviceListener)
fun onGenericMotionEvent(event: MotionEvent)
fun onPause()
fun onResume()
interface InputDeviceListener {
fun onInputDeviceAdded(deviceId: Int)
fun onInputDeviceChanged(deviceId: Int)
fun onInputDeviceRemoved(deviceId: Int)
}
}
Java
// The InputManagerCompat interface is a reference example.
// The full code is provided in the ControllerSample.zip sample.
public interface InputManagerCompat {
...
public InputDevice getInputDevice(int id);
public int[] getInputDeviceIds();
public void registerInputDeviceListener(
InputManagerCompat.InputDeviceListener listener,
Handler handler);
public void unregisterInputDeviceListener(
InputManagerCompat.InputDeviceListener listener);
public void onGenericMotionEvent(MotionEvent event);
public void onPause();
public void onResume();
public interface InputDeviceListener {
void onInputDeviceAdded(int deviceId);
void onInputDeviceChanged(int deviceId);
void onInputDeviceRemoved(int deviceId);
}
...
}
Giao diện InputManagerCompat cung cấp các phương thức sau:
getInputDevice()- Gương
getInputDevice(). Lấy đối tượngInputDeviceđại diện cho các chức năng của một tay điều khiển trò chơi. getInputDeviceIds()- Gương
getInputDeviceIds(). Trả về một mảng số nguyên, mỗi số nguyên là một mã nhận dạng cho một thiết bị đầu vào khác. Điều này hữu ích nếu bạn đang tạo một trò chơi hỗ trợ nhiều người chơi và bạn muốn phát hiện số lượng tay điều khiển được kết nối. registerInputDeviceListener()- Gương
registerInputDeviceListener(). Cho phép bạn đăng ký nhận thông tin khi có thiết bị mới được thêm, thay đổi hoặc xoá. unregisterInputDeviceListener()- Gương
unregisterInputDeviceListener(). Huỷ đăng ký một trình xử lý sự kiện thiết bị đầu vào. onGenericMotionEvent()- Gương
onGenericMotionEvent(). Cho phép trò chơi của bạn chặn và xử lý các đối tượngMotionEventvà giá trị trục đại diện cho các sự kiện như chuyển động của cần điều khiển và lượt nhấn nút kích hoạt tương tự. onPause()- Dừng thăm dò các sự kiện của bộ điều khiển trò chơi khi hoạt động chính bị tạm dừng hoặc khi trò chơi không còn được chú ý.
onResume()- Bắt đầu thăm dò các sự kiện của tay điều khiển trò chơi khi hoạt động chính được tiếp tục hoặc khi trò chơi bắt đầu và chạy ở nền trước.
InputDeviceListener- Phản ánh giao diện
InputManager.InputDeviceListener. Cho phép trò chơi của bạn biết khi nào tay điều khiển trò chơi được thêm, thay đổi hoặc xoá.
Tiếp theo, hãy tạo các phương thức triển khai cho InputManagerCompat hoạt động trên nhiều phiên bản nền tảng. Nếu trò chơi của bạn đang chạy trên Android 4.1 trở lên và gọi một phương thức InputManagerCompat, thì quá trình triển khai proxy sẽ gọi phương thức tương đương trong InputManager.
Tuy nhiên, nếu trò chơi của bạn đang chạy trên Android 3.1 đến Android 4.0, thì quy trình triển khai tuỳ chỉnh sẽ xử lý các lệnh gọi đến phương thức InputManagerCompat chỉ bằng cách sử dụng các API được giới thiệu không muộn hơn Android 3.1. Bất kể việc triển khai dành riêng cho phiên bản nào được dùng trong thời gian chạy, việc triển khai sẽ chuyển kết quả gọi lại một cách minh bạch cho trò chơi.
Triển khai giao diện trên Android 4.1 trở lên
InputManagerCompatV16 là một phương thức triển khai của giao diện InputManagerCompat, giao diện này sẽ uỷ quyền các lệnh gọi phương thức cho một InputManager và InputManager.InputDeviceListener thực tế.
InputManager được lấy từ Context của hệ thống.
Kotlin
// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16(
context: Context,
private val inputManager: InputManager =
context.getSystemService(Context.INPUT_SERVICE) as InputManager,
private val listeners:
MutableMap<InputManager.InputDeviceListener, V16InputDeviceListener> = mutableMapOf()
) : InputManagerCompat {
override val inputDeviceIds: IntArray = inputManager.inputDeviceIds
override fun getInputDevice(id: Int): InputDevice = inputManager.getInputDevice(id)
override fun registerInputDeviceListener(
listener: InputManager.InputDeviceListener,
handler: Handler?
) {
V16InputDeviceListener(listener).also { v16listener ->
inputManager.registerInputDeviceListener(v16listener, handler)
listeners += listener to v16listener
}
}
// Do the same for unregistering an input device listener
...
override fun onGenericMotionEvent(event: MotionEvent) {
// unused in V16
}
override fun onPause() {
// unused in V16
}
override fun onResume() {
// unused in V16
}
}
class V16InputDeviceListener(
private val idl: InputManager.InputDeviceListener
) : InputManager.InputDeviceListener {
override fun onInputDeviceAdded(deviceId: Int) {
idl.onInputDeviceAdded(deviceId)
}
// Do the same for device change and removal
...
}
Java
// The InputManagerCompatV16 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV16 implements InputManagerCompat {
private final InputManager inputManager;
private final Map<InputManagerCompat.InputDeviceListener,
V16InputDeviceListener> listeners;
public InputManagerV16(Context context) {
inputManager = (InputManager)
context.getSystemService(Context.INPUT_SERVICE);
listeners = new HashMap<InputManagerCompat.InputDeviceListener,
V16InputDeviceListener>();
}
@Override
public InputDevice getInputDevice(int id) {
return inputManager.getInputDevice(id);
}
@Override
public int[] getInputDeviceIds() {
return inputManager.getInputDeviceIds();
}
static class V16InputDeviceListener implements
InputManager.InputDeviceListener {
final InputManagerCompat.InputDeviceListener mIDL;
public V16InputDeviceListener(InputDeviceListener idl) {
mIDL = idl;
}
@Override
public void onInputDeviceAdded(int deviceId) {
mIDL.onInputDeviceAdded(deviceId);
}
// Do the same for device change and removal
...
}
@Override
public void registerInputDeviceListener(InputDeviceListener listener,
Handler handler) {
V16InputDeviceListener v16Listener = new
V16InputDeviceListener(listener);
inputManager.registerInputDeviceListener(v16Listener, handler);
listeners.put(listener, v16Listener);
}
// Do the same for unregistering an input device listener
...
@Override
public void onGenericMotionEvent(MotionEvent event) {
// unused in V16
}
@Override
public void onPause() {
// unused in V16
}
@Override
public void onResume() {
// unused in V16
}
}
Triển khai giao diện trên Android 3.1 đến Android 4.0
Để tạo một phương thức triển khai InputManagerCompat hỗ trợ Android 3.1 đến Android 4.0, bạn có thể sử dụng các đối tượng sau:
- Một
SparseArraygồm các mã nhận dạng thiết bị để theo dõi những tay điều khiển trò chơi được kết nối với thiết bị. - Một
Handlerđể xử lý các sự kiện trên thiết bị. Khi một ứng dụng được khởi động hoặc tiếp tục,Handlersẽ nhận được một thông báo để bắt đầu thăm dò tình trạng ngắt kết nối của tay điều khiển trò chơi.Handlersẽ bắt đầu một vòng lặp để kiểm tra từng tay điều khiển trò chơi đã kết nối mà hệ thống biết và xem có mã nhận dạng thiết bị nào được trả về hay không. Giá trị trả vềnullcho biết tay điều khiển trò chơi đã bị ngắt kết nối.Handlersẽ ngừng thăm dò khi ứng dụng bị tạm dừng. Một
MapgồmInputManagerCompat.InputDeviceListenerđối tượng. Bạn sẽ dùng các trình nghe này để cập nhật trạng thái kết nối của các tay điều khiển trò chơi được theo dõi.
Kotlin
// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
class InputManagerV9(
val devices: SparseArray<Array<Long>> = SparseArray(),
private val listeners:
MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
private val defaultHandler: Handler = PollingMessageHandler(this)
…
}
Java
// The InputManagerCompatV9 class is a reference implementation.
// The full code is provided in the ControllerSample.zip sample.
public class InputManagerV9 implements InputManagerCompat {
private final SparseArray<long[]> devices;
private final Map<InputDeviceListener, Handler> listeners;
private final Handler defaultHandler;
…
public InputManagerV9() {
devices = new SparseArray<long[]>();
listeners = new HashMap<InputDeviceListener, Handler>();
defaultHandler = new PollingMessageHandler(this);
}
}
Triển khai một đối tượng PollingMessageHandler mở rộng Handler và ghi đè phương thức handleMessage(). Phương thức này kiểm tra xem tay điều khiển trò chơi đã kết nối có bị ngắt kết nối hay không và thông báo cho các trình nghe đã đăng ký.
Kotlin
private class PollingMessageHandler(
inputManager: InputManagerV9,
private val mInputManager: WeakReference<InputManagerV9> = WeakReference(inputManager)
) : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
MESSAGE_TEST_FOR_DISCONNECT -> {
mInputManager.get()?.also { imv ->
val time = SystemClock.elapsedRealtime()
val size = imv.devices.size()
for (i in 0 until size) {
imv.devices.valueAt(i)?.also { lastContact ->
if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
// check to see if the device has been
// disconnected
val id = imv.devices.keyAt(i)
if (null == InputDevice.getDevice(id)) {
// Notify the registered listeners
// that the game controller is disconnected
imv.devices.remove(id)
} else {
lastContact[0] = time
}
}
}
}
sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
}
}
}
}
}
Java
private static class PollingMessageHandler extends Handler {
private final WeakReference<InputManagerV9> inputManager;
PollingMessageHandler(InputManagerV9 im) {
inputManager = new WeakReference<InputManagerV9>(im);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESSAGE_TEST_FOR_DISCONNECT:
InputManagerV9 imv = inputManager.get();
if (null != imv) {
long time = SystemClock.elapsedRealtime();
int size = imv.devices.size();
for (int i = 0; i < size; i++) {
long[] lastContact = imv.devices.valueAt(i);
if (null != lastContact) {
if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
// check to see if the device has been
// disconnected
int id = imv.devices.keyAt(i);
if (null == InputDevice.getDevice(id)) {
// Notify the registered listeners
// that the game controller is disconnected
imv.devices.remove(id);
} else {
lastContact[0] = time;
}
}
}
}
sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
CHECK_ELAPSED_TIME);
}
break;
}
}
}
Để bắt đầu và dừng quá trình thăm dò về trạng thái ngắt kết nối của tay điều khiển trò chơi, hãy ghi đè các phương thức sau:
Kotlin
private const val MESSAGE_TEST_FOR_DISCONNECT = 101
private const val CHECK_ELAPSED_TIME = 3000L
class InputManagerV9(
val devices: SparseArray<Array<Long>> = SparseArray(),
private val listeners:
MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
...
override fun onPause() {
defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT)
}
override fun onResume() {
defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, CHECK_ELAPSED_TIME)
}
...
}
Java
private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
private static final long CHECK_ELAPSED_TIME = 3000L;
@Override
public void onPause() {
defaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
}
@Override
public void onResume() {
defaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
CHECK_ELAPSED_TIME);
}
Để phát hiện thấy một thiết bị đầu vào đã được thêm, hãy ghi đè phương thức onGenericMotionEvent(). Khi hệ thống báo cáo một sự kiện chuyển động, hãy kiểm tra xem sự kiện này có đến từ một mã nhận dạng thiết bị đã được theo dõi hay từ một mã nhận dạng thiết bị mới. Nếu mã nhận dạng thiết bị là mã nhận dạng mới, hãy thông báo cho các trình nghe đã đăng ký.
Kotlin
override fun onGenericMotionEvent(event: MotionEvent) {
// detect new devices
val id = event.deviceId
val timeArray: Array<Long> = mDevices.get(id) ?: run {
// Notify the registered listeners that a game controller is added
...
arrayOf<Long>().also {
mDevices.put(id, it)
}
}
timeArray[0] = SystemClock.elapsedRealtime()
}
Java
@Override
public void onGenericMotionEvent(MotionEvent event) {
// detect new devices
int id = event.getDeviceId();
long[] timeArray = mDevices.get(id);
if (null == timeArray) {
// Notify the registered listeners that a game controller is added
...
timeArray = new long[1];
mDevices.put(id, timeArray);
}
long time = SystemClock.elapsedRealtime();
timeArray[0] = time;
}
Thông báo về trình nghe được triển khai bằng cách sử dụng đối tượng Handler để gửi đối tượng DeviceEvent
Runnable đến hàng đợi thông báo.
DeviceEvent chứa một tệp tham chiếu đến InputManagerCompat.InputDeviceListener. Khi DeviceEvent chạy, phương thức gọi lại thích hợp của trình nghe sẽ được gọi để báo hiệu xem bộ điều khiển trò chơi đã được thêm, thay đổi hay xoá.
Kotlin
class InputManagerV9(
val devices: SparseArray<Array<Long>> = SparseArray(),
private val listeners:
MutableMap<InputManager.InputDeviceListener, Handler> = mutableMapOf()
) : InputManagerCompat {
...
override fun registerInputDeviceListener(
listener: InputManager.InputDeviceListener,
handler: Handler?
) {
listeners[listener] = handler ?: defaultHandler
}
override fun unregisterInputDeviceListener(listener: InputManager.InputDeviceListener) {
listeners.remove(listener)
}
private fun notifyListeners(why: Int, deviceId: Int) {
// the state of some device has changed
listeners.forEach { listener, handler ->
DeviceEvent.getDeviceEvent(why, deviceId, listener).also {
handler?.post(it)
}
}
}
...
}
private val sObjectQueue: Queue<DeviceEvent> = ArrayDeque<DeviceEvent>()
private class DeviceEvent(
private var mMessageType: Int,
private var mId: Int,
private var mListener: InputManager.InputDeviceListener
) : Runnable {
companion object {
fun getDeviceEvent(messageType: Int, id: Int, listener: InputManager.InputDeviceListener) =
sObjectQueue.poll()?.apply {
mMessageType = messageType
mId = id
mListener = listener
} ?: DeviceEvent(messageType, id, listener)
}
override fun run() {
when(mMessageType) {
ON_DEVICE_ADDED -> mListener.onInputDeviceAdded(mId)
ON_DEVICE_CHANGED -> mListener.onInputDeviceChanged(mId)
ON_DEVICE_REMOVED -> mListener.onInputDeviceChanged(mId)
else -> {
// Handle unknown message type
}
}
}
}
Java
@Override
public void registerInputDeviceListener(InputDeviceListener listener,
Handler handler) {
listeners.remove(listener);
if (handler == null) {
handler = defaultHandler;
}
listeners.put(listener, handler);
}
@Override
public void unregisterInputDeviceListener(InputDeviceListener listener) {
listeners.remove(listener);
}
private void notifyListeners(int why, int deviceId) {
// the state of some device has changed
if (!listeners.isEmpty()) {
for (InputDeviceListener listener : listeners.keySet()) {
Handler handler = listeners.get(listener);
DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
listener);
handler.post(odc);
}
}
}
private static class DeviceEvent implements Runnable {
private int mMessageType;
private int mId;
private InputDeviceListener mListener;
private static Queue<DeviceEvent> sObjectQueue =
new ArrayDeque<DeviceEvent>();
...
static DeviceEvent getDeviceEvent(int messageType, int id,
InputDeviceListener listener) {
DeviceEvent curChanged = sObjectQueue.poll();
if (null == curChanged) {
curChanged = new DeviceEvent();
}
curChanged.mMessageType = messageType;
curChanged.mId = id;
curChanged.mListener = listener;
return curChanged;
}
@Override
public void run() {
switch (mMessageType) {
case ON_DEVICE_ADDED:
mListener.onInputDeviceAdded(mId);
break;
case ON_DEVICE_CHANGED:
mListener.onInputDeviceChanged(mId);
break;
case ON_DEVICE_REMOVED:
mListener.onInputDeviceRemoved(mId);
break;
default:
// Handle unknown message type
...
break;
}
// Put this runnable back in the queue
sObjectQueue.offer(this);
}
}
Giờ đây, bạn có 2 cách triển khai InputManagerCompat: một cách hoạt động trên các thiết bị chạy Android 4.1 trở lên và một cách khác hoạt động trên các thiết bị chạy Android 3.1 đến Android 4.0.
Sử dụng quy trình triển khai dành riêng cho phiên bản
Logic chuyển đổi dành riêng cho phiên bản được triển khai trong một lớp đóng vai trò là một factory (nhà máy).
Kotlin
object Factory {
fun getInputManager(context: Context): InputManagerCompat =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
InputManagerV16(context)
} else {
InputManagerV9()
}
}
Java
public static class Factory {
public static InputManagerCompat getInputManager(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return new InputManagerV16(context);
} else {
return new InputManagerV9();
}
}
}
Giờ đây, bạn có thể tạo một đối tượng InputManagerCompat và đăng ký một InputManagerCompat.InputDeviceListener trong View chính của mình. Nhờ logic chuyển đổi phiên bản mà bạn thiết lập, trò chơi của bạn sẽ tự động sử dụng chế độ triển khai phù hợp với phiên bản Android mà thiết bị đang chạy.
Kotlin
class GameView(context: Context) : View(context), InputManager.InputDeviceListener {
private val inputManager: InputManagerCompat = Factory.getInputManager(context).apply {
registerInputDeviceListener(this@GameView, null)
...
}
...
}
Java
public class GameView extends View implements InputDeviceListener {
private InputManagerCompat inputManager;
...
public GameView(Context context, AttributeSet attrs) {
inputManager =
InputManagerCompat.Factory.getInputManager(this.getContext());
inputManager.registerInputDeviceListener(this, null);
...
}
}
Tiếp theo, hãy ghi đè phương thức onGenericMotionEvent() trong khung hiển thị chính, như mô tả trong phần Xử lý MotionEvent từ Tay điều khiển trò chơi trở lên). Giờ đây, trò chơi của bạn có thể xử lý các sự kiện của tay điều khiển trò chơi một cách nhất quán trên các thiết bị chạy Android 3.1 (cấp độ API).
Kotlin
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
inputManager.onGenericMotionEvent(event)
// Handle analog input from the controller as normal
...
return super.onGenericMotionEvent(event)
}
Java
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
inputManager.onGenericMotionEvent(event);
// Handle analog input from the controller as normal
...
return super.onGenericMotionEvent(event);
}
Bạn có thể tìm thấy một cách triển khai hoàn chỉnh mã tương thích này trong lớp GameView có trong mẫu ControllerSample.zip có thể tải xuống.