הוספת תפריטים

כדאי לנסות את התכונה 'כתיבה מהירה'
Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ב-Android. איך מוסיפים רכיבים ב-Compose

תפריטים הם רכיב נפוץ בממשק המשתמש של סוגים רבים של אפליקציות. כדי לספק חוויית משתמש מוכרת ועקבית, כדאי להשתמש בממשקי ה-API של 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
הפניה ל-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(). בקטעים הבאים מוסבר איך להרחיב תפריט לכל סוג תפריט.

יצירת תפריט אפשרויות

בתפריט האפשרויות, כמו זה שמוצג באיור 1, אפשר לכלול פעולות ואפשרויות אחרות שרלוונטיות להקשר של הפעילות הנוכחית, כמו 'חיפוש', 'כתיבת אימייל' ו'הגדרות'.

תמונה שבה מוצגת שורת האפליקציות של אפליקציית Google Sheets
איור 2. אפליקציית Google Sheets, שבה מוצגים כמה לחצנים, כולל לחצן האפשרויות הנוספות.

אפשר להצהיר על פריטים לתפריט האפשרויות ממעמד המשנה Activity או ממעמד המשנה Fragment. אם גם הפעילות וגם הפאזות מגדירים פריטים לתפריט האפשרויות, הפריטים משולבים בממשק המשתמש. הפריטים של הפעילות מופיעים קודם, ואחריהם הפריטים של כל קטע, לפי הסדר שבו הקטעים נוספים לפעילות. אם צריך, אפשר לשנות את הסדר של פריטי התפריט באמצעות המאפיין android:orderInCategory בכל <item> שרוצים להעביר.

כדי לציין את תפריט האפשרויות של פעילות, משנים את הערך של onCreateOptionsMenu(). לקטעים יש קריאה חוזרת (callback) משלהם של onCreateOptionsMenu(). בשיטה הזו אפשר לנפח את משאב התפריט, הוגדר ב-XML, ל-Menu שסופק ב-callback. הדוגמה הבאה ממחישה זאת:

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() כדי לשנות את המאפיינים שלהם באמצעות ממשקי ה-API של 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 כפי שהוא קיים כרגע, כדי שתוכלו לשנות אותו, למשל על ידי הוספה, הסרה או השבתה של פריטים. בנוסף, קטעי קוד מספקים קריאה חוזרת (callback) מסוג 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 מאפשרת להרחיב את תפריט ההקשר ממשאב תפריט. הפרמטרים של שיטת ה-callback כוללים את View שהמשתמש בוחר ואובייקט ContextMenu.ContextMenuInfo שמספק מידע נוסף על הפריט שנבחר. אם לפעילות יש כמה תצוגות, שכל אחת מהן מספקת תפריט הקשר שונה, תוכלו להשתמש בפרמטרים האלה כדי לקבוע איזה תפריט הקשר תנפחו.

  3. מטמיעים את onContextItemSelected(), כפי שמוצג בדוגמה הבאה. כשהמשתמש בוחר פריט תפריט, המערכת קורא ל-method הזה כדי שתוכלו לבצע את הפעולה המתאימה.

    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. אם לא מטפלים בפריט התפריט, מעבירים אותו להטמעה של הכיתה האב. אם הפעילות כוללת קטעים, הקריאה החוזרת הזו תישלח אליה קודם. כשקוראים לסופר-קלאס במקרה של אירוע שלא טופל, המערכת מעבירה את האירוע לשיטת ה-callback המתאימה בכל קטע, אחד אחרי השני, לפי הסדר שבו כל קטע נוסף, עד שהיא מחזירה את הערך true או false. הטמעות ברירת המחדל של Activity ו-android.app.Fragment מחזירות את הערך false, לכן תמיד צריך לקרוא לסופר-קלאס כשהאירוע לא מטופל.

שימוש במצב פעולה לפי הקשר

מצב הפעולות לפי הקשר הוא הטמעה מערכתית של ActionMode שמתמקדת באינטראקציה של המשתמשים לביצוע פעולות לפי הקשר. כשמשתמש מפעיל את המצב הזה על ידי בחירה בפריט, סרגל הפעולות לפי הקשר מופיע בחלק העליון של המסך כדי להציג את הפעולות שהמשתמש יכול לבצע בפריטים שנבחרו. כשהמצב הזה מופעל, המשתמש יכול לבחור כמה פריטים, אם האפליקציה תומכת בכך, ולבטל את הבחירה בפריטים ולהמשיך לנווט בפעילות. מצב הפעולה מושבת וסרגל הפעולות לפי הקשר נעלם כשהמשתמש מבטל את הבחירה בכל הפריטים, מקייש על הלחצן 'הקודם' או מקייש על הפעולה סיום בצד ימין של הסרגל.

