إضافة قوائم

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

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

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

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

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

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

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

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

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

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

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

تحديد قائمة في ملف 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>

لاستخدام القائمة في نشاطك، عليك _inflate_ مورد القائمة، وتحويل مورد 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(). يُرجع التنفيذ الافتراضي خطأ.

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

تغيير عناصر القائمة في وقت التشغيل

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

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

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

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

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

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

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

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

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

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

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

  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 تضخيم قائمة السياقات من أحد موارد القوائم. تشمل مَعلمات طريقة معاودة الاتصال السمة 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 التي ترتبط بها القائمة.
  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" لكل عنصر، سيظهر كلاهما في شريط الإجراءات أو سيظهر كلاهما في المجموعة الكاملة للإجراءات.

استخدام عناصر القائمة القابلة للتحديد

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

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

يمكنك تحديد السلوك القابل للالتحقّق لعناصر القائمة الفردية باستخدام السمة 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().

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

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

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

فإذا لم تكن هناك أي تطبيقات مثبتة تفي بالغرض، فلن تتم إضافة عناصر قائمة.

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

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>

اقرأ المزيد حول كتابة فلاتر الأهداف في فلاتر الأهداف والغايات.