إضافة قوائم

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

القوائم هي مكوّن شائع لواجهة المستخدم في العديد من أنواع التطبيقات. لتوفير تجربة مستخدم مألوفة ومتسقة، استخدِم واجهات برمجة تطبيقات Menu لعرض إجراءات المستخدم وخيارات أخرى في أنشطتك.

صورة تعرض مثالاً لقائمة الخيارات الإضافية
الشكل 1. قائمة يتم تفعيلها من خلال النقر على رمز، تظهر أسفل رمز قائمة الخيارات الإضافية

يوضّح هذا المستند كيفية إنشاء الأنواع الأساسية الثلاثة من القوائم أو عروض الإجراءات على جميع إصدارات Android:

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

راجِع قسم إنشاء قائمة خيارات.

قائمة السياقات ووضع الإجراءات المستندة إلى السياق
قائمة السياقات هي قائمة عائمة تظهر عندما يلمس المستخدم عنصرًا ويضغط عليه مع الاستمرار. ويقدّم إجراءات تؤثّر في المحتوى أو إطار السياق المحدّد.

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

راجِع قسم إنشاء قائمة سياقية.

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

راجِع قسم إنشاء قائمة منبثقة.

تحديد قائمة في ملف XML

يقدّم Android تنسيق XML عاديًا لجميع أنواع القوائم لتحديد عناصرها. بدلاً من إنشاء قائمة في رمز نشاطك، حدِّد قائمة وجميع عناصرها في مرجع قائمة بتنسيق XML. يمكنك بعد ذلك تضخيم مورد القائمة، أي تحميله كعنصر Menu في نشاطك أو المقتطف.

يُعدّ استخدام مرجع قائمة طعام ممارسة جيدة للأسباب التالية:

  • من الأسهل تصور بنية القائمة بتنسيق XML.
  • ويفصل هذا القسم محتوى القائمة عن الرمز البرمجي لسلوك تطبيقك.
  • ويتيح لك إنشاء إعدادات قائمة بديلة لإصدارات منصّة مختلفة وأحجام شاشة وإعدادات أخرى من خلال الاستفادة من إطار عمل موارد التطبيق.

لتحديد قائمة طعام، أنشئ ملف XML داخل دليل res/menu/ في مشروعك وأنشئ القائمة باستخدام العناصر التالية:

<menu>
تُحدِّد Menu، وهي حاوية لعناصر القائمة. يجب أن يكون عنصر <menu> هو العقدة الجذر للملف، ويمكنه أن يحتوي على عنصر <item> و<group> واحد أو أكثر.
<item>
لإنشاء MenuItem، الذي يمثّل عنصرًا واحدًا في قائمة. يمكن أن يحتوي هذا العنصر على عنصر <menu> متداخل لإنشاء قائمة فرعية.
<group>
حاوية اختيارية غير مرئية لعناصر <item> تتيح لك هذه الميزة تصنيف عناصر القائمة كي تتشارك الخصائص، مثل الحالة النشطة ومستوى الظهور. لمزيد من المعلومات، يُرجى الاطّلاع على القسم إنشاء مجموعة قوائم الطعام.

في ما يلي مثال على قائمة باسم game_menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/new_game"
          android:icon="@drawable/ic_new_game"
          android:title="@string/new_game"
          app:showAsAction="ifRoom"/>
    <item android:id="@+id/help"
          android:icon="@drawable/ic_help"
          android:title="@string/help" />
</menu>

يتيح العنصر <item> استخدام عدة سمات يمكنك استخدامها لتحديد مظهر العنصر وسلوكه. تتضمّن العناصر في القائمة السابقة السمات التالية:

android:id
معرّف مورد فريد للعنصر، ما يتيح للتطبيق التعرّف على العنصر عند اختياره من قِبل المستخدم
android:icon
إشارة إلى عنصر قابل للرسم لاستخدامه كرمز للعنصر
android:title
مرجع إلى سلسلة لاستخدامها كعنوان للعنصر
android:showAsAction
مواصفات حالات ظهور هذا العنصر كعنصر إجراء وكيفية ظهوره في شريط التطبيق

