Dodaj menu

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak dodawać komponenty w oknie tworzenia wiadomości

Menu to powszechny element interfejsu w wielu typach aplikacji. Aby zapewnić użytkownikom dostęp do znanych i spójnych funkcji, wykorzystaj interfejsy API Menu do prezentowania działań użytkowników i innych opcji w swoich działaniach.

Obraz przedstawiający przykład rozszerzonego menu
Rysunek 1. Menu wywoływane po kliknięciu ikony pod ikoną rozszerzonego menu.

W tym dokumencie opisujemy, jak tworzyć 3 podstawowe rodzaje menu i prezentacji działań we wszystkich wersjach Androida:

menu Opcje i pasek aplikacji.
Menu opcji to podstawowy zbiór pozycji menu danej aktywności. Możesz tam dodawać działania, które mają globalny wpływ na działanie aplikacji, takie jak „Wyszukiwanie”, „Napisz e-maila” czy „Ustawienia”.

Zobacz sekcję Utwórz menu opcji.

Menu kontekstowe i tryb działań kontekstowych
Menu kontekstowe to pływające menu, które pojawia się, gdy użytkownik naciśnie i przytrzyma element. Zawiera ona działania, które wpływają na wybraną treść lub ramkę kontekstową.

Tryb działań kontekstowych wyświetla na pasku u góry ekranu działania, które mają wpływ na wybraną treść. Umożliwia on użytkownikowi wybranie kilku elementów.

Zobacz sekcję Tworzenie menu kontekstowego.

Wyskakujące menu
W menu znajduje się pionowa lista elementów zakotwiczonych w widoku, który wywołuje menu. Jest to przydatne, gdy udostępniasz dużo działań związanych z konkretną treścią lub udostępnia opcje dotyczące drugiej części polecenia. Działania w wyskakującym menu nie mają bezpośredniego wpływu na dane treści – od tego właśnie działają działania kontekstowe. Wyskakujące menu służy raczej do rozszerzonych działań związanych z regionami treści Twojej aktywności.

Zobacz sekcję Utwórz wyskakujące menu.

Definiowanie menu w pliku XML

W przypadku wszystkich typów menu Android udostępnia standardowy format XML do definiowania pozycji menu. Zamiast tworzyć menu w kodzie aktywności, zdefiniuj menu i wszystkie jego elementy w zasobie menu XML. Możesz następnie powiększyć zasób menu przez wczytanie go jako obiektu Menu w aktywności lub fragmencie.

Korzystanie z zasobu menu jest zalecane z tych powodów:

  • Ułatwia to wizualizację struktury menu w formacie XML.
  • Oddziela zawartość menu od kodu behawioralnego aplikacji.
  • Umożliwia tworzenie alternatywnych konfiguracji menu na potrzeby różnych wersji platformy, rozmiarów ekranu i innych konfiguracji za pomocą platformy zasobów aplikacji.

Aby zdefiniować menu, utwórz plik XML w katalogu res/menu/ projektu i utwórz menu za pomocą tych elementów:

<menu>
Określa element Menu, który jest kontenerem na pozycje menu. Element <menu> musi być węzłem głównym pliku i może zawierać co najmniej 1 element <item> oraz <group>.
<item>
Tworzy element MenuItem, który reprezentuje pojedynczy element menu. Ten element może zawierać zagnieżdżony element <menu> tworzący menu podrzędne.
<group>
Opcjonalny, niewidoczny kontener na elementy <item>. Umożliwia kategoryzowanie elementów menu w taki sposób, aby miały wspólne właściwości, np. stan aktywności i widoczność. Więcej informacji znajdziesz w sekcji Tworzenie grupy menu.

Oto przykładowe menu o nazwie 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>

Element <item> obsługuje kilka atrybutów, za pomocą których można zdefiniować wygląd i zachowanie elementu. Elementy w poprzednim menu obejmują te atrybuty:

android:id
Unikalny dla elementu identyfikator zasobu, dzięki któremu aplikacja może rozpoznać element, gdy użytkownik go wybierze.
android:icon
Odniesienie do elementu, który można rysować i używania jako ikony elementu.
android:title
Odwołanie do ciągu znaków, które służy jako tytuł elementu.
android:showAsAction
Specyfikacja dotycząca tego, kiedy i w jaki sposób element wyświetla się jako działanie na pasku aplikacji.