בתצוגות שמציעות פעולות לפי הקשר, בדרך כלל מפעילים את מצב הפעולה לפי הקשר כשמתרחש אחד משני האירועים הבאים:

  • המשתמש מבצע לחיצה ארוכה על התצוגה.
  • המשתמש בוחר תיבת סימון או רכיב דומה בממשק המשתמש בתצוגה.

האופן שבו האפליקציה מפעילה את מצב הפעולה לפי הקשר ומגדירה את ההתנהגות של כל פעולה תלוי בעיצוב שלכם. יש שני עיצובים:

  • לפעולות לפי הקשר בתצוגות ספציפיות שרירותיות.
  • כדי לבצע פעולות לפי הקשר בכמות גדולה על קבוצות של פריטים ב-RecyclerView, ומאפשרות למשתמש לבחור כמה פריטים ולבצע פעולה על כולם.

בקטעים הבאים מתוארת ההגדרה הנדרשת לתרחיש הראשון.

הפעלת מצב פעולה לפי הקשר בתצוגות ספציפיות

אם רוצים להפעיל את מצב הפעולה לפי הקשר רק כשהמשתמש בוחר תצוגות ספציפיות, צריך לבצע את הפעולות הבאות:

  1. מטמיעים את הממשק ActionMode.Callback כפי שמוצג בדוגמה הבאה. בשיטות החזרה (callbacks) שלו, אפשר לציין את הפעולות של שורת הפעולות לפי הקשר, להגיב לאירועי לחיצה על פריטי פעולה ולטפל באירועים אחרים במחזור החיים של מצב הפעולה.

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

    פונקציות ה-callbacks של האירועים האלה כמעט זהות לפונקציות ה-callbacks של תפריט האפשרויות, מלבד העובדה שכל אחת מהן מעבירה גם את האובייקט ActionMode שמשויך לאירוע. אפשר להשתמש בממשקי ה-API של 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 לא נוצרת מחדש אם היא כבר פעילה, על ידי בדיקה אם המשתנה הוא null לפני שמתחילים את מצב הפעולה.

יצירת תפריט קופץ

תמונה שמציגה תפריט קופץ באפליקציית Gmail, שמקובע לכפתור האפשרויות הנוספות בפינה השמאלית העליונה.
איור 4. תפריט קופץ באפליקציית Gmail, שמקובע ללחצן האפשרויות הנוספות בפינה השמאלית העליונה.

PopupMenu הוא תפריט מודלי שמקובע ל-View. הוא מופיע מתחת לתצוגת הצ'אט הראשית אם יש מקום, או מעל לתצוגה אחרת. האפשרות הזו שימושית במקרים הבאים:

  • הצגת תפריט בסגנון 'overflow' לפעולות שקשורות לתוכן ספציפי, כמו כותרות האימייל ב-Gmail, כפי שמוצג באיור 4.
  • מתן חלק שני של משפט פקודה, למשל לחצן עם הכיתוב Add (הוספה) שפותח תפריט קופץ עם אפשרויות שונות של Add (הוספה).
  • הצגת תפריט דומה ל-Spinner שלא שומר בחירה קבועה.

אם מגדירים את התפריט ב-XML, כך מציגים את התפריט הקופץ:

  1. יוצרים מופע של PopupMenu באמצעות ה-constructor שלו, שמקבל את האפליקציה הנוכחית 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(). כשהמשתמש בוחר פריט, המערכת קוראת ל-callback‏ onMenuItemClick() בממשק.

הדוגמה הבאה ממחישה זאת:

Kotlin

fun showMenu(v: View) {
    PopupMenu(this, v).apply {
        // MainActivity implements OnMenuItemClickListener.
        setOnMenuItemClickListener(this@MainActivity)
        inflate(R.menu.actions)
        show()
    }
}

override fun onMenuItemClick(item: MenuItem): Boolean {
    return when (item.itemId) {
        R.id.archive -> {
            archive(item)
            true
        }
        R.id.delete -> {
            delete(item)
            true
        }
        else -> false
    }
}

Java