هذه هي أهم السمات التي تستخدمها، ولكن هناك العديد من السمات الأخرى المتوفّرة. للحصول على معلومات عن جميع السمات المتوافقة، اطّلِع على مستندات مورد القائمة.

يمكنك إضافة قائمة فرعية إلى عنصر في أي قائمة من خلال إضافة عنصر <menu> كعنصر فرعي لعنصر <item>. تكون القوائم الفرعية مفيدة عندما يتضمّن تطبيقك الكثير من الوظائف التي يمكن تنظيمها حسب المواضيع، مثل العناصر في شريط قوائم تطبيق الكمبيوتر الشخصي، مثل ملف وتعديل وعرض. راجِع المثال التالي:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/file"
          android:title="@string/file" >
        <!-- "file" submenu -->
        <menu>
            <item android:id="@+id/create_new"
                  android:title="@string/create_new" />
            <item android:id="@+id/open"
                  android:title="@string/open" />
        </menu>
    </item>
</menu>

لاستخدام القائمة في نشاطك، عليك _تضخيم_ مورد القائمة، وتحويل مورد XML إلى عنصر قابل للبرمجة باستخدام MenuInflater.inflate(). توضّح الأقسام التالية كيفية تضخيم قائمة لكل نوع من أنواع القوائم.

إنشاء قائمة خيارات

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

صورة تعرض شريط التطبيق الخاص بتطبيق &quot;جداول بيانات Google&quot;
الشكل 2. تطبيق "جداول بيانات Google" الذي يعرض عدة أزرار، بما في ذلك زر القائمة الكاملة للإجراءات

يمكنك الإفصاح عن عناصر لقائمة الخيارات من Activity الطبقة الفرعية أو Fragment الطبقة الفرعية. إذا كان كلّ من نشاطك وشظاياك يعرِضان عناصر لقائمة الخيارات، يتم دمج العناصر في واجهة المستخدم. تظهر عناصر النشاط أولاً، ثم عناصر كلّ جزء، وذلك بالترتيب الذي تمت فيه إضافة الأجزاء إلى النشاط. إذا لزم الأمر، يمكنك إعادة ترتيب عناصر القائمة باستخدامسمة android:orderInCategory في كل<item> تريد نقله.

لتحديد قائمة الخيارات لنشاط معيّن، يمكنك إلغاء onCreateOptionsMenu(). تقدّم الأجزاء onCreateOptionsMenu() مكالمة برمجية خاصة بها. في هذه الطريقة، يمكنك توسيع نطاق مرجع القائمة، الذي تم تحديده في ملف XML، إلى Menu المقدَّمة في الاستدعاء. يظهر ذلك في المثال التالي:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    val inflater: MenuInflater = menuInflater
    inflater.inflate(R.menu.game_menu, menu)
    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_menu, menu);
    return true;
}

يمكنك أيضًا إضافة عناصر قائمة باستخدام add() واسترداد العناصر باستخدام findItem() لمراجعة خصائصها باستخدام واجهات برمجة تطبيقات MenuItem.

التعامل مع أحداث النقر

