نظرة عامة على أحداث الإدخال

تجربة طريقة ComposeAllowed
Jetpack Compose هي مجموعة أدوات واجهة المستخدم التي ننصح بها لنظام التشغيل Android. تعرَّف على كيفية استخدام اللمس والإدخال في ميزة "إنشاء".

على نظام التشغيل Android، هناك أكثر من طريقة لاعتراض الأحداث من تفاعل المستخدم مع تطبيقك. عند التفكير في الأحداث ضمن واجهة المستخدم، يكون النهج هو تسجيل الأحداث من عنصر "العرض" المحدّد الذي يتفاعل معه المستخدم. توفر فئة العرض الوسائل لإجراء ذلك.

ضمن فئات "العرض" المختلفة التي ستستخدمها لإنشاء تنسيقك، قد تلاحظ العديد من طرق معاودة الاتصال العامة التي تبدو مفيدة لأحداث واجهة المستخدم. ويتم استدعاء هذه الطرق بواسطة إطار عمل Android عند حدوث الإجراء ذي الصلة على ذلك الكائن. على سبيل المثال، عند لمس عنصر عرض (مثل زر)، يتم استدعاء طريقة onTouchEvent() في ذلك الكائن. ومع ذلك، لاعتراض هذا، يجب عليك توسيع الفئة وإلغاء الطريقة. ومع ذلك، لن يكون تمديد كل كائن View لمعالجة مثل هذا الحدث أمرًا عمليًا. وهذا هو السبب في أن فئة "عرض" تحتوي أيضًا على مجموعة من الواجهات المتداخلة مع استدعاءات يمكنك تحديدها بسهولة أكبر. هذه الواجهات، التي يُطلق عليها أدوات معالجة الأحداث، هي وسيلتك لتسجيل تفاعل المستخدم مع واجهة المستخدم.

على الرغم من أنّك ستستخدم بشكل أكثر شيوعًا أدوات معالجة الأحداث للاستماع إلى تفاعل المستخدم، قد يأتي وقت تريد فيه تمديد فئة العرض من أجل إنشاء مكوّن مخصّص. قد ترغب في تمديد الفصل الدراسي Button لصنع محتوى راقٍ. في هذه الحالة، ستتمكّن من تحديد سلوكيات الأحداث التلقائية لصفك الدراسي باستخدام معالِجات الأحداث للفئة.

أدوات معالجة الأحداث

أداة معالجة الأحداث هي واجهة في الفئة View تحتوي على طريقة واحدة لمعاودة الاتصال. سيتم استدعاء هذه الطرق بواسطة إطار عمل Android عندما يتم تشغيل طريقة العرض التي تم تسجيل المستمع بها من خلال تفاعل المستخدم مع العنصر في واجهة المستخدم.

ويتم تضمين طرق معاودة الاتصال التالية في واجهات أداة معالجة الأحداث:

onClick()
من View.OnClickListener. ويأتي ذلك عندما يلمس المستخدم العنصر (أثناء وضع اللمس) أو عندما يركّز على العنصر باستخدام مفاتيح التنقل أو كرة التتبع ويضغط على مفتاح "Enter" المناسب أو يضغط للأسفل على كرة التتبع.
onLongClick()
من View.OnLongClickListener. يُعرف ذلك عندما يلمس المستخدم العنصر مع الاستمرار (أثناء وضع اللمس)، أو يركّز على العنصر باستخدام مفاتيح التنقّل أو كرة التتبع ويضغط مع الاستمرار على مفتاح "Enter" المناسب أو يضغط مع الاستمرار على كرة التتبع (لمدة ثانية واحدة).
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() - يؤدي ذلك إلى عرض قيمة منطقية للإشارة إلى ما إذا كنت قد شاهدت الحدث ويجب عدم مواصلة تشغيله. ويعني ذلك عرض القيمة true للإشارة إلى أنّك تعاملت مع الحدث وأنّه يجب أن يتوقف عند هذا الحد، ويمكنك عرض false إذا لم تكن قد تعاملت معه و/أو استمرار الحدث لأي مستمعين آخرين ينقرون على الحدث.
  • onKey() - يؤدي ذلك إلى عرض قيمة منطقية للإشارة إلى ما إذا كنت قد شاهدت الحدث ويجب عدم مواصلة تشغيله. ويعني ذلك عرض القيمة صحيح للإشارة إلى أنّك تعاملت مع الفعالية وأنّها يجب أن تتوقف عند هذا الحد، ويمكنك عرض القيمة خطأ إذا لم تكن قد تعاملت معها و/أو مواصلة عرض الحدث لأي مستمعين آخرين.
  • onTouch(): يؤدي ذلك إلى عرض قيمة منطقية للإشارة إلى ما إذا كان المستمع يستخدم هذا الحدث. ومن المهم أن يتضمّن هذا الحدث عدة إجراءات تتبع بعضها بعضًا. وبالتالي، إذا عرضت القيمة false عند تلقّي حدث الإجراء المتراجع، هذا يعني أنّك لم تستخدم الحدث وأنّك غير مهتم بالإجراءات اللاحقة من هذا الحدث. وبالتالي، لن يُطلب منك اتخاذ أي إجراءات أخرى ضمن الفعالية، مثل إيماءة الإصبع أو الإجراء النهائي النهائي.

