افزودن منو

امتحان کردن روش Compose
Jetpack Compose جعبه‌ابزار واسط کاربر توصیه‌شده برای Android است. با نحوه افزودن مؤلفه‌ها در Compose آشنا شوید.

منوها مؤلفه میانای کاربر رایجی در بسیاری از انواع برنامه‌ها هستند. برای ارائه تجربه کاربری آشنا و منسجم، از Menu API برای ارائه کنش‌های کاربر و گزینه‌های دیگر در فعالیت‌هایتان استفاده کنید.

تصویری که نمونه‌ای از منوِ سرریز را نشان می‌دهد
شکل ۱. منویی که با تک‌ضرب زدن روی نماد فعال می‌شود، زیر نماد منوِ سرریز ظاهر می‌شود.

این سند نشان می‌دهد که چگونه سه نوع اصلی منو یا ارائه کنش را در همه نسخه‌های 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
ارجاع به یک drawable برای استفاده به‌عنوان نماد مورد.
android:title
ارجاع به رشته‌ای که به‌عنوان عنوان مورد استفاده می‌شود.
android:showAsAction
مشخصات مربوط به زمان و نحوه نمایش این مورد به‌عنوان مورد اقدام در نوار برنامه.

این‌ها مهم‌ترین مشخصه‌هایی هستند که استفاده می‌کنید، اما مشخصه‌های بسیار دیگری نیز دردسترس است. برای کسب اطلاعات درباره همه مشخصه‌های پشتیبانی‌شده، به سند منبع منو مراجعه کنید.

می‌توانید با افزودن عنصر <menu> به‌عنوان فرزند <item>، زیرمنویی به هر موردی در هر منویی اضافه کنید. زیرمنوها زمانی مفید هستند که برنامه شما عملکردهای زیادی داشته باشد که بتوان آن‌ها را در موضوعاتی سازماندهی کرد، مانند موارد موجود در نوار منو برنامه رایانه شخصی—مانند فایل، ویرایش، و مشاهده. مثال زیر را ببینید:

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

برای استفاده از منو در فعالیتتان، منبع منو را _ازهم باز کنید_ و منبع XML را بااستفاده از MenuInflater.inflate() به شیء قابل‌برنامه‌ریزی تبدیل کنید. بخش‌های زیر نحوه گسترش منو را برای هر نوع منو نشان می‌دهد.

ایجاد منو گزینه‌ها

منوِ گزینه‌ها، مانند آنچه در شکل ۱ نشان داده شده است، جایی است که کنش‌ها و گزینه‌های دیگری را که به بافت فعالیت فعلی مربوط هستند، مانند «جستجو»، «نوشتن ایمیل»، و «تنظیمات» اضافه می‌کنید.

تصویری که نوار برنامه را برای برنامه «کاربرگ‌نگار Google» نشان می‌دهد
شکل ۲. برنامه «کاربرگ‌نگار Google» که چندین دکمه را نشان می‌دهد، ازجمله دکمه سرریز کنش.

می‌توانید موارد منوِ گزینه‌ها را از زیرکلاس Activity یا زیرکلاس Fragment اعلام کنید. اگر هم فعالیت و هم قطعه‌های شما مواردی را برای منوِ گزینه‌ها اعلام کنند، این موارد در واسط کاربر ترکیب می‌شوند. موارد فعالیت ابتدا نشان داده می‌شوند و سپس موارد هر تکه به‌ترتیبی که تکه‌ها به فعالیت اضافه شده‌اند نشان داده می‌شوند. درصورت لزوم، می‌توانید ترتیب گزینه‌های منو را با مشخصه android:orderInCategory در هر <item> که می‌خواهید جابه‌جا کنید تغییر دهید.

برای مشخص کردن منوِ گزینه‌ها برای یک فعالیت، onCreateOptionsMenu() را لغو کنید. قطعه‌ها onCreateOptionsMenu() برگشت تماس خودشان را ارائه می‌دهند. در این روش، می‌توانید منبع منو را که در XML تعریف شده است، در Menu ارائه‌شده در فراخوانی برگشتی گسترش دهید. این موضوع در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

