إذا كنت تتيح استخدام وحدات التحكّم في الألعاب، تقع على عاتقك مسؤولية التأكّد من أنّ لعبتك تستجيب لوحدات التحكّم بشكل متسق على جميع الأجهزة التي تعمل بإصدارات مختلفة من Android. يتيح ذلك للعبتك الوصول إلى شريحة أكبر من الجمهور، كما يمكن للاعبين الاستمتاع بتجربة لعب سلسة باستخدام وحدات التحكّم حتى عند التبديل إلى أجهزة Android جديدة أو ترقيتها.
يوضّح هذا الدرس كيفية استخدام واجهات برمجة التطبيقات المتوفّرة في الإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث بطريقة متوافقة مع الأنظمة القديمة، ما يتيح للعبتك إتاحة الميزات التالية على الأجهزة التي تعمل بالإصدار 3.1 من نظام التشغيل Android والإصدارات الأحدث:
- يمكن للعبة رصد ما إذا تمت إضافة وحدة تحكّم جديدة أو تغييرها أو إزالتها.
- يمكن للعبة الاستعلام عن إمكانات ذراع التحكّم في الألعاب.
- يمكن للعبة التعرّف على أحداث الحركة الواردة من ذراع التحكّم في الألعاب.
الاستعداد لتجريد واجهات برمجة التطبيقات من أجل توفير إمكانية استخدام أدوات التحكّم في الألعاب
لنفترض أنّك تريد تحديد ما إذا كانت حالة اتصال وحدة التحكّم في الألعاب قد تغيّرت على الأجهزة التي تعمل بالإصدار 3.1 من نظام التشغيل Android (المستوى 12 من واجهة برمجة التطبيقات). ومع ذلك، لا تتوفّر واجهات برمجة التطبيقات إلا في الإصدار 4.1 من نظام التشغيل Android (المستوى 16 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لذا عليك توفير عملية تنفيذ تتوافق مع الإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث، مع توفير آلية احتياطية تتوافق مع الإصدار 3.1 من نظام التشغيل Android حتى الإصدار 4.0.
لمساعدتك في تحديد الميزات التي تتطلّب آلية احتياطية للإصدارات الأقدم، يوضّح الجدول 1 الاختلافات في إمكانية استخدام وحدات التحكّم في الألعاب بين الإصدار 3.1 من نظام التشغيل Android (المستوى 12 من واجهة برمجة التطبيقات) والإصدار 4.1 (المستوى 16 من واجهة برمجة التطبيقات).
الجدول 1. واجهات برمجة التطبيقات التي تتيح استخدام أذرع التحكّم في الألعاب على إصدارات Android المختلفة
| معلومات مسؤول التحكّم بالبيانات | Controller API | المستوى 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 وسيطة تجرِّد عملية تنفيذ ميزات ذراع التحكّم في الألعاب التي تتطلّبها لعبتك.
- أنشئ عملية تنفيذ بديلة للواجهة تستخدم واجهات برمجة التطبيقات في الإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث.
- أنشئ تنفيذًا مخصّصًا للواجهة يستخدِم واجهات برمجة التطبيقات المتاحة بين الإصدار 3.1 والإصدار 4.0 من نظام التشغيل Android.
- أنشئ منطقًا للتبديل بين عمليات التنفيذ هذه في وقت التشغيل، وابدأ في استخدام الواجهة في لعبتك.
للحصول على نظرة عامة حول كيفية استخدام التجريد للتحقّق من إمكانية عمل التطبيقات بطريقة متوافقة مع الإصدارات القديمة على مختلف إصدارات 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 تعمل على إصدارات مختلفة من المنصة. إذا كانت لعبتك تعمل على الإصدار 4.1 من نظام التشغيل Android أو الإصدارات الأحدث وتستدعي إحدى طرق InputManagerCompat، فإنّ عملية التنفيذ البديلة تستدعي الطريقة المكافئة في InputManager.
ومع ذلك، إذا كانت لعبتك تعمل على الإصدارات من Android 3.1 إلى Android 4.0، ستعالج عمليات التنفيذ المخصّصة طلبات البيانات من طرق InputManagerCompat باستخدام واجهات برمجة التطبيقات التي تم طرحها في الإصدار Android 3.1 أو الإصدارات الأحدث فقط. وبغض النظر عن عملية التنفيذ الخاصة بالإصدار التي يتم استخدامها في وقت التشغيل، فإنّ عملية التنفيذ تعيد نتائج طلب البحث بشكل شفاف إلى اللعبة.
تنفيذ الواجهة على الإصدار 4.1 من نظام التشغيل Android والإصدارات الأحدث
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 يتوافق مع الإصدار 3.1 من نظام التشغيل Android والإصدارات الأحدث حتى الإصدار 4.0، يمكنك استخدام العناصر التالية:
SparseArrayمن أرقام تعريف الأجهزة لتتبُّع أذرع التحكّم في الألعاب المتصلة بالجهازHandlerلمعالجة أحداث الجهاز عند بدء تطبيق أو استئنافه، يتلقّىHandlerرسالة لبدء التحقّق من انقطاع اتصال وحدة التحكّم في الألعاب. سيبدأHandlerحلقة للتحقّق من كل أداة تحكّم معروفة في الألعاب ومتصلة بالجهاز، ومعرفة ما إذا تم عرض معرّف جهاز. تشير قيمة الإرجاع Anullإلى أنّ ذراع التحكّم في الألعاب غير متصل. يتوقّف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.
استخدام التنفيذ الخاص بالإصدار
يتم تنفيذ منطق التبديل الخاص بالإصدار في فئة تعمل كـ مصنع.
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 من وحدة تحكّم في الألعاب والإصدارات الأحدث. من المفترض أن تتمكّن لعبتك الآن من معالجة أحداث وحدة التحكّم في الألعاب بشكل متّسق على الأجهزة التي تعمل بالإصدار 3.1 من نظام التشغيل Android (المستوى 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 المتاح للتنزيل.