اضافه کردن منوها

روش نوشتن را امتحان کنید
Jetpack Compose ابزار رابط کاربری پیشنهادی برای اندروید است. یاد بگیرید که چگونه کامپوننت‌ها را در Compose اضافه کنید.

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

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

این سند نحوه ایجاد سه نوع اساسی منو یا نمایش عملکرد را در تمام نسخه‌های اندروید نشان می‌دهد:

منوی گزینه‌ها و نوار برنامه
منوی گزینه‌ها، مجموعه اصلی آیتم‌های منو برای یک فعالیت است. جایی است که شما اقداماتی را که تأثیر کلی بر برنامه دارند، مانند «جستجو»، «نوشتن ایمیل» و «تنظیمات» قرار می‌دهید.

به بخش منوی «ایجاد گزینه‌ها» مراجعه کنید.

منوی زمینه و حالت اقدام زمینه‌ای
منوی زمینه، یک منوی شناور است که وقتی کاربر یک عنصر را لمس کرده و نگه می‌دارد، ظاهر می‌شود. این منو عملکردهایی را ارائه می‌دهد که بر محتوای انتخاب شده یا قاب زمینه تأثیر می‌گذارند.

حالت عملیات زمینه‌ای، آیتم‌های عملیاتی را که بر محتوای انتخاب‌شده تأثیر می‌گذارند، در نواری در بالای صفحه نمایش می‌دهد و به کاربر اجازه می‌دهد چندین آیتم را انتخاب کند.

به بخش ایجاد منوی زمینه‌ای مراجعه کنید.

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

به بخش ایجاد منوی بازشو مراجعه کنید.

تعریف منو در XML

برای همه انواع منو، اندروید یک فرمت استاندارد XML برای تعریف آیتم‌های منو ارائه می‌دهد. به جای ساخت یک منو در کد activity خود، یک منو و تمام آیتم‌های آن را در یک منبع منوی XML تعریف کنید. سپس می‌توانید منبع منو را inflate کنید - آن را به عنوان یک شیء Menu بارگذاری کنید - در activity یا fragment خود.

استفاده از منبع منو به دلایل زیر روش خوبی است:

  • تجسم ساختار منو در 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> ، یک زیرمنو به یک آیتم در هر منویی اضافه کنید. زیرمنوها زمانی مفید هستند که برنامه شما توابع زیادی داشته باشد که بتوان آنها را در قالب موضوعات سازماندهی کرد، مانند آیتم‌های موجود در نوار منوی یک برنامه کامپیوتری - مانند 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() به یک شیء قابل برنامه‌ریزی تبدیل کنید. بخش‌های زیر نحوه inflate کردن یک منو برای هر نوع منو را نشان می‌دهد.

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

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

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

شما می‌توانید آیتم‌هایی را برای منوی گزینه‌ها از زیرکلاس Activity یا زیرکلاس Fragment خود تعریف کنید. اگر هم activity و هم fragmentهای شما آیتم‌هایی را برای منوی گزینه‌ها تعریف کنند، این آیتم‌ها در رابط کاربری ترکیب می‌شوند. آیتم‌های activity ابتدا ظاهر می‌شوند و پس از آن آیتم‌های هر fragment، به ترتیبی که fragmentها به activity اضافه می‌شوند، نمایش داده می‌شوند. در صورت لزوم، می‌توانید آیتم‌های منو را با استفاده از ویژگی 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() آیتم‌ها را بازیابی کنید تا ویژگی‌های آنها را با استفاده از APIهای MenuItem اصلاح کنید.

مدیریت رویدادهای کلیک

وقتی کاربر یک آیتم را از منوی گزینه‌ها، از جمله آیتم‌های عملیاتی در نوار برنامه، انتخاب می‌کند، سیستم متد onOptionsItemSelected() اکتیویتی شما را فراخوانی می‌کند. این متد، MenuItem selected را ارسال می‌کند. می‌توانید آیتم را با فراخوانی 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() ارائه می‌دهند.