تذكَّر أنّه يتم دائمًا تسليم الأحداث الرئيسية للأجهزة إلى العرض محلّ التركيز حاليًا. ويتم إرسالها بدءًا من أعلى العرض الهرمي ثم إلى أسفل، حتى تصل إلى الوجهة المناسبة. إذا كان التركيز في الوقت الحالي لعرضك (أو عنصر فرعي من ملفك الشخصي)، يمكنك الاطّلاع على مسار الحدث من خلال طريقة dispatchKeyEvent(). بدلاً من تسجيل الأحداث الرئيسية من خلال ملفك الشخصي، يمكنك أيضًا تلقّي جميع الأحداث داخل نشاطك باستخدام onKeyDown() وonKeyUp().

كذلك، عند التفكير في إدخال النص لتطبيقك، تذكّر أن العديد من الأجهزة لا تستخدم سوى أساليب إدخال برامج. وهذه الأساليب ليست شرطًا يعتمد على المفاتيح، فقد يستخدم بعضها الإدخال الصوتي والكتابة بخط اليد وما إلى ذلك. وحتى إذا كان أسلوب الإدخال يعرض واجهة تشبه لوحة المفاتيح، لن يؤدي ذلك بشكل عام إلى تشغيل مجموعة onKeyDown() من الأحداث. يجب ألا تنشئ أبدًا واجهة مستخدم تتطلب ضغطات مفاتيح معينة للتحكم فيها إلا إذا كنت ترغب في قصر تطبيقك على الأجهزة التي تستخدم لوحة مفاتيح خارجية. وعلى وجه الخصوص، يجب عدم الاعتماد على هذه الطرق للتحقّق من صحة الإدخال عندما يضغط المستخدم على مفتاح الرجوع، بل يجب بدلاً من ذلك استخدام إجراءات مثل IME_ACTION_DONE للإشارة إلى أسلوب الإدخال الذي يتوقع تطبيقك أن يتفاعل به، لذلك قد يتم تغيير واجهة المستخدم الخاصة به بطريقة مفيدة. تجنب الافتراضات حول كيفية عمل أسلوب إدخال البرنامج واثق بها فقط لتزويد تطبيقك بنص منسق بالفعل.

ملاحظة: سيستدعي Android معالِجات الأحداث أولاً، ثم المعالِجات التلقائية المناسبة من تعريف الفئة ثانيًا. وبناءً على ذلك، سيؤدي عرض true من أدوات معالجة الأحداث هذه إلى إيقاف نشر الحدث إلى أدوات معالجة الأحداث الأخرى، كما سيؤدي إلى حظر معاودة الاتصال بمعالج الحدث التلقائي في الملف الشخصي. لذا تأكّد من أنّك تريد إنهاء الحدث عند عرض القيمة true.

معالِجات الأحداث

إذا كنت تنشئ مكونًا مخصصًا من "العرض"، ستتمكن من تحديد العديد من طرق الاستدعاء التي تستخدم كمعالجات أحداث تلقائية. في المستند الذي يتناول مكوّنات العرض المخصّص، ستتعرف على بعض عمليات الاستدعاء الشائعة المستخدمة لمعالجة الأحداث، بما في ذلك:

هناك بعض الطرق الأخرى التي يجب أن تكون على دراية بها، والتي ليست جزءًا من فئة العرض، ولكن يمكن أن تؤثر بشكل مباشر في الطريقة التي يمكنك بها التعامل مع الأحداث. لذلك، عند إدارة أحداث أكثر تعقيدًا داخل تخطيط، ضع في اعتبارك هذه الطرق الأخرى:

وضع اللمس