To najważniejsze atrybuty, z których korzystasz, ale dostępnych jest też wiele innych. Informacje na temat wszystkich obsługiwanych atrybutów znajdziesz w dokumentacji zasobów menu.

Możesz dodać menu podrzędne do elementu w dowolnym menu, dodając element <menu> jako element podrzędny <item>. Menu podrzędne są przydatne, gdy aplikacja zawiera wiele funkcji, które można uporządkować według tematów, takich jak pozycje na pasku menu aplikacji na PC, np. Plik, Edytuj i Widok. Przyjrzyj się temu przykładowi:

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

Aby użyć menu w aktywności, _inflate_ konwertuj zasób XML na obiekt dostępny do programowania za pomocą MenuInflater.inflate(). W sekcjach poniżej dowiesz się, jak powiększać menu w przypadku poszczególnych typów menu.

Tworzenie menu opcji

W menu opcji, podobnie jak na ilustracji 1, możesz dodać działania i inne opcje związane z bieżącym kontekstem działań, np. „Wyszukiwanie”, „Napisz e-maila” i „Ustawienia”.

Obraz przedstawiający pasek aplikacji Arkusze Google
Rysunek 2. Aplikacja Arkusze Google z kilkoma przyciskami, w tym przycisk menu rozszerzonego działania.

Elementy menu opcji możesz zadeklarować w podklasie Activity lub Fragment. Jeśli zarówno aktywność, jak i fragmenty deklarują elementy menu opcji, elementy te są łączone w interfejsie użytkownika. Na początku wyświetlają się elementy działania, a po nim te z każdego fragmentu, w kolejności, w jakiej zostały one dodane do działania. W razie potrzeby możesz zmienić kolejność pozycji menu, korzystając z atrybutu android:orderInCategory w każdym elemencie <item> do przeniesienia.

Aby określić menu opcji danej aktywności, zastąp onCreateOptionsMenu(). Fragmenty zawierają własne wywołanie zwrotne onCreateOptionsMenu(). Ta metoda pozwala uzupełnić zasób menu (zdefiniowany w pliku XML) do pliku Menu podanego w wywołaniu zwrotnym. Widać to w tym przykładzie:

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

Możesz też dodawać pozycje menu za pomocą funkcji add() i pobierać pozycje za pomocą funkcji findItem(), aby modyfikować ich właściwości za pomocą interfejsów API MenuItem.

Obsługa zdarzeń kliknięcia

Gdy użytkownik wybierze element w menu opcji, w tym działania na pasku aplikacji, system wywoła metodę onOptionsItemSelected() Twojej aktywności. Ta metoda przekazuje wybrane dane (MenuItem). Możesz zidentyfikować element, wywołując funkcję getItemId(), która zwraca unikalny identyfikator pozycji menu zdefiniowany przez atrybut android:id w zasobie menu lub liczbę całkowitą podaną w metodzie add(). Możesz go dopasować do znanych pozycji menu, by wykonać odpowiednie działanie.

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

Gdy przejdziesz do obsługi pozycji menu, zwróć true. Jeśli nie obsługujesz danego elementu menu, wywołaj implementację klasy nadrzędnej klasy onOptionsItemSelected(). Domyślna implementacja zwraca wartość „false”.

Jeśli aktywność zawiera fragmenty, system najpierw wywołuje dla niej funkcję onOptionsItemSelected(), a następnie dla każdego fragmentu w kolejności ich dodawania, aż do zwrócenia wartości true lub wywołania wszystkich fragmentów.

Zmieniaj pozycje menu podczas działania

Po wywołaniu funkcji onCreateOptionsMenu() system zachowuje instancję Menu uzupełnioną przez Ciebie i nie wywołuje ponownie onCreateOptionsMenu(), chyba że menu zostanie unieważnione. Pamiętaj jednak, żeby używać onCreateOptionsMenu() tylko do utworzenia początkowego stanu menu i nie wprowadzać zmian w trakcie cyklu życia aktywności.

