إضافة قوائم

تجربة طريقة الإنشاء
إنّ 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>. تكون القوائم الفرعية مفيدة عندما يحتوي التطبيق على الكثير من الوظائف التي يمكن تنظيمها إلى مواضيع، مثل العناصر في شريط القوائم في تطبيق الكمبيوتر الشخصي، مثل File (ملف) وEdit (تعديل) وView (عرض). اطّلِع على المثال التالي:

<?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>

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