عندما يتنقل المستخدم في واجهة المستخدم باستخدام مفاتيح الاتجاهات أو كرة التتبع، من الضروري التركيز على العناصر القابلة للتنفيذ (مثل الأزرار) حتى يتمكن المستخدم من معرفة ما يقبل المدخلات. أمّا إذا كان الجهاز يتضمّن إمكانيات اللمس، ويبدأ المستخدم في التفاعل مع الواجهة من خلال لمسها، فلم يعد من الضروري تمييز العناصر أو التركيز على "عرض" معيّن. وبالتالي، يوجد وضع للتفاعل يسمى "وضع اللمس".

بالنسبة إلى الجهاز الذي يعمل باللمس، سيدخل الجهاز في وضع اللمس بعد لمس الشاشة. من الآن فصاعدًا، يمكن التركيز فقط على طرق العرض التي تكون قيمة isFocusableInTouchMode() فيها صحيحة، مثل التطبيقات المصغّرة لتعديل النص. لن يتم التركيز على طرق العرض الأخرى التي يمكن لمسها، مثل الأزرار، عند لمسها؛ بل ببساطة تنشط المستمعين عند النقر عند الضغط عليها.

عندما يضغط المستخدم على مفتاح توجيه أو يتنقّل باستخدام كرة التتبع، يخرج الجهاز من وضع اللمس ويجد طريقة عرض للتركيز عليها. الآن، يمكن للمستخدم استئناف التفاعل مع واجهة المستخدم دون لمس الشاشة.

يتم الاحتفاظ بحالة وضع اللمس في جميع أنحاء النظام (جميع النوافذ والأنشطة). للاستعلام عن الحالة الحالية، يمكنك الاتصال بـ isInTouchMode() لمعرفة ما إذا كان الجهاز في وضع اللمس حاليًا.

التعامل مع التركيز

سيتعامل إطار العمل مع حركة التركيز الروتينية استجابةً لإدخال المستخدم. ويشمل ذلك تغيير التركيز مع إزالة طرق العرض أو إخفاؤها، أو عند توفر طرق عرض جديدة. تشير المشاهدات إلى استعدادها للتركيز من خلال طريقة isFocusable(). لتغيير ما إذا كان بإمكان إحدى طرق العرض التركيز، يمكنك طلب setFocusable(). في وضع اللمس، يمكنك الاستعلام عمّا إذا كان "طريقة العرض" تسمح بالتركيز باستخدام "isFocusableInTouchMode()". يمكنك تغيير هذا الخيار باستخدام setFocusableInTouchMode().

على الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث، لا يكون هناك تركيز أولي في الأنشطة. بدلاً من ذلك، يجب عليك طلب التركيز الأوّلي بشكل صريح، إذا رغبت في ذلك.

تعتمد حركة التركيز على خوارزمية تحدد الجار الأقرب في اتجاه معين. في حالات نادرة، قد لا تتوافق الخوارزمية الافتراضية مع السلوك المقصود للمطوّر. في هذه الحالات، يمكنك تقديم إلغاءات صريحة باستخدام سمات XML التالية في ملف التنسيق: nextFocusDown وnextFocusLeft وnextFocusRight وnextFocusUp. أضف إحدى هذه السمات إلى العرض من الذي يغادر منه التركيز. حدّد قيمة السمة لتكون رقم تعريف الملف الشخصي إلى العنصر الذي يجب التركيز عليه. على سبيل المثال:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

وعادة، في هذا التخطيط العمودي، لن يتنقل التنقل لأعلى من الزر الأول في أي مكان، ولن ينتقل لأسفل من الزر الثاني. الآن بعد أن حدّد الزر العلوي الزر السفلي nextFocusUp (والعكس صحيح)، سيبدأ تركيز التنقل من الأعلى إلى الأسفل ومن الأسفل إلى الأعلى.

إذا أردت تحديد عرض قابل للتركيز في واجهة المستخدم (على الرغم من أنّه لا يكون كذلك في العادة)، أضِف سمة XML android:focusable إلى "العرض" في بيان التنسيق. حدِّد القيمة true. بإمكانك أيضًا استخدام android:focusableInTouchMode للإشارة إلى أنّ العرض يمكن التركيز عليه أثناء استخدام وضع اللمس.

لطلب وضع تركيز معيّن في ملف شخصي معيّن، يمكنك الاتصال بالرقم requestFocus().

للاستماع إلى الأحداث التي يتم التركيز عليها (أي تلقّي إشعار عند تلقّي عنصر المشاهدة أو فقدان التركيز)، استخدِم onFocusChange()، كما هو موضَّح في قسم أدوات معالجة الأحداث.