إذا كنت تدعم وحدات التحكم في الألعاب في لعبتك، تقع على عاتقك مسؤولية التأكد من أن لعبتك تستجيب لوحدات التحكم بشكل مستمر في جميع الأجهزة التي تعمل بإصدارات مختلفة من Android. يتيح ذلك إمكانية وصول لعبتك إلى شريحة أكبر من الجمهور، ويمكن للّاعبين الاستمتاع بتجربة لعب سلسة باستخدام وحدات التحكّم حتى عند تبديل أجهزة Android أو ترقيتها.
يشرح هذا الدرس كيفية استخدام واجهات برمجة التطبيقات المتوفّرة في الإصدار Android 4.1 والإصدارات الأحدث بطريقة متوافقة مع الأنظمة القديمة، ما يتيح لعبتك إمكانية إتاحة الميزات التالية على الأجهزة التي تعمل بالإصدار 3.1 من نظام التشغيل Android والإصدارات الأحدث:
- يمكن أن ترصد اللعبة ما إذا تمت إضافة وحدة تحكّم جديدة في الألعاب أو تغييرها أو إزالتها.
- يمكن أن تستعلم اللعبة عن إمكانات وحدة التحكم في الألعاب.
- يمكن للّعبة التعرّف على أحداث الحركة الواردة من وحدة تحكُّم الألعاب.
تستند الأمثلة في هذا الدرس إلى طريقة تنفيذ المرجع
التي يوفّرها نموذج ControllerSample.zip
المتاح للتنزيل
أعلاه. يعرض هذا النموذج طريقة تنفيذ واجهة InputManagerCompat
لتوافق مع إصدارات مختلفة من Android. لتجميع النموذج، عليك استخدام
Android 4.1 (المستوى 16 لواجهة برمجة التطبيقات) أو إصدار أحدث. بعد تجميع نموذج التطبيق، يتم تشغيله على أي جهاز يعمل بالإصدار Android 3.1 (المستوى 12 لواجهة برمجة التطبيقات) أو إصدار أحدث كهدف الإصدار.
الاستعداد لتلخيص واجهات برمجة التطبيقات للحصول على دعم وحدات تحكّم الألعاب
لنفترض أنّك تريد التمكّن من تحديد ما إذا كانت حالة اتصال وحدة التحكّم في الألعاب قد تغيّرت على الأجهزة التي تعمل بالإصدار 3.1 من Android (المستوى 12 من واجهة برمجة التطبيقات). ومع ذلك، لا تتوفر واجهات برمجة التطبيقات إلا في الإصدار Android 4.1 (المستوى 16 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لذا عليك توفير عملية تنفيذ تتوافق مع الإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث مع توفير آلية احتياطية تتوافق مع الإصدار 3.1 من نظام التشغيل Android وصولاً إلى الإصدار 4.0 من نظام التشغيل Android.
لمساعدتك في تحديد الميزات التي تتطلّب استخدام آلية احتياطية في الإصدارات الأقدم، يسرد الجدول 1 الاختلافات في إمكانية استخدام وحدات تحكّم الألعاب بين الإصدار 3.1 من نظام التشغيل Android (المستوى 12 من واجهة برمجة التطبيقات) والإصدار 4.1 (المستوى 16 من واجهة برمجة التطبيقات).
معلومات مسؤول التحكّم بالبيانات | واجهة برمجة تطبيقات وحدة التحكم | المستوى 12 من واجهة برمجة التطبيقات | المستوى 16 من واجهة برمجة التطبيقات |
---|---|---|---|
تحديد الجهاز | getInputDeviceIds() |
• | |
getInputDevice() |
• | ||
getVibrator() |
• | ||
SOURCE_JOYSTICK |
• | • | |
SOURCE_GAMEPAD |
• | • | |
حالة الاتصال | onInputDeviceAdded() |
• | |
onInputDeviceChanged() |
• | ||
onInputDeviceRemoved() |
• | ||
تحديد حدث الإدخال | الضغط على لوحة التحكّم (
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 ) |
• | • |
يمكنك استخدام التجريد في الحصول على دعم لوحدات تحكم الألعاب على مختلف الأنظمة الأساسية. يتضمن هذا النهج الخطوات التالية:
- حدد واجهة وسيطة Java تفسر تنفيذ ميزات وحدة التحكم في الألعاب التي تتطلبها اللعبة.
- يمكنك إنشاء تنفيذ خادم وكيل للواجهة التي تستخدم واجهات برمجة التطبيقات في الإصدار Android 4.1 والإصدارات الأحدث.
- يمكنك إنشاء تنفيذ مخصّص للواجهة يستخدم واجهات برمجة التطبيقات المتاحة بين الإصدار Android 3.1 وAndroid 4.0.
- أنشئ المنطق للتبديل بين عمليات التنفيذ هذه في وقت التشغيل، وابدأ باستخدام الواجهة في لعبتك.
للحصول على نظرة عامة حول طريقة استخدام التجريد لضمان عمل التطبيقات بطريقة متوافقة مع الأنظمة القديمة على مختلف إصدارات Android، يمكنك الاطّلاع على إنشاء واجهات مستخدم متوافقة مع الأنظمة القديمة.
إضافة واجهة للتوافق مع الأنظمة القديمة
لتوفير توافق مع الأنظمة القديمة، يمكنك إنشاء واجهة مخصّصة ثمّ إضافة عمليات تنفيذ خاصة بإصدار معيّن. تتمثل إحدى ميزات هذا الأسلوب في أنّه يتيح لك عكس الواجهات العامة على نظام التشغيل Android 4.1 (المستوى 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()
إلغاء تسجيل مستمِع جهاز الإدخال onGenericMotionEvent()
- مرايا
onGenericMotionEvent()
للسماح للّعبة باعتراض ومعالجة كائناتMotionEvent
وقيم المحاور التي تمثّل أحداثًا مثل حركات ذراع التحكّم والضغط على زر التشغيل التناظري. onPause()
- يوقف استطلاع الرأي حول أحداث وحدات التحكم في الألعاب عندما يكون النشاط الرئيسي متوقفًا مؤقتًا، أو عندما لا تكون اللعبة بؤرة التركيز.
onResume()
- تبدأ هذه الميزة في إجراء الاستطلاعات حول أحداث وحدات التحكّم في الألعاب عند استئناف النشاط الرئيسي أو عند بدء اللعبة وتشغيلها في المقدّمة.
InputDeviceListener
- يعكس واجهة
InputManager.InputDeviceListener
. يتيح للّعبة معرفة ما إذا تمت إضافة ذراع تحكّم في الألعاب أو تغييرها أو إزالتها.
الخطوة التالية هي إنشاء عمليات تنفيذ لـ InputManagerCompat
تعمل
على مختلف الإصدارات من النظام الأساسي إذا كانت لعبتك تعمل على نظام التشغيل Android 4.1 أو إصدار أحدث واستدعت طريقة InputManagerCompat
، يستدعي تنفيذ الخادم الوكيل الطريقة المكافئة في InputManager
.
ومع ذلك، إذا كانت لعبتك تعمل على نظام التشغيل Android 3.1 أو Android 4.0، يعالج التنفيذ المخصّص عمليات الاستدعاء لطرق InputManagerCompat
باستخدام واجهات برمجة التطبيقات التي تم تقديمها في إصدار أحدث من 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
كائنات. ستستخدم أدوات التحكم لتحديث حالة اتصال وحدات التحكم في الألعاب التي يتم تتبعها.
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()
. تتحقّق هذه الطريقة مما إذا كان قد تم فصل وحدة تحكّم
الألعاب المرفقة وترسل إشعارًا إلى المستمعين المسجَّلين.
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()
. عندما يبلغ النظام عن حدث حركة، تحقّق مما إذا كان مصدر هذا الحدث هو رقم تعريف جهاز تم تتبُّعه مسبقًا أو من رقم تعريف جهاز جديد. إذا كان رقم تعريف الجهاز جديدًا، يُرجى إرسال إشعارات إلى المستمعين المسجّلين.
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
، يتم استدعاء طريقة معاودة الاتصال المناسبة للمستمع للإشارة إلى ما إذا تمت إضافة وحدة تحكّم في الألعاب أو تغييرها أو إزالتها.
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
: تطبيق متوافق مع الأجهزة التي تعمل بالإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث، والأخرى تعمل على الأجهزة التي تعمل بالإصدارات 3.1 من نظام التشغيل Android وصولاً إلى الإصدار 4.0 من نظام التشغيل Android.
استخدام التنفيذ الخاص بالإصدار
يتم تنفيذ منطق التبديل الخاص بالإصدار في فئة تعمل كمصنع.
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 (مستوى واجهة برمجة التطبيقات 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
المتاح للتنزيل أعلاه.