Jeśli chcesz modyfikować menu opcji na podstawie zdarzeń występujących w trakcie cyklu życia aktywności, możesz to zrobić w metodzie onPrepareOptionsMenu(). Ta metoda przekazuje obiekt Menu w takiej postaci, w jakiej obecnie istnieje, dzięki czemu możesz go zmodyfikować, np. dodając, usuwając lub wyłączając elementy. Fragmenty zawierają też wywołanie zwrotne onPrepareOptionsMenu().

Gdy elementy menu znajdują się na pasku aplikacji, menu opcji jest uważane za zawsze otwarte. Gdy wystąpi zdarzenie i chcesz zaktualizować menu, wywołaj invalidateOptionsMenu(), aby zażądać wywołania systemowego onPrepareOptionsMenu().

Tworzenie menu kontekstowego

Obraz przedstawiający pływające menu kontekstowe
Rysunek 3. Pływające menu kontekstowe.

Menu kontekstowe zawiera działania, które wpływają na element lub ramkę kontekstową w interfejsie. Możesz podać menu kontekstowe dowolnego widoku, ale jest ono najczęściej używane w przypadku elementów w kolekcji RecylerView lub innych widoków, w których użytkownik może bezpośrednio wykonywać działania związane z każdym elementem.

Działania kontekstowe można określić na 2 sposoby:

  • w pływającym menu kontekstowym, Menu ma postać pływającej listy elementów menu, podobnie jak okno, gdy użytkownik naciśnie i przytrzyma widok, który deklaruje obsługę menu kontekstowego. Użytkownicy mogą wykonywać działania kontekstowe na jednym elemencie naraz.
  • w trybie działań kontekstowych, Ten tryb to implementacja systemowa ActionMode, która wyświetla u góry ekranu pasek działań kontekstowych(CAB) z działaniami wpływającymi na wybrane elementy. Gdy ten tryb jest aktywny, użytkownicy mogą wykonywać działania na wielu elementach jednocześnie, o ile Twoja aplikacja to umożliwia.

Tworzenie pływającego menu kontekstowego

Aby wyświetlić pływające menu kontekstowe:

  1. Zarejestruj element View, z którym jest powiązane menu kontekstowe, wywołując metodę registerForContextMenu() i przekazując ją View.

    Jeśli Twoja aktywność korzysta z właściwości RecyclerView i chcesz, aby każdy element miał to samo menu kontekstowe, zarejestruj wszystkie elementy w menu kontekstowym, przekazując RecyclerView do registerForContextMenu().

  2. Zaimplementuj metodę onCreateContextMenu() w Activity lub Fragment.

    Gdy zarejestrowany widok otrzyma zdarzenie kliknięcia i przytrzymania, system wywoła metodę onCreateContextMenu(). W tym miejscu definiuje się elementy menu, zwykle dodając nadmiar zasobu menu, jak w tym przykładzie:

    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 pozwala powiększyć menu kontekstowe z poziomu zasobu menu. Parametry metody wywołania zwrotnego obejmują obiekt View wybierany przez użytkownika oraz obiekt ContextMenu.ContextMenuInfo, który zawiera dodatkowe informacje o wybranym elemencie. Jeśli Twoja aktywność ma kilka widoków danych, z których każdy zawiera inne menu kontekstowe, możesz na podstawie tych parametrów określić, które menu kontekstowe powiększyć.

  3. Wdróż onContextItemSelected() zgodnie z tym przykładem. Gdy użytkownik wybierze pozycję w menu, system wywołuje tę metodę, aby umożliwić Ci wykonanie odpowiedniego działania.

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

    Metoda getItemId() wysyła zapytanie do identyfikatora wybranej pozycji menu, która jest przypisywana do każdej pozycji menu w języku XML za pomocą atrybutu android:id, jak pokazano w sekcji Definiowanie menu w języku XML.

    Gdy przejdziesz do obsługi pozycji menu, zwróć true. Jeśli nie obsługujesz elementu menu, przekaż je do implementacji klasy nadrzędnej. Jeśli aktywność zawiera fragmenty, najpierw otrzymuje to wywołanie zwrotne. Wywołując klasę nadrzędną, gdy jest nieobsługiwana, system przekazuje zdarzenie do odpowiedniej metody wywołania zwrotnego w każdym fragmencie – pojedynczo, w kolejności dodania każdego fragmentu, aż do zwrócenia wartości true lub false. Domyślne implementacje Activity i android.app.Fragment zwracają wartości false, więc zawsze wywołuj klasę nadrzędną, gdy jest nieobsługiwana.