منوی گزینه‌ها (options menu) زمانی که آیتم‌های منو در نوار برنامه نمایش داده می‌شوند، همیشه باز در نظر گرفته می‌شود. وقتی رویدادی رخ می‌دهد و می‌خواهید یک به‌روزرسانی منو انجام دهید، تابع invalidateOptionsMenu() را فراخوانی کنید تا از سیستم بخواهید که تابع onPrepareOptionsMenu() را فراخوانی کند.

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

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

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

دو راه برای ارائه اقدامات زمینه‌ای وجود دارد:

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

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

ایجاد یک منوی زمینه شناور

برای ایجاد یک منوی زمینه شناور، مراحل زیر را انجام دهید:

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

    اگر activity شما از 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 است که اطلاعات بیشتری در مورد مورد انتخاب شده ارائه می‌دهد. اگر activity شما چندین View دارد که هر کدام منوی زمینه متفاوتی ارائه می‌دهند، می‌توانید از این پارامترها برای تعیین اینکه کدام منوی زمینه را بزرگ کنید، استفاده کنید.

  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() شناسه (ID) آیتم منوی انتخاب شده را جستجو می‌کند، که شما با استفاده از ویژگی android:id به هر آیتم منو در XML اختصاص می‌دهید، همانطور که در تعریف منو در XML نشان داده شده است.

    وقتی با موفقیت یک آیتم منو را مدیریت کردید، true را برگردانید. اگر آیتم منو را مدیریت نکردید، آیتم منو را به پیاده‌سازی کلاس بالا (superclass) منتقل کنید. اگر activity شما شامل fragmentها باشد، activity ابتدا این callback را دریافت می‌کند. با فراخوانی کلاس بالا (superclass) در هنگام unhandled، سیستم رویداد را به متد callback مربوطه در هر fragment، یکی یکی، به ترتیب اضافه شدن هر fragment، تا زمانی که true یا false برگردانده شود، منتقل می‌کند. پیاده‌سازی‌های پیش‌فرض برای Activity و android.app.Fragment false را برمی‌گردانند، بنابراین همیشه هنگام unhandled، کلاس بالا را فراخوانی کنید.

از حالت اقدام زمینه‌ای استفاده کنید

حالت عملیات زمینه‌ای (contextual action mode) یک پیاده‌سازی سیستمی از ActionMode است که تعامل کاربر را بر انجام عملیات زمینه‌ای متمرکز می‌کند. وقتی کاربر این حالت را با انتخاب یک آیتم فعال می‌کند، یک نوار عملیات زمینه‌ای در بالای صفحه ظاهر می‌شود تا عملیاتی را که کاربر می‌تواند روی آیتم‌های انتخاب شده انجام دهد، نمایش دهد. در حالی که این حالت فعال است، کاربر می‌تواند چندین آیتم را انتخاب کند، اگر برنامه شما از آن پشتیبانی کند، و می‌تواند آیتم‌ها را از حالت انتخاب خارج کند و به پیمایش در داخل activity ادامه دهد. حالت عملیات غیرفعال است و نوار عملیات زمینه‌ای زمانی که کاربر همه آیتم‌ها را از حالت انتخاب خارج می‌کند، دکمه بازگشت را لمس می‌کند یا عملیات انجام شده (Done action) را در سمت چپ نوار لمس می‌کند، ناپدید می‌شود.

برای نماهایی که اقدامات زمینه‌ای ارائه می‌دهند، معمولاً وقتی یک یا هر دوی این دو رویداد رخ می‌دهد، حالت اقدام زمینه‌ای را فراخوانی می‌کنید:

  • کاربر روی نما لمس کرده و نگه می‌دارد.
  • کاربر یک کادر انتخاب یا یک کامپوننت رابط کاربری مشابه را در داخل نما انتخاب می‌کند.

اینکه برنامه شما چگونه حالت contextual action را فراخوانی می‌کند و رفتار هر اکشن را تعریف می‌کند، به طراحی شما بستگی دارد. دو طرح وجود دارد:

  • برای اقدامات زمینه‌ای روی دیدگاه‌های فردی و دلخواه.
  • برای انجام اقدامات زمینه‌ای دسته‌ای روی گروه‌هایی از آیتم‌ها در RecyclerView ، به کاربر اجازه می‌دهد چندین آیتم را انتخاب کند و روی همه آنها یک عمل انجام دهد.