@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() داده شده است تعریف می‌شود. می‌توانید این شناسه را با گزینه‌های منو شناخته‌شده مطابقت دهید تا کنش مناسب را انجام دهید.

کاتلین

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)
    }
}

جاوا

@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() تماس بگیرد.

ایجاد منوِ بافتی

تصویری که منوِ بافت شناور را نشان می‌دهد
شکل ۳. منوِ زمینه‌ای شناور.

منوِ بافتی کنش‌هایی را ارائه می‌دهد که بر مورد یا قاب بافت خاصی در واسط کاربر تأثیر می‌گذارد. می‌توانید برای هر نمایی منوِ زمینه‌ای ارائه دهید، اما این منوها اغلب برای موارد موجود در RecylerView یا مجموعه‌های نمای دیگر استفاده می‌شوند که در آن‌ها کاربر می‌تواند کنش‌های مستقیم روی هر مورد انجام دهد.

دو روش برای ارائه کنش‌های بافتاری وجود دارد:

  • در منوِ بافت شناور. وقتی کاربر روی نمایی که پشتیبانی از منوِ بافت را اعلام می‌کند لمس طولانی انجام می‌دهد، منو به‌صورت فهرست شناوری از گزینه‌های منو، شبیه به کادر گفتگو، ظاهر می‌شود. کاربران می‌توانند در هر زمان یک کنش بافتاری روی یک مورد انجام دهند.
  • در حالت کنش بافتاری. این حالت پیاده‌سازی سیستمی ActionMode است که نوار کنش بافتاری (یا CAB) را در بالای صفحه با دستورهای اقدام که بر مورد(های) انتخاب‌شده تأثیر می‌گذارد نمایش می‌دهد. وقتی این حالت فعال باشد، کاربران می‌توانند کنشی را به‌طور هم‌زمان روی چند مورد انجام دهند، البته اگر برنامه شما از آن پشتیبانی کند.

توجه: منوِ بافت از میان‌برهای مورد و نمادهای مورد پشتیبانی نمی‌کند.

ایجاد منوِ بافت شناور

برای ارائه منوِ زمینه‌ای شناور، کارهای زیر را انجام دهید:

  1. View را که منوِ بافت با آن مرتبط است با فراخوانی registerForContextMenu() و ارسال View به آن ثبت کنید.

    اگر فعالیت شما از RecyclerView استفاده می‌کند و می‌خواهید هر مورد منوِ بافت یکسانی ارائه دهد، همه موارد را برای منوِ بافت با ارسال RecyclerView به registerForContextMenu() ثبت کنید.

  2. روش onCreateContextMenu() را در Activity یا Fragment پیاده‌سازی کنید.

    وقتی نمای ثبت‌شده رویداد لمس کردن و نگه داشتن را دریافت می‌کند، سیستم روش onCreateContextMenu() شما را فراخوانی می‌کند. در اینجا گزینه‌های منو را تعریف می‌کنید، معمولاً با گسترش دادن منبع منو، مانند مثال زیر:

    کاتلین

        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)
        }
        

    جاوا

        @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() را همان‌طور که در مثال زیر نشان داده شده است پیاده‌سازی کنید. وقتی کاربر یکی از گزینه‌های منو را انتخاب می‌کند، سیستم این روش را فراخوانی می‌کند تا بتوانید کنش مناسب را انجام دهید.

    کاتلین

        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)
            }
        }
        

    جاوا

        @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 را همان‌طور که در مثال زیر نشان داده شده است پیاده‌سازی کنید. در روش‌های فراخوانی آن، می‌توانید کنش‌های مربوط به نوار کنش بافتاری را مشخص کنید، به رویدادهای کلیک روی دستورهای اقدام پاسخ دهید، و دیگر رویدادهای چرخه حیات را برای حالت کنش مدیریت کنید.

    کاتلین

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

    جاوا

        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 برای ایجاد تغییرات مختلف در نوار کنش زمینه‌ای استفاده کنید، مثلاً بازنگری عنوان و عنوان فرعی با setTitle() و setSubtitle()، که برای نشان دادن تعداد موارد انتخاب‌شده مفید است.

    نمونه قبلی متغیر actionMode را روی null تنظیم می‌کند وقتی حالت کنش ازبین می‌رود. در مرحله بعد، ببینید چگونه مقداردهی اولیه می‌شود و چگونه ذخیره کردن متغیر عضو در فعالیت یا قطعه شما می‌تواند مفید باشد.

  2. وقتی می‌خواهید نوار را نشان دهید، مثلاً وقتی کاربر روی نما لمس می‌کند و نگه می‌دارد، startActionMode() را فراخوانی کنید.

    کاتلین

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

    جاوا

        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 درصورتی‌که قبلاً فعال باشد، دوباره ایجاد نمی‌شود. این کار با بررسی اینکه آیا عضو قبل‌از شروع حالت کنش null است یا نه انجام می‌شود.

