Android: Jak włączyć / wyłączyć element menu opcji po kliknięciu przycisku?

127

Mogę to łatwo zrobić, używając metod onCreateOptionsMenulub onOptionsItemSelected.

Ale mam przycisk gdzieś na ekranie i po kliknięciu tego przycisku powinien on włączać / wyłączać elementy menu kontekstowego.

Vikas
źródło

Odpowiedzi:

272

W każdym razie dokumentacja obejmuje wszystkie rzeczy.

Zmiana pozycji menu w czasie wykonywania

Po utworzeniu działania onCreateOptionsMenu()metoda jest wywoływana tylko raz, jak opisano powyżej. System zachowuje i ponownie wykorzystuje Menuzdefiniowane w tej metodzie wartości, dopóki Twoja aktywność nie zostanie zniszczona. Jeśli chcesz zmienić menu opcji w dowolnym momencie po jego pierwszym utworzeniu, musisz nadpisać onPrepareOptionsMenu()metodę. Spowoduje to przekazanie obiektu Menu w takiej postaci, w jakiej obecnie istnieje. Jest to przydatne, jeśli chcesz usunąć, dodać, wyłączyć lub włączyć elementy menu w zależności od bieżącego stanu aplikacji.

Na przykład

@Override
public boolean onPrepareOptionsMenu (Menu menu) {
    if (isFinalized) {
        menu.getItem(1).setEnabled(false);
        // You can also use something like:
        // menu.findItem(R.id.example_foobar).setEnabled(false);
    }
    return true;
}

W systemie Android 3.0 i nowszych menu opcji jest uważane za zawsze otwarte, gdy elementy menu są prezentowane na pasku akcji. Gdy wystąpi zdarzenie i chcesz wykonać aktualizację menu, musisz zadzwonić, invalidateOptionsMenu()aby zażądać wywołania systemowego onPrepareOptionsMenu().

Vikas
źródło
2
setEnable()zmienia to, co się dzieje po naciśnięciu tego menu, ale nie zmienia jego wyglądu (co jest nie tak, programiści Androida?). Dlatego łatwiej jest wyłączyć i zmienić tytuł, a najlepiej po prostu uczynić go MenuItemniewidzialnym.
Vlad
19
Szybka wskazówka: wróć, falseaby całkowicie wyłączyć menu.
Bart Friederichs,
5
Dodatkowo komentarz API dotyczący onPrepareOptionsMenu wyraźnie stwierdza: Klasy pochodne powinny zawsze (!) Wywoływać do implementacji klasy bazowej. Zapomniałeś tam o swoim super powołaniu.
AgentKnopf,
Skąd znasz identyfikator całkowity elementu MenuItem dodany w czasie wykonywania? Możesz dodać je tylko po imieniu.
Antonio Sesto
66

We wszystkich wersjach Androida najprostszy sposób: użyj tego, aby POKAZYĆ ikonę akcji menu jako wyłączoną ORAZ sprawić, by działała również jako wyłączona:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {

    MenuItem item = menu.findItem(R.id.menu_my_item);

    if (myItemShouldBeEnabled) {
        item.setEnabled(true);
        item.getIcon().setAlpha(255);
    } else {
        // disabled
        item.setEnabled(false);
        item.getIcon().setAlpha(130);
    }
}
Szczery
źródło
1
Po co ustawiać alfa na 0, skoro można ustawić widoczność na fałsz?
MLProgrammer-CiM
8
Nie ustawiam alfa na 0, ale na 130.
Frank
8
Zależy od kontekstu przycisku. Jeśli funkcja, którą normalnie służyłby przycisk, jest całkowicie bezużyteczna w obecnym stanie, to tak, widoczność powinna być niewidoczna / zniknąć. Jeśli jednak funkcja, którą normalnie służyłby przycisk, MOGŁAby być użyteczna z jakichkolwiek powodów (np. Jeśli użytkownik zostanie członkiem premium, zaloguje się, łączy się z Internetem itp.), To dobrze jest mieć ją wyszarzoną ponieważ wtedy pozwala użytkownikowi wiedzieć, że istnieje jakaś funkcja, do której z jakiegoś powodu nie ma dostępu.
yiati
6
Ustawienie ikony na niewidoczną nie jest lepsze w prawie wszystkich przypadkach użycia. W przypadku, gdy masz grupę, lepiej jest, aby pozostawały na swoich pozycjach, a wyszarzone wskazuje użytkownikowi, że istnieje opcja, którą można włączyć w określonym stanie programu. uczynienie czegoś niewidzialnym i niewidzialnym oraz prawdopodobnie spowodowanie zmian w układzie jest bardzo głupie. Istnieje powód, dla którego większość menu wyszarza elementy i nie usuwa ich w zależności od trybu.
RichieHH
1
A co jeśli menu nie zawiera ikony? Nie możesz ustawić Alfy ().
Latief Anwar
42