Korzystanie z trybu działań kontekstowych

Tryb działań kontekstowych to implementacja systemowa elementu ActionMode, która koncentruje się na interakcjach użytkownika z wykorzystaniem działań kontekstowych. Gdy użytkownik włączy ten tryb przez wybranie elementu, u góry ekranu wyświetli się pasek działań kontekstowych przedstawiający czynności, które może wykonać na wybranych elementach. Gdy ten tryb jest włączony, użytkownik może wybrać wiele elementów (jeśli aplikacja to obsługuje), odznaczyć elementy i kontynuować poruszanie się w obrębie aktywności. Tryb działań jest wyłączony, a pasek działań kontekstowych znika, gdy użytkownik odznaczy wszystkie elementy, kliknie przycisk Wstecz lub wybierz działanie Gotowe po lewej stronie paska.

W przypadku widoków danych z działaniami kontekstowymi wywoływany jest tryb działań kontekstowych, gdy wystąpi co najmniej jedno z tych 2 zdarzeń:

  • Użytkownik dotyka widoku i go przytrzymuje.
  • Użytkownik zaznacza pole wyboru lub podobny komponent interfejsu w widoku.

Sposób, w jaki aplikacja wywołuje tryb działań kontekstowych i określa zachowanie w przypadku każdego działania, zależy od Twojego projektu. Dostępne są 2 projekty:

  • Dla działań kontekstowych dotyczących poszczególnych, dowolnych widoków.
  • Dotyczy zbiorczych działań kontekstowych na grupach elementów w elemencie RecyclerView, umożliwiając użytkownikowi wybranie wielu elementów i wykonanie na nich wszystkich działań.

W sekcjach poniżej opisujemy konfigurację wymaganą w każdym scenariuszu.

Włączanie trybu działań kontekstowych dla poszczególnych widoków danych

Jeśli chcesz wywoływać tryb działań kontekstowych tylko wtedy, gdy użytkownik wybierze określone widoki, wykonaj te czynności:

  1. Wdróż interfejs ActionMode.Callback zgodnie z tym przykładem. W metodach wywołań zwrotnych możesz określić działania na pasku działań kontekstowych, reagować na zdarzenia kliknięć elementów działań i obsługiwać inne zdarzenia cyklu życia w trybie działania.

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

    Te wywołania zwrotne zdarzeń są prawie takie same jak wywołania zwrotne menu opcji, z tym że każde z nich przekazuje również obiekt ActionMode powiązany ze zdarzeniem. Za pomocą interfejsów API ActionMode możesz wprowadzać różne zmiany w CAB, np. poprawiać tytuł i podtytuł za pomocą setTitle() i setSubtitle(), co przydaje się do wskazywania liczby wybranych elementów.

    Poprzedni przykład ustawia zmienną actionMode na null po zniszczeniu trybu działania. W następnym kroku sprawdź, jak jest zainicjowany i jak może pomóc zapisanie zmiennej członkowskiej w aktywności lub fragmencie.

  2. Zadzwoń pod numer startActionMode(), jeśli chcesz wyświetlić pasek, np. gdy użytkownik kliknie i przytrzymaj widok.

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

    Gdy wywołasz startActionMode(), system zwróci utworzony ActionMode. Zapisując ją w zmiennej członka, możesz wprowadzać zmiany na pasku działań kontekstowych w odpowiedzi na inne zdarzenia. W poprzednim przykładzie używana jest instancja ActionMode, aby przed uruchomieniem trybu działania nie można było odtworzyć instancji ActionMode, jeśli jest już aktywna.

Utwórz wyskakujące menu

Obraz przedstawiający wyskakujące menu w aplikacji Gmail zakotwiczone obok przycisku menu w prawym górnym rogu.
Rysunek 4. Wyskakujące menu w aplikacji Gmail zakotwiczone obok przycisku menu w prawym górnym rogu.