عندما يختار المستخدم عنصرًا من قائمة الخيارات، بما في ذلك عناصر الإجراءات في شريط التطبيق، يستدعي النظام onOptionsItemSelected() طريقة نشاطك. تُرسِل هذه الطريقة MenuItem المحدّد. يمكنك تحديد العنصر من خلال استدعاء getItemId()، الذي يعرض المعرّف الفريد لعنصر القائمة، والذي يتم تحديده من خلال سمة android:id في مورد القائمة أو باستخدام عدد صحيح يتم تقديمه لطريقة add(). يمكنك مطابقة هذا المعرّف مع ملف تعريف العناصر المعروفة في القائمة لتنفيذ الإجراء المناسب.

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // Handle item selection.
    return when (item.itemId) {
        R.id.new_game -> {
            newGame()
            true
        }
        R.id.help -> {
            showHelp()
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection.
    switch (item.getItemId()) {
        case R.id.new_game:
            newGame();
            return true;
        case R.id.help:
            showHelp();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

عند معالجة عنصر قائمة بنجاح، أرسِل القيمة true. إذا لم تعالج عنصر القائمة، استخدِم تنفيذ الفئة الأساسية لمحاولة onOptionsItemSelected(). يعرض التنفيذ التلقائي قيمة false.

إذا كان نشاطك يتضمّن أجزاء، يستدعي النظام أولاً onOptionsItemSelected() للنشاط، ثم يستدعي كل جزء بالترتيب الذي تمت به إضافة الأجزاء، إلى أن يعرض أحدها true أو يتم استدعاء جميع الأجزاء.

تغيير عناصر القائمة أثناء التشغيل

بعد أن يستدعي النظام onCreateOptionsMenu()، يحتفظ بأحد مثيلات Menu التي تم تعبئتها ولا يستدعي onCreateOptionsMenu() مرة أخرى ما لم يتم إلغاء صلاحية القائمة. ومع ذلك، استخدِم onCreateOptionsMenu() فقط لإنشاء حالة القائمة الأولية وليس لإجراء تغييرات أثناء دورة حياة النشاط.

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

تُعتبر قائمة الخيارات مفتوحة دائمًا عند عرض عناصر القائمة في شريط التطبيق. عند حدوث حدث وأردت إجراء تعديل على القائمة، اتصل بالرقم invalidateOptionsMenu() لطلب أن يتصل النظام بالرقم onPrepareOptionsMenu().

إنشاء قائمة سياقية

صورة تعرض قائمة سياقات عائمة
الشكل 3. قائمة سياق عائمة

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

هناك طريقتان لتقديم الإجراءات المستندة إلى السياق:

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

ملاحظة: لا تتيح قائمة السياقات استخدام اختصارات العناصر ورموزها.

إنشاء قائمة سياقات عائمة

لتقديم قائمة سياق عائمة، اتّبِع الخطوات التالية:

  1. سجِّل View المرتبط بقائمة السياقات من خلال الاتصال registerForContextMenu() ونقْل View إليه.

    إذا كان نشاطك يستخدم RecyclerView وأردت أن يقدّم كل عنصر قائمة السياق نفسها، سجِّل جميع العناصر لقائمة سياق من خلال تمرير RecyclerView إلى registerForContextMenu().

  2. نفِّذ onCreateContextMenu() Activity أو Fragment.

    عندما يتلقّى العرض المسجَّل حدث لمس مع الاستمرار، يستدعي النظام طريقة onCreateContextMenu(). يمكنك هنا تحديد عناصر القائمة، عادةً من خلال تضخيم مورد قائمة، كما هو موضّح في المثال التالي:

    Kotlin

        override fun onCreateContextMenu(menu: ContextMenu, v: View,
                                menuInfo: ContextMenu.ContextMenuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo)
            val inflater: MenuInflater = menuInflater
            inflater.inflate(R.menu.context_menu, menu)
        }
        

    Java

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v,
                                        ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
        }
        

    MenuInflater يتيح لك هذا العنصر تضخيم قائمة السياقات من مورد قائمة. تتضمّن مَعلمات أسلوب callback العنصر View الذي يختاره المستخدم وموضوع ContextMenu.ContextMenuInfo الذي يقدّم معلومات إضافية عن العنصر المحدّد. إذا كان نشاطك يتضمّن عدة طرق عرض يقدّم كلّ منها قائمة سياقات مختلفة، يمكنك استخدام هذه المَعلمات لتحديد قائمة السياقات التي تريد توسيعها.

  3. نفِّذ onContextItemSelected()، كما هو موضّح في المثال التالي. عندما يختار المستخدم عنصر قائمة، يُطلِق نظام التشغيل هذه الطريقة لتتمكّن من تنفيذ الإجراء المناسب.

    Kotlin

        override fun onContextItemSelected(item: MenuItem): Boolean {
            val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
            return when (item.itemId) {
                R.id.edit -> {
                    editNote(info.id)
                    true
                }
                R.id.delete -> {
                    deleteNote(info.id)
                    true
                }
                else -> super.onContextItemSelected(item)
            }
        }
        

    Java

        @Override
        public boolean onContextItemSelected(MenuItem item) {
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            switch (item.getItemId()) {
                case R.id.edit:
                    editNote(info.id);
                    return true;
                case R.id.delete:
                    deleteNote(info.id);
                    return true;
                default:
                    return super.onContextItemSelected(item);
            }
        }
        

    تبحث طريقة getItemId() عن رقم تعريف عنصر القائمة المحدّد، والذي تحدّده لكل عنصر قائمة في ملف XML باستخدام سمة android:id، كما هو موضّح في تحديد قائمة في ملف XML.

    عند معالجة عنصر قائمة بنجاح، أرسِل القيمة true. إذا لم تعالج عنصر القائمة، مرِّر عنصر القائمة إلى تنفيذ الدرجة العليا من الفئة. إذا كان نشاطك يتضمّن أجزاء، يتلقّى النشاط هذا المرجع إلى دالة الربط أولاً. من خلال استدعاء الفئة الفائقة عند عدم معالجتها، يُحيل النظام الحدث إلى طريقة الاستدعاء ذات الصلة في كل جزء، واحدًا تلو الآخر ، بترتيب إضافة كل جزء، إلى أن يتم عرض true أو false. تُعرِض عمليات التنفيذ التلقائية لمحاولة Activity وandroid.app.Fragment القيمة false، لذا يجب دائمًا استدعاء الفئة الفائقة عند عدم معالجتها.

استخدام وضع الإجراءات المستندة إلى السياق

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

بالنسبة إلى المشاهد التي توفّر إجراءات مستندة إلى السياق، يتم عادةً تفعيل وضع الإجراءات المستندة إلى السياق عند حدوث أحد هذين الحدثَين أو كليهما:

  • يضغط المستخدم مع الاستمرار على العرض.
  • يختار المستخدم مربّع اختيار أو مكوّن واجهة مستخدم مشابهًا ضمن طريقة العرض.

تعتمد طريقة تطبيقك في استدعاء وضع الإجراءات السياقية وتحديد السلوك لكل إجراء على تصميمك. هناك تصميمان:

  • للإجراءات السياقية على مشاهدات فردية عشوائية
  • للإجراءات السياقية المجمّعة على مجموعات من العناصر في RecyclerView، ما يتيح للمستخدم اختيار عناصر متعددة وتنفيذ إجراء عليها جميعًا.

توضّح الأقسام التالية الإعدادات المطلوبة للسيناريو الأول.

تفعيل وضع الإجراءات المستندة إلى السياق لعروض فردية

إذا كنت تريد تفعيل وضع الإجراءات المستندة إلى السياق فقط عندما يختار المستخدِم طرق عرض معيّنة، اتّبِع الخطوات التالية:

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

    Kotlin

        private val actionModeCallback = object : ActionMode.Callback {
            // Called when the action mode is created. startActionMode() is called.
            override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
                // Inflate a menu resource providing context menu items.
                val inflater: MenuInflater = mode.menuInflater
                inflater.inflate(R.menu.context_menu, menu)
                return true
            }
    
            // Called each time the action mode is shown. Always called after
            // onCreateActionMode, and might be called multiple times if the mode
            // is invalidated.
            override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
                return false // Return false if nothing is done
            }
    
            // Called when the user selects a contextual menu item.
            override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
                return when (item.itemId) {
                    R.id.menu_share -> {
                        shareCurrentItem()
                        mode.finish() // Action picked, so close the CAB.
                        true
                    }
                    else -> false
                }
            }
    
            // Called when the user exits the action mode.
            override fun onDestroyActionMode(mode: ActionMode) {
                actionMode = null
            }
        }
        

    Java

        private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
    
            // Called when the action mode is created. startActionMode() is called.
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                // Inflate a menu resource providing context menu items.
                MenuInflater inflater = mode.getMenuInflater();
                inflater.inflate(R.menu.context_menu, menu);
                return true;
            }
    
            // Called each time the action mode is shown. Always called after
            // onCreateActionMode, and might be called multiple times if the mode
            // is invalidated.
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false; // Return false if nothing is done.
            }
    
            // Called when the user selects a contextual menu item.
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
               switch (item.getItemId()) {
                    case R.id.menu_share:
                        shareCurrentItem();
                        mode.finish(); // Action picked, so close the CAB.
                        return true;
                    default:
                        return false;
                }
            }
    
            // Called when the user exits the action mode.
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                actionMode = null;
            }
        };
        

    إنّ وظائف الاستدعاء هذه للأحداث متطابقة تقريبًا مع وظائف الاستدعاء ل قائمة الخيارات، باستثناء أنّ كلّ واحدة من هذه الوظائف تُمرِّر أيضًا عنصر ActionMode المرتبط بالحدث. يمكنك استخدام واجهات برمجة التطبيقات ActionMode لإجراء تغييرات مختلفة على CAB، مثل مراجعة العنوان والعنوان الفرعي باستخدام setTitle() و setSubtitle()، وهو أمر مفيد للإشارة إلى عدد العناصر المحدّدة.

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

  2. اتصل startActionMode() عندما تريد عرض الشريط، مثلاً عندما يلمس المستخدم الشاشة ويشدِّد على اللمس في العرض.

    Kotlin

        someView.setOnLongClickListener { view ->
            // Called when the user performs a touch & hold on someView.
            when (actionMode) {
                null -> {
                    // Start the CAB using the ActionMode.Callback defined earlier.
                    actionMode = activity?.startActionMode(actionModeCallback)
                    view.isSelected = true
                    true
                }
                else -> false
            }
        }
        

    Java

        someView.setOnLongClickListener(new View.OnLongClickListener() {
            // Called when the user performs a touch & hold on someView.
            public boolean onLongClick(View view) {
                if (actionMode != null) {
                    return false;
                }
    
                // Start the CAB using the ActionMode.Callback defined earlier.
                actionMode = getActivity().startActionMode(actionModeCallback);
                view.setSelected(true);
                return true;
            }
        });
        

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