public void showMenu(View v) {
    PopupMenu popup = new PopupMenu(this, v);

    // This activity implements OnMenuItemClickListener.
    popup.setOnMenuItemClickListener(this);
    popup.inflate(R.menu.actions);
    popup.show();
}

@Override
public boolean onMenuItemClick(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.archive:
            archive(item);
            return true;
        case R.id.delete:
            delete(item);
            return true;
        default:
            return false;
    }
}

יצירת קבוצת תפריטים

קבוצת תפריטים היא אוסף של פריטי תפריט שיש להם מאפיינים משותפים. בעזרת קבוצה תוכלו:

  • מציגים או מסתירים את כל הפריטים באמצעות setGroupVisible().
  • מפעילים או משביתים את כל הפריטים באמצעות setGroupEnabled().
  • מציינים אם אפשר לסמן את כל הפריטים באמצעות setGroupCheckable().

אפשר ליצור קבוצה על ידי הטמעת רכיבי <item> בתוך רכיב <group> במשאב התפריט, או על ידי ציון מזהה קבוצה באמצעות השיטה add().

דוגמה למשאב תפריט שכולל קבוצה:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_save"
          android:icon="@drawable/menu_save"
          android:title="@string/menu_save" />
    <!-- menu group -->
    <group android:id="@+id/group_delete">
        <item android:id="@+id/menu_archive"
              android:title="@string/menu_archive" />
        <item android:id="@+id/menu_delete"
              android:title="@string/menu_delete" />
    </group>
</menu>

הפריטים בקבוצה מופיעים באותו הרמה כמו הפריט הראשון – כל שלושת הפריטים בתפריט הם אחים. עם זאת, אפשר לשנות את המאפיינים של שני הפריטים בקבוצה על ידי הפניה למזהה הקבוצה ושימוש בשיטות הקודמות. בנוסף, המערכת לעולם לא מפרידה בין פריטים מקובצים. לדוגמה, אם מגדירים את android:showAsAction="ifRoom" לכל פריט, שניהם מופיעים בסרגל הפעולות או שניהם מופיעים בסרגל האפשרויות הנוספות של הפעולות.

שימוש באפשרויות בתפריט שניתן לסמן

איור 5. תפריט משנה עם פריטים שאפשר לסמן.

תפריט יכול לשמש כממשק להפעלה או להשבתה של אפשרויות, באמצעות תיבת סימון לאפשרויות עצמאיות או לחצני בחירה לקבוצות של אפשרויות בלעדיות. באיור 5 מוצג תפריט משנה עם פריטים שאפשר לסמן באמצעות לחצני בחירה.

אפשר להגדיר את ההתנהגות של האפשרויות לבחירה בפריטי תפריט ספציפיים באמצעות המאפיין android:checkable ברכיב <item>, או בקבוצה שלמה באמצעות המאפיין android:checkableBehavior ברכיב <group>. לדוגמה, אפשר לסמן את כל הפריטים בקבוצת התפריט הזו באמצעות לחצן בחירה:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item android:id="@+id/red"
              android:title="@string/red" />
        <item android:id="@+id/blue"
              android:title="@string/blue" />
    </group>
</menu>

המאפיין android:checkableBehavior מקבל אחד מהערכים הבאים:

single
אפשר לסמן רק פריט אחד מהקבוצה, וכתוצאה מכך יופיעו לחצני רדיו.
all
אפשר לסמן את כל הפריטים, וכתוצאה מכך יופיעו תיבות סימון.
none
לא ניתן לסמן פריטים.

אפשר להחיל מצב מסומן כברירת מחדל על פריט באמצעות המאפיין android:checked ברכיב <item>, ולשנות אותו בקוד באמצעות השיטה setChecked().

כשבוחרים פריט שאפשר לסמן, המערכת קוראת לשיטת ה-callback הרלוונטית של הפריט שנבחר, למשל 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() במהלך שיטת ה-callback המתאימה לבחירת פריט, כמו ה-callback‏ onOptionsItemSelected().

עם זאת, אם אתם לא בטוחים שהמכשיר של המשתמש מכיל אפליקציה שמטפלת בכוונה, הוספת פריט תפריט שמפעיל אותה עלולה לגרום לכך שפריט התפריט לא יפעל, כי ייתכן שהכוונה לא תיפתר לפעילות. כדי לפתור את הבעיה, מערכת 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>

מידע נוסף על כתיבת מסנני כוונות מפורט במאמר כוונות ומסנני כוונות.