ایجاد منوِ بالاپر

تصویری که منوِ بالاپری را در برنامه Gmail نشان می‌دهد که به دکمه سرریز در بالا سمت چپ متصل شده است.
شکل ۴. منوِ بالاپری در برنامه Gmail، که به دکمه سرریز در گوشه بالا سمت چپ متصل شده است.

PopupMenu منویی مدال است که به View متصل شده است. اگر فضا وجود داشته باشد، در زیر نمای لنگر ظاهر می‌شود، درغیراین‌صورت در بالای نما ظاهر می‌شود. این کار برای موارد زیر مفید است:

  • ارائه منوِ سبک سرریز برای کنش‌هایی که مربوط به محتوای خاصی هستند، مثل سرصفحه‌های ایمیل Gmail، که در شکل ۴ نشان داده شده است.
  • ارائه بخش دوم جمله فرمان، مثل دکمه‌ای با برچسب افزودن که منوِ بالاپری با گزینه‌های مختلف افزودن تولید می‌کند.
  • ارائه منویی مشابه 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" />

سپس فعالیت می‌تواند منوِ بالاپر را به‌این‌صورت نشان دهد:

کاتلین

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()
}

جاوا

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 را پیاده‌سازی کنید و آن را با فراخوانی setOnMenuItemclickListener() در PopupMenu خود ثبت کنید. وقتی کاربر موردی را انتخاب می‌کند، سیستم onMenuItemClick() فراخوانی برگشتی را در واسط شما فراخوانی می‌کند.

این موضوع در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

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" را برای هر مورد اعلام کنید، هر دو در نوار اقدام یا هر دو در سرریز اقدام ظاهر می‌شوند.

استفاده از گزینه‌های منوِ قابل‌انتخاب

شکل ۵. منو فرعی با موارد قابل‌علامت‌گذاری.

منو می‌تواند به‌عنوان واسطی برای روشن و خاموش کردن گزینه‌ها، استفاده از چارگوش انتخاب برای گزینه‌های مستقل، یا دکمه‌های رادیویی برای گروه‌های گزینه‌های ناسازگار با یکدیگر مفید باشد. شکل ۵ زیرمنویی را با گزینه‌هایی نشان می‌دهد که با دکمه‌های رادیویی قابل‌انتخاب هستند.

می‌توانید رفتار قابل‌انتخاب را برای موارد منو به‌صورت جداگانه بااستفاده از مشخصه 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() تنظیم کنید. این موضوع در مثال زیر نشان داده شده است:

کاتلین

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)
    }
}

جاوا

@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 برنامه‌هایی را که می‌توانند هدف را اجرا کنند جستجو می‌کند و آن‌ها را به منو اضافه می‌کند.

اگر هیچ برنامه‌ای نصب نشده باشد که هدف را برآورده کند، هیچ موردی به منو اضافه نمی‌شود.

این موضوع در مثال زیر نشان داده شده است:

کاتلین

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
}

جاوا

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

درباره نوشتن فیلترهای قصد در قصدها و فیلترهای قصد بیشتر بخوانید.