إنشاء قائمة منبثقة

صورة تعرض قائمة منبثقة في تطبيق Gmail، تم تثبيتها على زر القائمة الكاملة في أعلى يسار الصفحة
الشكل 4. قائمة منبثقة في تطبيق Gmail، مرتبطة بزر القائمة الكاملة في أعلى يسار الصفحة

PopupMenu هي قائمة وضع نوافذ مشروطة مرتبطة بعنصر View. يظهر أسفل العنصر المرتبط العرض إذا كانت هناك مساحة، أو فوق العرض في حال عدم توفّر مساحة. وهي مفيدة في ما يلي:

  • توفير قائمة بأسلوب القائمة المنسدلة للإجراءات التي ترتبط بمحتوى معيّن، مثل رؤوس الرسائل الإلكترونية في Gmail، كما هو موضّح في الشكل 4
  • تقديم جزء ثانٍ من جملة الأمر، مثل زر يحمل العلامة إضافة يعرض قائمة منبثقة تتضمّن خيارات مختلفة لالإضافة
  • توفير قائمة مشابهة لرمز Spinner لا تحتفظ باختيار دائم

إذا كنت تحدّد قائمتك بتنسيق XML، إليك كيفية عرض القائمة المنبثقة:

  1. أنشئ مثيلًا لعنصر PopupMenu باستخدام وظيفته المُنشئة التي تأخذ Context التطبيق الحالي View و View الذي تم تثبيت القائمة عليه.
  2. استخدِم MenuInflater لتضخيم مورد القائمة في عنصر Menu الذي يعرضه PopupMenu.getMenu().
  3. الاتصال بالرقم PopupMenu.show().