Możesz zapisać element jako zmienną podczas tworzenia menu opcji, a następnie dowolnie zmieniać jego właściwości.

private MenuItem securedConnection;
private MenuItem insecuredConnection;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.connect_menu, menu);
    securedConnection = menu.getItem(0);
    insecuredConnection =  menu.getItem(1);
    return true;
}

public void foo(){
       securedConnection.setEnabled(true);
}    
nir
źródło
To jest poprawna odpowiedź na każdą wersję Androida. Zweryfikowana odpowiedź jest ważna tylko dla urządzeń API 11>.
Marco HC
2
Próbowałem tego i to nie działa. Czy miałeś na myśli onPrepareOptionsMenu?
Christopher Pickslay
6

uprościć wersję @Vikas

@Override
public boolean onPrepareOptionsMenu (Menu menu) {

    menu.findItem(R.id.example_foobar).setEnabled(isFinalized);
    return true;
}
Kosrat D. Ahmad
źródło
4

Jak zaktualizować bieżące menu, aby włączyć lub wyłączyć elementy po zakończeniu AsyncTask.

W moim przypadku musiałem wyłączyć moje menu, gdy mój AsyncTask ładował dane, a następnie po załadowaniu wszystkich danych musiałem ponownie włączyć całe menu, aby użytkownik mógł z niego korzystać.

Uniemożliwiło to aplikacji umożliwienie użytkownikom klikania elementów menu podczas ładowania danych.

Najpierw deklaruję zmienną stanu, jeśli zmienna ma wartość 0, menu jest wyświetlane, jeśli ta zmienna ma wartość 1, menu jest ukryte.

private mMenuState = 1; //I initialize it on 1 since I need all elements to be hidden when my activity starts loading.

Następnie w moim onCreateOptionsMenu()sprawdzam tę zmienną, jeśli jest 1, wyłączam wszystkie moje elementy, jeśli nie, po prostu je wszystkie pokazuję

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_galeria_pictos, menu);

        if(mMenuState==1){
            for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(false);
            }
        }else{
             for (int i = 0; i < menu.size(); i++) {
                menu.getItem(i).setVisible(true);
            }
        }

        return super.onCreateOptionsMenu(menu);
    }

Teraz, gdy rozpocznie się moja aktywność, onCreateOptionsMenu()zostanie wywołana tylko raz, a wszystkie moje elementy znikną, ponieważ ustawiłem dla nich stan na początku.

Następnie tworzę AsyncTask, w którym ustawiam tę zmienną stanu na 0 w pliku onPostExecute()

Ten krok jest bardzo ważny!

Kiedy zadzwonisz invalidateOptionsMenu();, uruchomi się ponownieonCreateOptionsMenu();

Tak więc, po ustawieniu mojego stanu na 0, po prostu przerysowuję całe menu, ale tym razem z moją zmienną na 0, co oznacza, że ​​całe menu zostanie wyświetlone po zakończeniu całego procesu asynchronicznego, a następnie mój użytkownik może korzystać z menu .

 public class LoadMyGroups extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mMenuState = 1; //you can set here the state of the menu too if you dont want to initialize it at global declaration. 

        }

        @Override
        protected Void doInBackground(Void... voids) {
           //Background work

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            mMenuState=0; //We change the state and relaunch onCreateOptionsMenu
            invalidateOptionsMenu(); //Relaunch onCreateOptionsMenu

        }
    }

Wyniki

wprowadź opis obrazu tutaj

Gastón Saillén
źródło
2

najlepsze rozwiązanie, gdy grasz w szufladzie nawigacji

@Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.setGroupVisible(0,false);
        return true;
    }
Prashant Gosai
źródło
To ukrywa całe menu i uniemożliwia jego ponowne
otwarcie
2

Bardziej nowoczesna odpowiedź na stare pytanie:

MainActivity.kt

private var myMenuIconEnabled by Delegates.observable(true) { _, old, new ->
    if (new != old) invalidateOptionsMenu()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    findViewById<Button>(R.id.my_button).setOnClickListener { myMenuIconEnabled = false }
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.menu_main_activity, menu)
    return super.onCreateOptionsMenu(menu)
}

override fun onPrepareOptionsMenu(menu: Menu): Boolean {
    menu.findItem(R.id.action_my_action).isEnabled = myMenuIconEnabled
    return super.onPrepareOptionsMenu(menu)
}