بخش‌های زیر تنظیمات مورد نیاز برای سناریوی اول را شرح می‌دهند.

حالت عملکرد زمینه‌ای را برای نماهای منفرد فعال کنید

اگر می‌خواهید حالت contextual action را فقط زمانی که کاربر viewهای خاصی را انتخاب می‌کند، فراخوانی کنید، موارد زیر را انجام دهید:

  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 مرتبط با رویداد را نیز ارسال می‌کنند. می‌توانید از APIهای ActionMode برای ایجاد تغییرات مختلف در CAB استفاده کنید، مانند اصلاح عنوان و زیرنویس با setTitle() و setSubtitle() که برای نشان دادن تعداد موارد انتخاب شده مفید است.

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

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

ایجاد منوی پاپ‌آپ

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

یک منوی PopupMenu یک منوی مدال است که به یک View متصل شده است. اگر فضای کافی وجود داشته باشد، در زیر نمای anchor و در غیر این صورت در بالای نمای 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)

گاهی اوقات می‌خواهید یک آیتم منو، چه یک اکتیویتی در برنامه خودتان باشد و چه یک برنامه دیگر، یک اکتیویتی را با استفاده از Intent اجرا کند. وقتی که intent مورد نظر خود را می‌دانید و یک آیتم منوی خاص دارید که intent را آغاز می‌کند، می‌توانید intent را با startActivity() در طول متد فراخوانی on-item-selected مناسب، مانند onOptionsItemSelected() ، اجرا کنید.

با این حال، اگر مطمئن نیستید که دستگاه کاربر حاوی برنامه‌ای است که intent را مدیریت می‌کند، اضافه کردن یک آیتم منو که آن را فراخوانی می‌کند، می‌تواند منجر به عدم عملکرد یک آیتم منو شود، زیرا ممکن است intent به یک activity تبدیل نشود. برای حل این مشکل، اندروید به شما امکان می‌دهد وقتی اندروید activityهایی را در دستگاه پیدا می‌کند که intent شما را مدیریت می‌کنند، به صورت پویا آیتم‌های منو را به منوی خود اضافه کنید.

برای افزودن آیتم‌های منو بر اساس فعالیت‌های موجود که یک intent را می‌پذیرند، موارد زیر را انجام دهید:

  1. یک intent با دسته بندی CATEGORY_ALTERNATIVE یا CATEGORY_SELECTED_ALTERNATIVE یا هر دو، به علاوه هرگونه الزامات دیگر، تعریف کنید.
  2. تابع Menu.addIntentOptions() را فراخوانی کنید. سپس اندروید به دنبال هر برنامه‌ای که بتواند اینتنت را اجرا کند، می‌گردد و آن را به منوی شما اضافه می‌کند.

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

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

کاتلین

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

برای هر فعالیتی که یک فیلتر intent مطابق با intent تعریف شده ارائه می‌دهد، یک آیتم منو اضافه می‌شود، با استفاده از مقدار موجود در android:label فیلتر intent به عنوان عنوان آیتم منو و آیکون برنامه به عنوان آیکون آیتم منو. متد addIntentOptions() تعداد آیتم‌های منو اضافه شده را برمی‌گرداند.

بگذارید فعالیت شما به منوهای دیگر اضافه شود

شما می‌توانید سرویس‌های فعالیت خود را به برنامه‌های دیگر ارائه دهید تا برنامه شما در منوی دیگران قرار گیرد - برعکس نقش‌هایی که قبلاً توضیح داده شد.

برای اینکه در منوهای دیگر برنامه‌ها گنجانده شود، طبق معمول یک فیلتر intent تعریف کنید، اما مقادیر CATEGORY_ALTERNATIVE یا CATEGORY_SELECTED_ALTERNATIVE یا هر دو را برای دسته فیلتر intent وارد کنید. این در مثال زیر نشان داده شده است:

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

درباره نوشتن فیلترهای intent در بخش Intents و intent filters بیشتر بخوانید.