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

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

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

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

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

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

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

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

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

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

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

وضع اللمس

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

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

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

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

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

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

على الأجهزة التي تعمل بالإصدار 9 من نظام التشغيل Android (المستوى 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()، كما هو موضَّح في قسم أدوات معالجة الأحداث.