على سبيل المثال، إليك زر يعرض قائمة منبثقة:

<ImageButton
    android:id="@+id/dropdown_menu"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/descr_overflow_button"
    android:src="@drawable/arrow_drop_down" />

يمكن للنشاط بعد ذلك عرض القائمة المنبثقة على النحو التالي:

Kotlin

findViewById<ImageButton>(R.id.dropdown_menu).setOnClickListener {
    val popup = PopupMenu(this, it)
    val inflater: MenuInflater = popup.menuInflater
    inflater.inflate(R.menu.actions, popup.menu)
    popup.show()
}

Java

findViewById(R.id.dropdown_menu).setOnClickListener(v -> {
    PopupMenu popup = new PopupMenu(this, v);
    popup.getMenuInflater().inflate(R.menu.actions, popup.getMenu());
    popup.show();
});

يتم إغلاق القائمة عندما يختار المستخدم عنصرًا أو ينقر خارج منطقة القائمة. يمكنك الاستماع إلى حدث الإغلاق باستخدام PopupMenu.OnDismissListener.

التعامل مع أحداث النقر

لتنفيذ إجراء عندما يختار المستخدم عنصرًا من القائمة، نفِّذ واجهة PopupMenu.OnMenuItemClickListener واسجِّلها في PopupMenu من خلال استدعاء setOnMenuItemclickListener(). عندما يختار المستخدم عنصرًا، يستدعي النظام onMenuItemClick() دالة الاستدعاء في واجهتك.

