على Android، هناك أكثر من طريقة لاعتراض الأحداث الناتجة عن تفاعل المستخدم مع تطبيقك. عند وضع الأحداث في واجهة المستخدم في الاعتبار، يكون النهج هو التقاط الأحداث من عنصر العرض المحدّد الذي يتفاعل معه المستخدم. توفّر فئة View الوسائل اللازمة لإجراء ذلك.
ضمن فئات العرض المختلفة التي ستستخدمها لإنشاء تخطيطك، قد تلاحظ العديد من طرق معاودة الاتصال العامة التي تبدو مفيدة لأحداث واجهة المستخدم. تستدعي هذه الطرق إطار عمل Android عندما يحدث الإجراء المعني على هذا العنصر. على سبيل المثال، عند لمس عنصر View (مثل زر)، يتم استدعاء طريقة onTouchEvent() على هذا العنصر. ومع ذلك، لاعتراض هذا الإجراء، يجب توسيع الفئة وتجاوز الطريقة. ومع ذلك، لن يكون توسيع كل عنصر View لمعالجة مثل هذا الحدث أمرًا عمليًا. لهذا السبب، تحتوي فئة View أيضًا على مجموعة من الواجهات المتداخلة التي تتضمّن طرق معاودة الاتصال التي يمكنك تحديدها بسهولة أكبر. هذه الواجهات،
التي تُعرف باسم أدوات معالجة الأحداث، هي الوسيلة التي تتيح لك التقاط تفاعل المستخدم مع واجهة المستخدم.
مع أنّك ستستخدم عادةً أدوات معالجة الأحداث للاستماع إلى تفاعل المستخدم، قد يحين الوقت الذي تريد فيه توسيع فئة View لإنشاء مكوّن مخصّص.
ربما تريد توسيع فئة Button لإنشاء شيء أكثر جاذبية. في هذه الحالة، ستتمكّن من تحديد السلوكيات التلقائية للأحداث لفئتك باستخدام معالجات أحداث الفئة.
أدوات معالجة الأحداث
متتبِّع الأحداث هو واجهة في فئة View تحتوي على طريقة رد الاتصال واحدة. سيستدعي إطار عمل Android هذه الطرق عندما يتم تشغيل عنصر View الذي تم تسجيل أداة المعالجة له من خلال تفاعل المستخدم مع العنصر في واجهة المستخدم.
تتضمّن واجهات متتبِّع الأحداث طرق رد الاتصال التالية:
onClick()- من
View.OnClickListener. يتم استدعاء هذه الطريقة عندما يلمس المستخدم العنصر (في وضع اللمس)، أو يركّز على العنصر باستخدام مفاتيح التنقّل أو كرة التتبّع و يضغط على مفتاح "الإدخال" المناسب أو يضغط على كرة التتبّع. onLongClick()- من
View.OnLongClickListener. يتم استدعاء هذه الطريقة عندما يلمس المستخدم العنصر ويضغط عليه مع الاستمرار (في وضع اللمس)، أو يركّز على العنصر باستخدام مفاتيح التنقّل أو كرة التتبّع و يضغط على مفتاح "الإدخال" المناسب مع الاستمرار أو يضغط على كرة التتبّع مع الاستمرار (لمدّة ثانية واحدة). onFocusChange()- من
View.OnFocusChangeListener. يتم استدعاء هذه الطريقة عندما ينتقل المستخدم إلى العنصر أو يبتعد عنه، باستخدام مفاتيح التنقّل أو كرة التتبّع. onKey()- من
View.OnKeyListener. يتم استدعاء هذه الطريقة عندما يركّز المستخدم على العنصر ويضغط على مفتاح خارجي أو يحرّره على الجهاز. onTouch()- من
View.OnTouchListener. يتم استدعاء هذه الطريقة عندما يتّخذ المستخدم إجراءً مؤهلاً كحدث لمس، بما في ذلك الضغط أو التحرير أو أي إيماءة حركة على الشاشة (ضمن حدود العنصر). onCreateContextMenu()- من
View.OnCreateContextMenuListener. يتم استدعاء هذه الطريقة عند إنشاء قائمة سياقية (نتيجةً لـ "نقر طويل" مستمر). اطّلِع على المناقشة حول القوائم السياقية في دليل المطوّرين حول القوائم.
هذه الطرق هي العناصر الوحيدة في واجهاتها المعنيّة. لتحديد إحدى هذه الطرق ومعالجة أحداثك، عليك تنفيذ الواجهة المتداخلة في نشاطك أو تحديدها كفئة مجهولة.
بعد ذلك، مرِّر مثيلاً من عملية التنفيذ إلى طريقة View.set...Listener() المعنيّة. (على سبيل المثال، استدعِ
ومرِّر إليه عملية تنفيذ setOnClickListener()OnClickListener.)
يوضّح المثال أدناه كيفية تسجيل أداة معالجة النقر لزر.
Kotlin
protected void onCreate(savedValues: Bundle) { ... val button: Button = findViewById(R.id.corky) // Register the onClick listener with the implementation above button.setOnClickListener { view -> // do something when the button is clicked } ... }
Java
// Create an anonymous implementation of OnClickListener private OnClickListener corkyListener = new OnClickListener() { public void onClick(View v) { // do something when the button is clicked } }; protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button button = (Button)findViewById(R.id.corky); // Register the onClick listener with the implementation above button.setOnClickListener(corkyListener); ... }
قد يكون من الأنسب أيضًا تنفيذ OnClickListener كجزء من نشاطك. سيؤدي ذلك إلى تجنُّب تحميل الفئة الإضافية وتخصيص الكائن. على سبيل المثال:
Kotlin
class ExampleActivity : Activity(), OnClickListener { protected fun onCreate(savedValues: Bundle) { val button: Button = findViewById(R.id.corky) button.setOnClickListener(this) } // Implement the OnClickListener callback fun onClick(v: View) { // do something when the button is clicked } }
Java
public class ExampleActivity extends Activity implements OnClickListener { protected void onCreate(Bundle savedValues) { ... Button button = (Button)findViewById(R.id.corky); button.setOnClickListener(this); } // Implement the OnClickListener callback public void onClick(View v) { // do something when the button is clicked } ... }
يُرجى العِلم أنّ طريقة معاودة الاتصال onClick() في المثال أعلاه لا تتضمّن قيمة إرجاع، ولكن يجب أن تعرض بعض طرق متتبِّع الأحداث الأخرى قيمة منطقية. يعتمد السبب على الحدث. بالنسبة إلى الطرق القليلة التي تتضمّن قيمة إرجاع، إليك السبب:
- تعرض هذه الطريقة قيمة منطقية للإشارة إلى ما إذا كنت قد استهلكت الحدث ويجب عدم نقله إلى أبعد من ذلك. أي عليك عرض صحيح للإشارة إلى أنّك عالجت الحدث ويجب أن يتوقف هنا، وعرض خطأ إذا لم تعالجه و/أو إذا كان يجب أن يستمر الحدث إلى أي أدوات معالجة أخرى للنقر.onLongClick()- تعرض هذه الطريقة قيمة منطقية للإشارة إلى ما إذا كنت قد استهلكت الحدث ويجب عدم نقله إلى أبعد من ذلك. أي عليك عرض صحيح للإشارة إلى أنّك عالجت الحدث ويجب أن يتوقف هنا، وعرض خطأ إذا لم تعالجه و/أو إذا كان يجب أن يستمر الحدث إلى أي أدوات معالجة أخرى للمفاتيح.onKey()- تعرض هذه الطريقة قيمة منطقية للإشارة إلى ما إذا كانت أداة المعالجة تستهلك هذا الحدث. الأمر المهم هو أنّ هذا الحدث يمكن أن يتضمّن إجراءات متعدّدة تتبع بعضها البعض. لذلك، إذا عرضت خطأ عند تلقّي حدث الإجراء "الضغط"، فإنّك تشير إلى أنّك لم تستهلك الحدث ولست مهتمًا أيضًا بالإجراءات اللاحقة من هذا الحدث. وبالتالي، لن يتم استدعاؤك لأي إجراءات أخرى ضمن الحدث، مثل إيماءة إصبع أو حدث الإجراء "الرفع" النهائي.onTouch()
يُرجى العِلم أنّ أحداث مفاتيح الجهاز يتم تسليمها دائمًا إلى عنصر View الذي يركّز عليه المستخدم حاليًا. يتم إرسالها بدءًا من أعلى هيكلية طرق العرض، ثم إلى الأسفل، إلى أن تصل إلى الوجهة المناسبة. إذا كان عنصر View (أو عنصر فرعي من عنصر View)
يركّز عليه المستخدم حاليًا، يمكنك الاطّلاع على الحدث أثناء انتقاله من خلال طريقة كبديل لالتقاط أحداث المفاتيح من خلال عنصر View، يمكنك أيضًا تلقّي
جميع الأحداث داخل نشاطك باستخدام dispatchKeyEvent()
و onKeyDown().onKeyUp()
عند التفكير في إدخال النص لتطبيقك، يُرجى العِلم أنّ العديد من الأجهزة لا تتضمّن سوى طرق إدخال البرامج. ليس من الضروري أن تستند هذه الطرق إلى المفاتيح، فقد يستخدم بعضها الإدخال الصوتي والكتابة اليدوية وما إلى ذلك. حتى إذا
كانت طريقة الإدخال تعرض واجهة مشابهة للوحة المفاتيح، فإنّها لن تبدأ بشكل عام مجموعة أحداث
يجب ألا تنشئ واجهة مستخدم تتطلب الضغط على مفاتيح معيّنة للتحكّم فيها إلا إذا كنت تريد حصر تطبيقك على الأجهزة التي تتضمّن لوحة مفاتيح فعلية. على وجه الخصوص، لا تعتمد على هذه الطرق للتحقّق من صحة الإدخال عندما يضغط المستخدم على مفتاح الرجوع، بل استخدِم إجراءات مثل onKeyDown()IME_ACTION_DONE للإشارة إلى طريقة الإدخال التي يتوقّع تطبيقك أن يتفاعل بها، حتى يتمكّن من تغيير واجهة المستخدم بطريقة مفيدة. تجنَّب وضع افتراضات حول كيفية عمل طريقة إدخال البرامج، وثِق بها فقط لتزويد تطبيقك بنص منسّق مسبقًا.
ملاحظة: سيستدعي Android معالجات الأحداث أولاً، ثم معالجات الإعدادات التلقائية المناسبة من تعريف الفئة ثانيًا. وبالتالي، سيؤدي عرض صحيح من أدوات معالجة الأحداث هذه إلى إيقاف انتشار الحدث إلى أدوات معالجة الأحداث الأخرى، وسيؤدي أيضًا إلى حظر معاودة الاتصال بمعالج الأحداث التلقائي في عنصر View. لذلك، تأكَّد من أنّك تريد إنهاء الحدث عند عرض صحيح.
معالجات الأحداث
إذا كنت تنشئ مكوّنًا مخصّصًا من View، ستتمكّن من تحديد عدة طرق معاودة اتصال تُستخدَم كمعالجات أحداث تلقائية. في المستند حول مكوّنات العرض المخصّصة، ستتعرّف على بعض طرق معاودة الاتصال الشائعة المستخدَمة لمعالجة الأحداث، بما في ذلك:
- يتم استدعاؤها عند وقوع حدث رئيسي جديد.onKeyDown(int, KeyEvent): يتم استدعاؤها عند وقوع حدث رفع مفتاح.onKeyUp(int, KeyEvent): يتم استدعاؤها عند وقوع حدث حركة كرة التتبّع.onTrackballEvent(MotionEvent): يتم استدعاؤها عند وقوع حدث حركة شاشة اللمس.onTouchEvent(MotionEvent)- يتم استدعاؤها عندما يكتسب العرض التركيز أو يفقده.onFocusChanged(boolean, int, Rect)
هناك بعض الطرق الأخرى التي يجب أن تكون على دراية بها، وهي ليست جزءًا من فئة View، ولكن يمكن أن تؤثر بشكل مباشر في طريقة معالجتك للأحداث. لذلك، عند إدارة أحداث أكثر تعقيدًا داخل تخطيط، ضَع في الاعتبار هذه الطرق الأخرى:
- يتيح ذلك لـActivity.dispatchTouchEvent(MotionEvent)Activityاعتراض جميع أحداث اللمس قبل إرسالها إلى النافذة.- يتيح ذلك لـViewGroup.onInterceptTouchEvent(MotionEvent)ViewGroupمراقبة الأحداث أثناء إرسالها إلى عناصر View الفرعية.- استدعِ هذه الطريقة على عنصر View رئيسي للإشارة إلى أنّه يجب ألا يعترض أحداث اللمس باستخدامViewParent.requestDisallowInterceptTouchEvent(boolean).onInterceptTouchEvent(MotionEvent)
وضع اللمس
عندما يتنقّل المستخدم في واجهة مستخدم باستخدام مفاتيح الاتجاهات أو كرة التتبّع، من الضروري التركيز على العناصر القابلة للتنفيذ (مثل الأزرار) حتى يتمكّن المستخدم من الاطّلاع على ما سيقبل الإدخال. ومع ذلك، إذا كان الجهاز يتضمّن إمكانات اللمس وبدأ المستخدم التفاعل مع الواجهة عن طريق لمسها، لن يعود من الضروري تمييز العناصر أو التركيز على عنصر View معيّن. لذلك، هناك وضع للتفاعل يُعرف باسم "وضع اللمس".
بالنسبة إلى الجهاز الذي يتضمّن إمكانات اللمس، سينتقل الجهاز إلى وضع اللمس بمجرد أن يلمس المستخدم الشاشة. من هذه النقطة فصاعدًا، لن يكون التركيز متاحًا إلا لعناصر View التي تكون فيها isFocusableInTouchMode() صحيحة، مثل أدوات تعديل النصوص.
لن تركّز عناصر View الأخرى القابلة للمس، مثل الأزرار، عند لمسها، بل ستطلق ببساطة أدوات معالجة النقر عند الضغط عليها.
في أي وقت يضغط فيه المستخدم على مفتاح اتجاه أو يمرّر باستخدام كرة التتبّع، سيخرج الجهاز من وضع اللمس، وسيبحث عن عنصر View للتركيز عليه. يمكن للمستخدم الآن استئناف التفاعل مع واجهة المستخدم بدون لمس الشاشة.
يتم الاحتفاظ بحالة وضع اللمس على مستوى النظام بأكمله (جميع النوافذ والأنشطة).
للاستعلام عن الحالة الحالية، يمكنك استدعاء isInTouchMode() لمعرفة ما إذا كان الجهاز حاليًا في وضع اللمس.
معالجة التركيز
سيتعامل إطار العمل مع حركة التركيز الروتينية استجابةً لبيانات أدخلها المستخدم.
ويشمل ذلك تغيير التركيز عند إزالة عناصر View أو إخفائها، أو عند توفّر عناصر View جديدة. تشير عناصر View إلى استعدادها للتركيز
من خلال طريقة لتغيير ما إذا كان بإمكان عنصر View التركيز
، استدعِ isFocusable() في وضع اللمس،
يمكنك الاستعلام عما إذا كان عنصر View يسمح بالتركيز باستخدام setFocusable().
يمكنك تغيير ذلك باستخدام isFocusableInTouchMode()setFocusableInTouchMode()
على الأجهزة التي تعمل بالإصدار 9 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 28) أو الإصدارات الأحدث، لا تُخصّص الأنشطة تركيزًا أوليًا. بدلاً من ذلك، يجب أن تطلب التركيز الأولي بشكل صريح، إذا أردت ذلك.
تستند حركة التركيز إلى خوارزمية تعثر على أقرب عنصر مجاور في اتجاه معيّن. في حالات نادرة، قد لا تتطابق الخوارزمية التلقائية مع السلوك المقصود للمطوّر. في هذه الحالات، يمكنك تقديم عمليات إلغاء صريحة باستخدام سمات XML التالية في ملف التخطيط: nextFocusDown وnextFocusLeft وnextFocusRight و nextFocusUp. أضِف إحدى هذه السمات إلى عنصر View الذي يغادره التركيز. حدِّد قيمة السمة لتكون رقم تعريف عنصر View الذي يجب أن يركّز عليه المستخدم. على سبيل المثال:
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
في هذا التخطيط العمودي، لن يؤدي التنقّل للأعلى من الزر الأول أو التنقّل للأسفل من الزر الثاني إلى أي مكان. بعد أن حدّد الزر العلوي الزر السفلي كـ nextFocusUp (والعكس صحيح)، ستنتقل حركة إطار التركيز من الأعلى إلى الأسفل ومن الأسفل إلى الأعلى.
إذا أردت الإعلان عن عنصر View على أنّه قابل للتركيز في واجهة المستخدم (عندما لا يكون كذلك عادةً)، أضِف سمة XML android:focusable إلى عنصر View في بيان التخطيط.
اضبط القيمة على true. يمكنك أيضًا الإعلان عن عنصر View على أنّه قابل للتركيز في وضع اللمس باستخدام android:focusableInTouchMode.
لطلب تركيز عنصر View معيّن، استدعِ .requestFocus()
للاستماع إلى أحداث التركيز (تلقّي إشعار عندما يكتسب عنصر View التركيز أو يفقده)، استخدِم
,
كما هو موضّح في قسم أدوات معالجة الأحداث.onFocusChange()