إضافة قوائم

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

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

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

إذا كان نشاطك يتضمّن أجزاءً، يطلب النظام أولاً الرمز 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" لكل عنصر، سيظهر كلاهما في شريط الإجراءات أو يظهر كلاهما في الإجراء overflow.

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

الشكل 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 إضافة عناصر في القائمة بشكل ديناميكي عندما يعثر 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>

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