ถ้าคุณสนับสนุนตัวควบคุมเกมในเกม เป็นความรับผิดชอบของคุณ เพื่อให้แน่ใจว่าเกมจะตอบสนองต่อตัวควบคุม อย่างสอดคล้องกันในทุกอุปกรณ์ ที่ทำงานใน Android เวอร์ชันต่างๆ กัน วิธีนี้จะช่วยให้เกมเข้าถึง และผู้เล่นสามารถเพลิดเพลินกับประสบการณ์การเล่นเกมที่ราบรื่น แม้ในตอนที่เปลี่ยนหรืออัปเกรดอุปกรณ์ Android ก็ตาม
บทเรียนนี้สาธิตวิธีใช้ API ที่มีอยู่ใน Android 4.1 ขึ้นไป ด้วยวิธีที่เข้ากันได้แบบย้อนหลัง ซึ่งช่วยให้เกมของคุณรองรับ คุณลักษณะบนอุปกรณ์ที่ใช้ Android 3.1 ขึ้นไป
- เกมจะตรวจหาได้ว่ามีการเพิ่ม เปลี่ยนแปลง หรือนำตัวควบคุมเกมใหม่ออกหรือไม่
- เกมค้นหาความสามารถของเกมคอนโทรลเลอร์ได้
- เกมสามารถจดจำเหตุการณ์การเคลื่อนไหวที่เข้ามาจากตัวควบคุมเกม
ตัวอย่างในบทเรียนนี้อิงตามการใช้งานข้อมูลอ้างอิง
มาจากตัวอย่าง ControllerSample.zip
พร้อมให้ดาวน์โหลด
ที่ด้านบน ตัวอย่างนี้แสดงวิธีนำ InputManagerCompat
ไปใช้
เพื่อรองรับ Android เวอร์ชันต่างๆ ในการรวบรวมตัวอย่าง คุณจะต้อง
ต้องใช้ Android 4.1 (API ระดับ 16) ขึ้นไป เมื่อคอมไพล์แล้ว แอปตัวอย่าง
ทำงานบนอุปกรณ์ทั้งหมดที่ใช้ Android 3.1 (API ระดับ 12) ขึ้นไปเป็นบิลด์
เป้าหมาย
เตรียมพร้อมที่จะใช้ API สำหรับการสนับสนุนตัวควบคุมเกม
สมมติว่าคุณต้องการระบุได้ว่าการเชื่อมต่อของตัวควบคุมเกมหรือไม่ มีการเปลี่ยนแปลงในอุปกรณ์ที่ใช้ Android 3.1 (API ระดับ 12) อย่างไรก็ตาม API มีให้ใช้งานเฉพาะใน Android 4.1 (API ระดับ 16) ขึ้นไป ดังนั้น คุณต้องมีการติดตั้งใช้งาน ที่รองรับ Android 4.1 ขึ้นไป ให้บริการกลไกสำรองที่สนับสนุน Android 3.1 ถึง Android 4.0
เพื่อช่วยให้คุณทราบว่าคุณลักษณะใดต้องใช้กลไกสำรองสำหรับ เวอร์ชันเก่า ตารางที่ 1 แสดงความแตกต่างในการรองรับตัวควบคุมเกม ระหว่าง Android 3.1 (API ระดับ 12) และ 4.1 (ระดับ API 16)
ข้อมูลผู้ควบคุมข้อมูล | API ตัวควบคุม | API ระดับ 12 | API ระดับ 16 |
---|---|---|---|
การระบุอุปกรณ์ | getInputDeviceIds() |
• | |
getInputDevice() |
• | ||
getVibrator() |
• | ||
SOURCE_JOYSTICK |
• | • | |
SOURCE_GAMEPAD |
• | • | |
สถานะการเชื่อมต่อ | onInputDeviceAdded() |
• | |
onInputDeviceChanged() |
• | ||
onInputDeviceRemoved() |
• | ||
การระบุเหตุการณ์อินพุต | กด D-pad (
KEYCODE_DPAD_UP ,
KEYCODE_DPAD_DOWN ,
KEYCODE_DPAD_LEFT ,
KEYCODE_DPAD_RIGHT ,
KEYCODE_DPAD_CENTER ) |
• | • |
การกดปุ่มเกมแพด (
BUTTON_A ,
BUTTON_B ,
BUTTON_THUMBL ,
BUTTON_THUMBR ,
BUTTON_SELECT ,
BUTTON_START ,
BUTTON_R1
BUTTON_L1 ,
BUTTON_R2 ,
BUTTON_L2 ) |
• | • | |
การเคลื่อนที่ของจอยสติ๊กและหมวก (
AXIS_X ,
AXIS_Y ,
AXIS_Z ,
AXIS_RZ ,
AXIS_HAT_X ,
AXIS_HAT_Y ) |
• | • | |
การกดทริกเกอร์แบบแอนะล็อก (
AXIS_LTRIGGER ,
AXIS_RTRIGGER ) |
• | • |
คุณสามารถใช้ Abstraction เพื่อสร้างการรองรับตัวควบคุมเกมแบบรับรู้เวอร์ชัน ใช้งานได้ในหลายแพลตฟอร์ม วิธีการดังกล่าวมีขั้นตอนต่อไปนี้
- กำหนดอินเทอร์เฟซ Java ตัวกลางที่แอบนำการติดตั้ง ฟีเจอร์เกมคอนโทรลเลอร์ที่เกมต้องการ
- สร้างการใช้พร็อกซีสำหรับอินเทอร์เฟซที่ใช้ API ใน Android 4.1 ขึ้นไป
- สร้างการใช้งานอินเทอร์เฟซของคุณที่กำหนดเองซึ่งใช้ API ที่พร้อมใช้งาน ระหว่าง Android 3.1 ถึง Android 4.0
- สร้างตรรกะสำหรับการเปลี่ยนระหว่างการใช้งานเหล่านี้ขณะรันไทม์ และเริ่มใช้อินเทอร์เฟซในเกม
สำหรับภาพรวมเกี่ยวกับวิธีใช้นามธรรมเพื่อให้มั่นใจว่าแอปพลิเคชัน สามารถทำงานในลักษณะที่เข้ากันได้แบบย้อนหลังใน Android เวอร์ชันต่างๆ โปรดดู กำลังสร้าง UI ที่เข้ากันได้แบบย้อนหลัง
เพิ่มอินเทอร์เฟซสำหรับความเข้ากันได้แบบย้อนหลัง
หากต้องการระบุความเข้ากันได้แบบย้อนหลัง คุณสามารถสร้างอินเทอร์เฟซที่กำหนดเอง จากนั้น เพิ่มการติดตั้งใช้งานเฉพาะเวอร์ชัน ข้อดีอย่างหนึ่งของวิธีนี้คือ ช่วยให้คุณมิเรอร์อินเทอร์เฟซสาธารณะใน Android 4.1 (API ระดับ 16) ได้ รองรับตัวควบคุมเกม
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); } ... }
อินเทอร์เฟซของ InputManagerCompat
มีวิธีการต่อไปนี้
getInputDevice()
- กระจก
getInputDevice()
รับInputDevice
ที่แสดงถึงความสามารถของเกมคอนโทรลเลอร์ getInputDeviceIds()
- กระจก
getInputDeviceIds()
แสดงผลอาร์เรย์ของจำนวนเต็ม โดยแต่ละค่า ซึ่งเป็นรหัสสำหรับอุปกรณ์อินพุตอื่น ซึ่งจะเป็นประโยชน์หากคุณกำลังสร้าง เกมที่รองรับผู้เล่นหลายคน และคุณต้องการตรวจสอบจำนวน เชื่อมต่อกับตัวควบคุมแล้ว registerInputDeviceListener()
- กระจก
registerInputDeviceListener()
ช่วยให้คุณสามารถลงทะเบียน เพื่อรับการแจ้งเตือนเมื่อมี มีการเพิ่ม เปลี่ยนแปลง หรือนำอุปกรณ์ออก unregisterInputDeviceListener()
- กระจก
unregisterInputDeviceListener()
ยกเลิกการลงทะเบียน Listener ของอุปกรณ์อินพุต onGenericMotionEvent()
- กระจก
onGenericMotionEvent()
ให้เกมสกัดกั้นและจัดการ วัตถุMotionEvent
และค่าแกนที่แสดงเหตุการณ์ เช่น การเคลื่อนที่ของจอยสติ๊กและการกดทริกเกอร์แบบแอนะล็อก onPause()
- หยุดแบบสำรวจสำหรับเหตุการณ์ตัวควบคุมเกมเมื่อ กิจกรรมหลักจะหยุดชั่วคราว หรือเมื่อเกมไม่มีสมาธิอีกต่อไป
onResume()
- เริ่มแบบสำรวจกิจกรรมตัวควบคุมเกมเมื่อ กิจกรรมหลักจะกลับมาทำงานอีกครั้ง หรือเมื่อเกมเริ่มต้นและทำงาน เบื้องหน้า
InputDeviceListener
- มิเรอร์
InputManager.InputDeviceListener
ของ Google แจ้งให้เกมของคุณทราบเมื่อมีการเพิ่ม เปลี่ยนแปลงตัวควบคุมเกม หรือ ลบแล้ว
ต่อไป ให้สร้างการติดตั้งใช้งานสำหรับ InputManagerCompat
ที่ได้ผล
ในแพลตฟอร์มเวอร์ชันต่างๆ หากเกมของคุณใช้ Android 4.1 หรือ
สูงขึ้นและเรียกเมธอด InputManagerCompat
ซึ่งมีการใช้พร็อกซี
เรียกใช้เมธอดที่เทียบเท่าใน InputManager
อย่างไรก็ตาม หากเกมของคุณใช้ Android 3.1 ถึง Android 4.0 การใช้งานที่กำหนดเอง
ประมวลผลการเรียกไปยังเมธอด InputManagerCompat
โดยใช้
เฉพาะ API ที่เปิดตัวไม่เกิน Android 3.1 ไม่ว่าจะอัปโหลดแบบใด
การติดตั้งใช้งานเฉพาะเวอร์ชันจะถูกใช้ขณะรันไทม์ และการติดตั้งใช้งานก็จะผ่าน
ผลลัพธ์การโทรกลับไปที่เกมอย่างโปร่งใส
ใช้อินเทอร์เฟซบน Android 4.1 ขึ้นไป
InputManagerCompatV16
เป็นการปรับใช้
อินเทอร์เฟซ InputManagerCompat
ที่พร็อกซีเมธอดเรียกใช้เมธอด
ตามจริงคือ InputManager
และ InputManager.InputDeviceListener
ได้รับ InputManager
จากระบบ
Context
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 } }
ใช้อินเทอร์เฟซบน Android 3.1 ถึง Android 4.0
หากต้องการสร้างการใช้งาน InputManagerCompat
ที่รองรับ Android 3.1 ถึง Android 4.0 คุณสามารถใช้
ออบเจ็กต์ต่อไปนี้
SparseArray
ของรหัสอุปกรณ์เพื่อติดตาม เกมคอนโทรลเลอร์ที่เชื่อมต่อกับอุปกรณ์Handler
สำหรับประมวลผลเหตุการณ์ในอุปกรณ์ เมื่อแอปเริ่มทำงาน หรือกลับมาใช้งานอีกครั้งHandler
จะได้รับข้อความเพื่อเริ่มแบบสำรวจ สำหรับการยกเลิกการเชื่อมต่อตัวควบคุมเกมHandler
จะเริ่มต้น วนซ้ำเพื่อตรวจสอบตัวควบคุมเกมที่เชื่อมต่อที่รู้จักแต่ละรายการและดูว่ารหัสอุปกรณ์ ส่งคืนแล้ว ผลลัพธ์null
บ่งบอกว่าตัวควบคุมเกม ยกเลิกการเชื่อมต่อแล้วHandler
จะหยุดแบบสำรวจเมื่อแอป หยุดชั่วคราวแล้วMap
ของInputManagerCompat.InputDeviceListener
ออบเจ็กต์ คุณจะใช้ Listener เพื่ออัปเดตสถานะการเชื่อมต่อของที่ติดตาม เกมคอนโทรลเลอร์
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); } }
ใช้ออบเจ็กต์ PollingMessageHandler
ที่ขยาย
Handler
แล้วลบล้าง
handleMessage()
วิธีการนี้จะตรวจสอบว่าเกมคอนโทรลเลอร์ที่แนบอยู่
ยกเลิกการเชื่อมต่อแล้วและแจ้ง Listener ที่ลงทะเบียนไว้
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; } } }
หากต้องการเริ่มและหยุดการสำรวจการยกเลิกการเชื่อมต่อกับตัวควบคุมเกม ให้ลบล้าง วิธีการเหล่านี้
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); }
ในการตรวจหาว่ามีการเพิ่มอุปกรณ์อินพุต ให้ลบล้าง
onGenericMotionEvent()
วิธี เมื่อระบบรายงานเหตุการณ์การเคลื่อนไหว
ตรวจสอบว่าเหตุการณ์นี้มาจากรหัสอุปกรณ์ที่ติดตามอยู่แล้ว หรือจาก
รหัสอุปกรณ์ใหม่ หากรหัสอุปกรณ์เป็นเครื่องใหม่ ให้แจ้ง Listener ที่ลงทะเบียนไว้
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; }
การแจ้งเตือนของผู้ฟังสามารถทำได้โดยใช้
Handler
ออบเจ็กต์สำหรับส่ง DeviceEvent
Runnable
ในคิวข้อความ DeviceEvent
มีการอ้างอิงไปยัง InputManagerCompat.InputDeviceListener
วันและเวลา
DeviceEvent
ทำงาน ซึ่งเป็นเมธอด Callback ที่เหมาะสมของผู้ฟัง
จะถูกเรียกเพื่อส่งสัญญาณหากมีการเพิ่ม เปลี่ยนแปลง หรือนำตัวควบคุมเกมออก
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); } }
ขณะนี้คุณมีการติดตั้งใช้งาน InputManagerCompat
2 แบบ ได้แก่
ทำงานบนอุปกรณ์ที่ใช้ Android 4.1 ขึ้นไป
ใช้ได้กับอุปกรณ์ที่ใช้ Android 3.1 ถึง Android 4.0
ใช้การใช้งานเฉพาะเวอร์ชัน
มีการใช้ตรรกะการเปลี่ยนเฉพาะเวอร์ชันในคลาสที่ทำหน้าที่เป็น โรงงาน
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(); } } }
ตอนนี้คุณสามารถสร้างอินสแตนซ์ของออบเจ็กต์ InputManagerCompat
และ
จดทะเบียน InputManagerCompat.InputDeviceListener
ในบัตรหลัก
View
เนื่องจากตรรกะการสลับเวอร์ชันที่คุณตั้งค่าไว้
เกมของคุณจะใช้การติดตั้งใช้งานที่เหมาะกับ
ของ Android ที่อุปกรณ์ใช้อยู่
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); ... } }
ถัดไป ลบล้าง
onGenericMotionEvent()
ในมุมมองหลัก ตามที่อธิบายไว้ใน
จัดการ MotionEvent จากเกม
ตัวควบคุม เกมของคุณควรประมวลผลเหตุการณ์ตัวควบคุมเกมได้แล้ว
สอดคล้องกันในอุปกรณ์ที่ใช้ Android 3.1 (API ระดับ 12) ขึ้นไป
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); }
คุณสามารถดูการใช้งานโค้ดความเข้ากันได้นี้อย่างเต็มรูปแบบได้ใน
คลาส GameView
ที่ให้ไว้ในตัวอย่าง ControllerSample.zip
พร้อมให้ดาวน์โหลดด้านบน