يظهر ذلك في المثال التالي:

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener.
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

Java

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener.
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

إنشاء مجموعة قوائم طعام

مجموعة القوائم هي مجموعة من عناصر القائمة التي تتشارك سمات معيّنة. باستخدام المجموعة، يمكنك إجراء ما يلي:

  • يمكنك إظهار جميع العناصر أو إخفاؤها باستخدام setGroupVisible().
  • فعِّل كل العناصر أو أوقِفها باستخدام setGroupEnabled().
  • حدِّد ما إذا كان بإمكان المستخدمين وضع علامة في المربّعات بجانب كل العناصر باستخدام رمز setGroupCheckable().

يمكنك إنشاء مجموعة من خلال إدراج عناصر <item> داخل عنصر <group> في مورد القائمة أو من خلال تحديد معرّف مجموعة باستخدام الأسلوب add().

في ما يلي مثال على مورد قائمة يتضمّن مجموعة:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

تظهر العناصر في المجموعة على المستوى نفسه الذي يظهر به العنصر الأول، وجميع العناصر الثلاثة في القائمة هي عناصر متسلسلة. ومع ذلك، يمكنك تعديل سمات السلعتَين في المجموعة من خلال الإشارة إلى معرّف المجموعة واستخدام الطرق السابقة. ولا يفصل النظام أبدًا العناصر المجمّعة. على سبيل المثال، إذا أعلنت عن android:showAsAction="ifRoom" لكل عنصر، سيظهر كلاهما في شريط الإجراءات أو في overflow الإجراء.

استخدام عناصر قائمة يمكن وضع علامة عليها

الشكل 5. قائمة فرعية تحتوي على عناصر يمكن وضع علامة عليها

يمكن أن تكون القائمة مفيدة كواجهة لتفعيل الخيارات وإيقافها، وذلك باستخدام مربّع اختيار للخيارات المستقلة أو أزرار اختيار لمجموعات من الخيارات المشترَكة. يعرض الشكل 5 قائمة فرعية تحتوي على عناصر يمكن وضع علامة عليها باستخدام buttonsradio buttons.

يمكنك تحديد السلوك الذي يمكن التحكّم فيه لعناصر قائمة فردية باستخدام سمة android:checkable في العنصر <item> ، أو لمجموعة كاملة باستخدام سمة android:checkableBehavior في العنصر <group>. على سبيل المثال، يمكن وضع علامة في المربّع بجانب كل العناصر في مجموعة القوائم هذه باستخدام زر اختيار:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

تقبل السمة android:checkableBehavior أحد العناصر التالية:

single
يمكن وضع علامة في عنصر واحد فقط من المجموعة، ما يؤدي إلى استخدام مفاتيح اختيار.
all
يمكن وضع علامة في كل العناصر، ما يؤدي إلى ظهور مربّعات اختيار.
none
لا يمكن وضع علامة في المربّع بجانب أي عناصر.

يمكنك تطبيق حالة محدَّدة تلقائيًا على عنصر باستخدام سمة android:checked في العنصر <item> وتغييرها في الرمز باستخدام الطريقة setChecked().