menu_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
    android:id="@+id/action_my_action"
    android:icon="@drawable/ic_my_icon_24dp"
    app:iconTint="@drawable/menu_item_icon_selector"
    android:title="My title"
    app:showAsAction="always" />
</menu>

menu_item_icon_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?enabledMenuIconColor" android:state_enabled="true" />
<item android:color="?disabledMenuIconColor" />

attrs.xml

<resources>   
    <attr name="enabledMenuIconColor" format="reference|color"/>
    <attr name="disabledMenuIconColor" format="reference|color"/>
</resources>

styles.xml or themes.xml

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="disabledMenuIconColor">@color/white_30_alpha</item>
    <item name="enabledMenuIconColor">@android:color/white</item>
DrogieBrzuch
źródło
Świetne podejście, którego można używać bez wielu schematów w całej aplikacji.
Arthur
Jaki masz problem?
ExpensiveBelly
@ExpensiveBelly nie jest problemem per se, tylko publiczne ogłoszenie.
Big McLargeHuge
1
  @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            switch (item.getItemId()) {

                case R.id.item_id:

                       //Your Code....

                        item.setEnabled(false);
                        break;
              }
            return super.onOptionsItemSelected(item);
     }
Tousif Akram
źródło
Z recenzji: Cześć, nie odpowiadaj tylko za pomocą kodu źródłowego. Postaraj się przedstawić miły opis tego, jak działa Twoje rozwiązanie. Zobacz: Jak napisać dobrą odpowiedź? . Dzięki
sɐunıɔ ןɐ qɐp
1

Zapisałem odniesienie do menu pod adresem onCreateOptionsMenu. Jest to podobne do odpowiedzi nir, z tym że zamiast zapisywać każdą pojedynczą pozycję, zapisałem całe menu.

Zadeklaruj menu Menu toolbarMenu;.

Następnie onCreateOptionsMenuzapisz menu do swojej zmiennej

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.main_menu, menu);
    toolbarMenu = menu;
    return true;
}

Teraz możesz uzyskać dostęp do swojego menu i wszystkich jego pozycji w dowolnym momencie. toolbarMenu.getItem(0).setEnabled(false);

Smitty-Werben-Jager-Manjenson
źródło
0
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    // getMenuInflater().inflate(R.menu.home, menu);
    return false;
}
Draken
źródło
2
Chociaż ten kod może odpowiedzieć na pytanie OP, kilka słów wyjaśnienia pomoże obecnym i przyszłym użytkownikom jeszcze lepiej zrozumieć Twoją odpowiedź.
Thom
0

Jeśli widoczne menu

menu.findItem(R.id.id_name).setVisible(true);

Jeśli ukryj menu

menu.findItem(R.id.id_name).setVisible(false);
murugan mani
źródło
-4

Ogólnie można zmienić właściwości widoków w czasie wykonywania:

(Button) item = (Button) findViewById(R.id.idBut);

i wtedy...

item.setVisibility(false)

ale

jeśli chcesz zmodyfikować widoczność opcji z ContextMenu, po naciśnięciu przycisku możesz aktywować flagę, a następnie w onCreateContextMenu możesz zrobić coś takiego:

 @Override
        public void onCreateContextMenu(ContextMenu menu, 
                View v,ContextMenu.ContextMenuInfo menuInfo) {

            super.onCreateContextMenu(menu, v, menuInfo);

                menu.setHeaderTitle(R.string.context_title);

                if (flagIsOn()) {
                    addMenuItem(menu, "Option available", true);
                } else {
                    Toast.makeText(this, "Option not available", 500).show();
                }

        }

mam nadzieję, że to pomoże

Eric
źródło
Muszę powiedzieć, że się mylisz, chcę, aby pozycja menu była wyłączona, a nie przycisk.
Vikas
moja odpowiedź jest zakończona. Ten kod działa, użyłem w swoich projektach
Eric
1
Dziękuję za pracę, ale należy dobrze przeczytać pytanie, które już powiedziałem, że mogę to zmienić w onCreateContextMenumetodzie. Ale chcę uzyskać dostęp do menu kontekstowego poza tą metodą.
Vikas
onCreateContextMenuzostanie wywołana tylko raz, ale mogę klikać przycisk dużo czasu, aby włączyć / wyłączyć element menu.
Vikas
Tak, ale menu kontekstowe zwykle jest ukryte. Jeśli naciśniesz przycisk `` gdzieś '' i ustawisz flagę, jak powiedziałem, to menu kontekstowe nie jest widoczne, a następnym razem, gdy ponownie załadujesz menu kontekstowe, ta opcja będzie niewidoczna. Inną opcją jest utworzenie innego rodzaju menu o takim samym wyglądzie, aby obsłużyć zdarzenia innymi metodami.
Eric