PopupMenu to menu modalne powiązane z elementem View. Pojawia się ona pod widokiem zakotwiczenia, jeśli jest wystarczająco dużo miejsca, lub nad widokiem w innym przypadku. Jest to przydatne, gdy:

  • Udostępnienie rozszerzonego menu działań odnoszących się do określonej treści, takich jak nagłówki e-maili w Gmailu, co widać na ilustracji 4.
  • Podanie drugiej części zdania, na przykład przycisku oznaczonego Dodaj, który powoduje wyświetlenie menu z różnymi opcjami Dodaj.
  • Udostępnianie menu podobnego do Spinner, które nie zachowuje stałego wyboru.

Jeśli zdefiniujesz menu w języku XML, menu możesz wyświetlić w ten sposób:

  1. Utwórz instancję PopupMenu za pomocą swojego konstruktora, który pobiera bieżącą aplikację Context i View, w których jest zakotwiczone menu.
  2. Użyj MenuInflater, aby powiększyć zasób menu do obiektu Menu zwracanego przez PopupMenu.getMenu().
  3. Będziesz dzwonić pod numer PopupMenu.show().

Oto przykład przycisku, który wyświetla wyskakujące menu:

<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" />

Aktywność może wtedy wyświetlić menu w ten sposób:

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

Menu jest zamykane, gdy użytkownik wybierze pozycję lub kliknie poza obszarem menu. Nasłuchiwania zdarzenia odrzucenia możesz włączyć za pomocą polecenia PopupMenu.OnDismissListener.

Obsługa zdarzeń kliknięcia

Aby wykonać działanie, gdy użytkownik wybierze pozycję menu, zaimplementuj interfejs PopupMenu.OnMenuItemClickListener i zarejestruj go w PopupMenu, wywołując setOnMenuItemclickListener(). Gdy użytkownik wybierze element, system wywoła w interfejsie wywołanie zwrotne onMenuItemClick().

Widać to w tym przykładzie:

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

Utwórz grupę menu

Grupa menu to zbiór pozycji menu o określonych cechach. Dzięki grupie możesz:

  • Aby wyświetlić lub ukryć wszystkie elementy, użyj właściwości setGroupVisible().
  • Włącz lub wyłącz wszystkie elementy używające właściwości setGroupEnabled().
  • Określ, czy wszystkie elementy można sprawdzić za pomocą właściwości setGroupCheckable().

Grupę możesz utworzyć, zagnieżdżając elementy <item> wewnątrz elementu <group> w zasobie menu lub podając identyfikator grupy za pomocą metody add().

Oto przykład zasobu menu zawierającego grupę:

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

Elementy w tej grupie są wyświetlane na tym samym poziomie co pierwszy – wszystkie 3 elementy w menu są elementami równorzędnymi. Możesz jednak zmienić cechy 2 elementów w grupie, odwołując się do identyfikatora grupy i korzystając z poprzednich metod. Ponadto system nigdy nie rozdziela zgrupowanych elementów. Jeśli na przykład zadeklarujesz zasadę android:showAsAction="ifRoom" dla każdego elementu, oba te elementy pojawią się na pasku działań lub oba będą widoczne w rozszerzonym menu działań.

Używaj elementów menu, które można zaznaczyć

Rysunek 5. Menu podrzędne z elementami, które można zaznaczyć.

Menu może służyć jako interfejs do włączania i wyłączania opcji, w tym z polem wyboru dla samodzielnych opcji lub przyciskami do grupowania wzajemnie się wykluczających opcji. Rys. 5 przedstawia menu podrzędne z elementami, które można zaznaczyć za pomocą przycisków opcji.

Możesz zdefiniować możliwość sprawdzania działania poszczególnych pozycji menu za pomocą atrybutu android:checkable w elemencie <item> lub dla całej grupy – za pomocą atrybutu android:checkableBehavior w elemencie <group>. Na przykład wszystkie elementy w tej grupie menu można zaznaczyć przyciskiem:

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

Atrybut android:checkableBehavior akceptuje 1 z tych wartości:

single
Można zaznaczyć tylko jeden element z grupy, co spowoduje pojawienie się odpowiednich przycisków.
all
Zaznaczyć można wszystkie elementy, co spowoduje pojawienie się pól wyboru.
none
Brak elementów, które można zaznaczyć.

Możesz zastosować do elementu domyślny stan zaznaczonego pola za pomocą atrybutu android:checked w elemencie <item> i zmienić go w kodzie za pomocą metody setChecked().

Po wybraniu elementu, który można zaznaczyć, system wywołuje odpowiednią metodę wywołania zwrotnego wybranego elementu, np. onOptionsItemSelected(). W tym miejscu ustawia się stan pola wyboru, ponieważ pole wyboru lub opcja nie zmieniają stanu automatycznie. Możesz przesłać zapytanie dotyczące bieżącego stanu elementu (w takiej postaci, w jakiej był przed wybraniem go przez użytkownika) za pomocą funkcji isChecked(), a następnie ustawić stan zaznaczonego elementu za pomocą funkcji setChecked(). Widać to w tym przykładzie:

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

Jeśli nie ustawisz stanu zaznaczenia w ten sposób, stan widoczności pola wyboru lub opcji nie zmieni się, gdy użytkownik go wybierze. Gdy określisz stan, aktywność zachowa zaznaczony stan elementu – gdy użytkownik później otworzy menu, stan, który został przez Ciebie zaznaczony, będzie widoczny.

Dodawanie pozycji menu na podstawie intencji

Czasami chcesz, aby element menu uruchamiał działanie za pomocą funkcji Intent, niezależnie od tego, czy jest to działanie w Twojej aplikacji, czy w innej aplikacji. Jeśli znasz intencję, której chcesz użyć, i masz określoną pozycję w menu, która inicjuje intencję, możesz wykonać intencję za pomocą polecenia startActivity() za pomocą odpowiedniej metody wywołania zwrotnego wybranego elementu, np. wywołania zwrotnego onOptionsItemSelected().

Jeśli jednak nie masz pewności, że urządzenie użytkownika zawiera aplikację, która obsługuje intencję, dodanie wywołującego ją elementu menu może spowodować, że pozycja menu nie będzie działać, ponieważ intencja może nie odpowiadać działaniu. Aby rozwiązać ten problem, Android umożliwia dynamiczne dodawanie pozycji menu do menu, gdy wykryje na urządzeniu działania, które obsługują daną intencję.

Aby dodać pozycje menu na podstawie dostępnych działań, które akceptują intencję, wykonaj te czynności:

  1. Zdefiniuj intencję, używając kategorii CATEGORY_ALTERNATIVE lub CATEGORY_SELECTED_ALTERNATIVE albo obu tych opcji i uwzględniając inne wymagania.
  2. Zadzwoń pod numer Menu.addIntentOptions(). Android wyszuka wszystkie aplikacje, które mogą wykonać intencję, i dodaje je do Twojego menu.

Jeśli nie masz zainstalowanych aplikacji, które spełniają wymagania tego zamiaru, pozycje menu nie są dodawane.

Widać to w tym przykładzie:

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

Do każdego znalezionego działania, który dostarcza filtr intencji pasujący do zdefiniowanej intencji, dodawana jest pozycja menu z wartością w polu android:label filtra intencji jako tytułem menu, a ikoną aplikacji – z ikoną elementu menu. Metoda addIntentOptions() zwraca liczbę dodanych elementów menu.

Zezwalaj na dodawanie aktywności do innych menu

Możesz oferować usługi związane z Twoją aktywnością innym aplikacjom, aby można było dodać Twoją aplikację do menu innych użytkowników, odwracając role opisane wcześniej.

Aby znaleźć się w innych menu aplikacji, zdefiniuj filtr intencji w zwykły sposób, ale uwzględnij w nim wartość CATEGORY_ALTERNATIVE lub CATEGORY_SELECTED_ALTERNATIVE (albo obie te wartości) w przypadku kategorii filtra intencji. Widać to w tym przykładzie:

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

Więcej informacji o pisaniu filtrów intencji znajdziesz w artykule o filtrach intencji i intencji.