عند اختيار عنصر قابل للتحقّق، يستدعي النظام أسلوب callback المرتبط بالعنصر، مثل onOptionsItemSelected(). يمكنك هنا ضبط حالة مربّع الاختيار، لأنّ مربّع الاختيار أو زر الاختيار المبرمَج لا يغيّر حالته تلقائيًا. يمكنك الاستعلام عن الحالة الحالية للعنصر، كما كانت قبل أن يختارها المستخدم، باستخدام isChecked() ثم ضبط الحالة التي تم وضع علامة عليها باستخدام setChecked(). يظهر ذلك في المثال التالي:

Kotlin

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.vibrate, R.id.dont_vibrate -> {
            item.isChecked = !item.isChecked
            true
        }
        else -> super.onOptionsItemSelected(item)
    }
}

Java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.vibrate:
        case R.id.dont_vibrate:
            if (item.isChecked()) item.setChecked(false);
            else item.setChecked(true);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

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

إضافة عناصر قائمة استنادًا إلى نية

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

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

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

  1. حدِّد نية باستخدام الفئة CATEGORY_ALTERNATIVE أو CATEGORY_SELECTED_ALTERNATIVE، أو كليهما، بالإضافة إلى أي متطلبات أخرى.
  2. يُرجى الاتصال بالرقم Menu.addIntentOptions(). بعد ذلك، يبحث Android عن أي تطبيقات يمكنها تنفيذ الطلب ويُعدِّلها ويُضيفها إلى قائمتك.

إذا لم تكن هناك تطبيقات مثبَّتة تستوفي الطلب، لن تتم إضافة أي ملف شخصي في القائمة.

يظهر ذلك في المثال التالي:

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)

    // Create an Intent that describes the requirements to fulfill, to be
    // included in the menu. The offering app must include a category value
    // of Intent.CATEGORY_ALTERNATIVE.
    val intent = Intent(null, dataUri).apply {
        addCategory(Intent.CATEGORY_ALTERNATIVE)
    }

    // Search and populate the menu with acceptable offering apps.
    menu.addIntentOptions(
            R.id.intent_group,  // Menu group to which new items are added.
            0,                  // Unique item ID (none).
            0,                  // Order for the items (none).
            this.componentName, // The current activity name.
            null,               // Specific items to place first (none).
            intent,             // Intent created above that describes the requirements.
            0,                  // Additional flags to control items (none).
            null)               // Array of MenuItems that correlate to specific items (none).

    return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu){
    super.onCreateOptionsMenu(menu);

    // Create an Intent that describes the requirements to fulfill, to be
    // included in the menu. The offering app must include a category value
    // of Intent.CATEGORY_ALTERNATIVE.
    Intent intent = new Intent(null, dataUri);
    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

    // Search and populate the menu with acceptable offering apps.
    menu.addIntentOptions(
         R.id.intent_group,         // Menu group to which new items are added.
         0,                         // Unique item ID (none).
         0,                         // Order for the items (none).
         this.getComponentName(),   // The current activity name.
         null,                      // Specific items to place first (none).
         intent,                    // Intent created above that describes the requirements.
         0,                         // Additional flags to control items (none).
         null);                     // Array of MenuItems that correlate to specific items (none).

    return true;
}

بالنسبة إلى كل نشاط تم العثور عليه ويقدّم فلتر نية يتطابق مع النيّة المحدّدة، تتم إضافة عنصر قائمة باستخدام القيمة في android:label لفلتر النيّة كعنوان لعنصر القائمة ورمز التطبيق كرمز لعنصر القائمة. تُعرِض الطريقة addIntentOptions() عدد عناصر القائمة التي تمت إضافتها.

السماح بإضافة نشاطك إلى قوائم أخرى

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

لتضمين التطبيق في قوائم التطبيقات الأخرى، حدِّد فلتر أهداف كالمعتاد، ولكن أدرِج القيم CATEGORY_ALTERNATIVE أو CATEGORY_SELECTED_ALTERNATIVE أو كليهما لفئة فلتر الأهداف. يظهر ذلك في المثال التالي:

<intent-filter label="@string/resize_image">
    ...
    <category android:name="android.intent.category.ALTERNATIVE" />
    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    ...
</intent-filter>

اطّلِع على مزيد من المعلومات عن كتابة فلاتر الأهداف في مقالة الأهداف وفلاتر الأهداف.