Jak wymusić użycie rozszerzonego menu na urządzeniach z przyciskiem menu

159

Chciałbym, aby wszystkie elementy menu, które nie pasują do paska akcji, przechodziły do ​​menu przepełnienia (tego, do którego można dotrzeć z paska akcji, a nie przycisku menu), nawet na urządzeniach, które mają przycisk menu . Wydaje się to o wiele bardziej intuicyjne dla użytkowników niż wrzucenie ich do oddzielnej listy menu, która wymaga od użytkownika przejścia z interakcji dotykowej (ekranowej) do interakcji opartej na przyciskach, ponieważ układ ActionBar nie może zmieścić ich na pasku.

Na emulatorze mogę ustawić wartość „Hardware Back / Home Keys” na „no” i uzyskać ten efekt. Szukałem sposobu, aby to zrobić w kodzie dla rzeczywistego urządzenia, które ma przycisk menu, ale nie może go dobrze. Czy ktoś może mi pomóc?

Paul P.
źródło

Odpowiedzi:

54

EDYCJA: Zmodyfikowano, aby odpowiadać za sytuację fizycznego przycisku menu.

Zapobiega temu projekt. Zgodnie z sekcją Zgodność Android Design Guide ,

„... przepełnienie akcji jest dostępne za pomocą klawisza sprzętowego menu. Wyskakujące okienko akcji ... jest wyświetlane u dołu ekranu.”

Zauważysz na zrzutach ekranu, telefony z fizycznym przyciskiem menu nie mają przepełnionego menu na pasku akcji. Pozwala to uniknąć niejednoznaczności dla użytkownika, zasadniczo mając dostępne dwa przyciski do otwierania dokładnie tego samego menu.

Aby rozwiązać problem spójności między urządzeniami: Ostatecznie dla wygody użytkownika ważniejsze jest, aby Twoja aplikacja zachowywała się spójnie z każdą inną aplikacją na tym samym urządzeniu, niż aby działała spójnie ze sobą na wszystkich urządzeniach.

Alexander Lucas
źródło
1
Alexander - Nie, wypróbowałem wszystkie wartości showAsAction i kilka kombinacji i żadna z nich nie załatwia sprawy (przynajmniej na emulatorze). Rozszerzone menu na pasku akcji pojawia się tylko wtedy, gdy emuluję urządzenie bez przycisku menu. Chcę zobaczyć tę pionową wielokropek i wyświetlać przepełnione elementy na urządzeniach z przyciskiem menu. Zredagowałem moje pytanie, aby było trochę jaśniejsze.
PaulP
41
Przejdźmy do tej rozmowy i przedyskutujmy: wiem, że zapobiega temu projekt (przeczytałem wytyczne projektowe). Ale to chyba% $ /% # +. Na przykład: użytkownik przełącza się z Galaxy Nexus (-> w Overflow) na Nexus One (w 4.0 / -> no Overflow). Założę się, że użytkownik nie znajdzie już pozycji menu. Z tego powodu chcę tego samego zastosowania dla wszystkich urządzeń. Więc kończę z tym samym problemem co Paulp. Czy nie ma żadnego czystego obejścia?
Sprigg
10
Ja też najpierw przeczytałem przewodnik projektowy. Dla mnie był to zły wybór projektu w pakiecie wsparcia. Lepszą strategią przeniesienia użytkowników Z DALA od przycisku (cel, prawda?) Byłoby uczynienie go zbędnym, umieszczenie funkcji zarówno na ekranie, jak i w przycisku. Tak jak teraz, przycisk nie wyświetla wszystkich opcji menu, tylko te, których nie ma na pasku akcji, więc ten projekt ani nie obsługuje płynnego przejścia do paska akcji, ani nie robi tego, co robił przed dodaniem paska akcji (wyświetla całe menu wybory). Podejrzewam, że masz rację i nie ma prostego obejścia.
PaulP
18
Google sprzeciwia się temu w swoich nowych aplikacjach Google+. Ma przepełnienie niezależnie od urządzenia ... ale nadal, o ile wiem, nadal uniemożliwiają programistom zrobienie tego samego i radzą przeciwko temu.
Karl
10
Szczerze mówiąc, widziałem zbyt wielu użytkowników, którzy nie wypróbowywali twardego klawisza menu, aby kiedykolwiek się tego spodziewać. Jeśli projekt mówi, że żaden użytkownik dowolnego telefonu nie powinien mieć wskaźnika na ekranie, że istnieją opcje menu, ten projekt jest słabo poinformowany.
Lance Nanek
323

Możesz również użyć tego małego hacka tutaj:

try {
    ViewConfiguration config = ViewConfiguration.get(this);
    Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
    if (menuKeyField != null) {
        menuKeyField.setAccessible(true);
        menuKeyField.setBoolean(config, false);
    }
} catch (Exception ignored) {
}

Dobrym miejscem na umieszczenie tego byłoby onCreate-Method twojej klasy Application.

Zmusi to aplikację do wyświetlenia menu przepełnienia. Przycisk menu będzie nadal działał, ale otworzy menu w prawym górnym rogu.

[Edytuj] Ponieważ pojawił się kilka razy: ten hack działa tylko dla natywnego ActionBar wprowadzonego w Androidzie 3.0, a nie ActionBarSherlock. Ten ostatni używa własnej wewnętrznej logiki, aby zdecydować, czy wyświetlić rozszerzone menu. Jeśli używasz ABS, wszystkie platformy <4.0 są obsługiwane przez ABS i dlatego podlegają jego logice. Hack będzie nadal działać na wszystkich urządzeniach z Androidem 4.0 lub nowszym (możesz bezpiecznie zignorować Androida 3.x, ponieważ tak naprawdę nie ma tam żadnych tabletów z przyciskiem menu).

Istnieje specjalny motyw ForceOverflow, który wymusi menu w ABS, ale z pewnością zostanie usunięty w przyszłych wersjach z powodu komplikacji .

Timo Ohr
źródło
4
Przejrzałem kod źródłowy. Gaj skarbów nieudokumentowanych funkcji :) Po prostu upewnij się, że masz działające rozwiązanie awaryjne na takie rzeczy.
Timo Ohr
4
Wielkie dzięki! To jest naprawdę świetne. Chciałem umieścić recenzję, zgłosić błąd i udostępnić akcje w przepełnieniu, ale nie było to wyświetlane na Nexusie S. Użytkownicy nawet nie klikają przycisku Menu. Dzięki przepełnieniu użytkownicy widzą, że dostępne są dodatkowe akcje.
Yuriy Kulikov
4
@Ewoks Spóźniłem się na komentarz tutaj, ale właśnie wypróbowałem to z najnowszą wersją ActionBarCompat i DZIAŁAło.
jacobhyphenated
3
Działa to na Samsungu Galaxy S3 i S4, ale nie na LG G2 (podejrzewamy, że na sztywno kodują czek, aby wyświetlić przycisk menu przepełnienia)
DVD
18
Piękny! Jeśli Eclipse daje ci 6 różnych importów do wyboru, Fieldwybierz ten java.lang.reflect.Field;)
Eugene van der Merwe
35

Używam, aby to obejść, definiując moje menu w następujący sposób (również z ikoną ActionBarSherlock używaną w moim przykładzie):

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/abs__ic_menu_moreoverflow_normal_holo_light"
        android:orderInCategory="11111"
        android:showAsAction="always">
        <menu>
            <item
                android:id="@+id/menu_overflow_item1"
                android:showAsAction="never"
                android:title="@string/overflow_item1_title"/>
            <item
                android:id="@+id/menu_overflow_item2"
                android:showAsAction="never"
                android:title="@string/overflow_item2_title"/>
        </menu>
    </item>

</menu>

Przyznaję, że może to wymagać ręcznego „zarządzania przepełnieniem” w Twoim xml, ale uznałem to rozwiązanie za przydatne.

Możesz także wymusić na urządzeniu użycie przycisku HW w celu otwarcia rozszerzonego menu w Twojej aktywności:

private Menu mainMenu;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // TODO: init menu here...
    // then:
    mainMenu=menu;
    return true;
}

@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            if (mainMenu !=null) {
                mainMenu.performIdentifierAction(R.id.menu_overflow, 0);
            }
    }

    return super.onKeyUp(keycode, e);
}

:-)

Berťák
źródło
2
jak używać przycisku HW, aby otworzyć przepełnione menu?
bladefury
1
Zobacz moją zaktualizowaną odpowiedź dotyczącą otwierania przepełnionego menu przyciskiem HW. :)
Berťák
11

Jeśli używasz paska akcji z biblioteki obsługi ( android.support.v7.app.ActionBar), użyj następującego:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:yorapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/icon"
        yourapp:showAsAction="always"
        android:title="">
        <menu>
            <item
                android:id="@+id/item1"
                android:title="item1"/>
            <item
                android:id="@+id/item2"
                android:title="item2"/>
        </menu>
    </item>

</menu>
uczciwy gracz
źródło
Korzystając z biblioteki wsparcia, zaimplementowałem to również i działa dobrze zarówno na urządzeniach przed, jak i po 3.0
leafcutter
7

Tej metodzie zapobiega system Android Developers Design System, ale znalazłem sposób, aby to przekazać:

Dodaj to do pliku menu XML:

<item android:id="@+id/pick_action_provider"
    android:showAsAction="always"
    android:title="More"
    android:icon="@drawable/ic_action_overflow"
    android:actionProviderClass="com.example.AppPickActionProvider" />

Następnie utwórz klasę o nazwie „AppPickActionProvider” i skopiuj do niej następujący kod:

    package com.example;

import android.content.Context;
import android.util.Log;
import android.view.ActionProvider;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.SubMenu;
import android.view.View;

public class AppPickActionProvider extends ActionProvider implements
        OnMenuItemClickListener {

    static final int LIST_LENGTH = 3;

    Context mContext;

    public AppPickActionProvider(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        Log.d(this.getClass().getSimpleName(), "onCreateActionView");

        return null;
    }

    @Override
    public boolean onPerformDefaultAction() {
        Log.d(this.getClass().getSimpleName(), "onPerformDefaultAction");

        return super.onPerformDefaultAction();
    }

    @Override
    public boolean hasSubMenu() {
        Log.d(this.getClass().getSimpleName(), "hasSubMenu");

        return true;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        Log.d(this.getClass().getSimpleName(), "onPrepareSubMenu");

        subMenu.clear();

        subMenu.add(0, 1, 1, "Item1")
        .setIcon(R.drawable.ic_action_home).setOnMenuItemClickListener(this);

        subMenu.add(0, 2, 1, "Item2")
            .setIcon(R.drawable.ic_action_downloads).setOnMenuItemClickListener(this);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch(item.getItemId())
        {
            case 1:

                // What will happen when the user presses the first menu item ( 'Item1' )

                break;
            case 2:

                // What will happen when the user presses the second menu item ( 'Item2' )

                break;

        }

        return true;
    }
}
Eli Revah
źródło
jaka jest różnica między użyciem tej metody a zwykłym użyciem podmenu, jak pokazano tutaj: stackoverflow.com/a/14634780/878126 ?
programista Androida
5

Cóż, myślę, że Alexander Lucas udzielił (niestety) poprawnej odpowiedzi, więc zaznaczam ją jako „właściwą”. Alternatywną odpowiedzią, którą tutaj dodam, jest po prostu wskazanie nowym czytelnikom tego posta na blogu Android Developers jako dość wyczerpujące omówienie tego tematu z kilkoma konkretnymi sugestiami, jak radzić sobie z kodem podczas przechodzenia z poziomu wyższego niż 11 do nowego paska akcji.

Nadal uważam, że był to błąd projektowy, ponieważ przycisk menu nie zachowywał się jak nadmiarowy przycisk „Przepełnienie akcji” w urządzeniach z włączonym przyciskiem menu jako lepszy sposób na przeniesienie doświadczenia użytkownika, ale w tym momencie woda pod mostem.

Paul P.
źródło
Całkowicie zgadzam się z tobą co do błędu projektowego - i to frustrujące!
Paul Hunnisett
1
Zgodziłbym się z tobą w tej sprawie, gdyby nie fakt, że samo Google zaczął naruszać tę zasadę w swojej ostatniej aplikacji G +. I słusznie. Przycisk menu nie jest przestarzały, mają go nawet nowe urządzenia, takie jak SGS3. Niestety, zostanie tutaj. A to poważnie utrudnia użyteczność.
Timo Ohr
4

Nie jestem pewien, czy tego właśnie szukasz, ale zbudowałem podmenu w menu ActionBar i ustawiłem jego ikonę tak, aby pasowała do ikony menu przepełnienia. Chociaż nie będzie miał automatycznie wysyłanych do niego elementów (tj. Musisz wybrać to, co jest zawsze widoczne, a co zawsze przepełnione), wydaje mi się, że takie podejście może ci pomóc.

Chris
źródło
2

W aplikacji Gmail, która jest dostarczana z preinstalowaną usługą ICS, przycisk menu jest wyłączony, gdy zaznaczonych jest wiele elementów. Menu przepełnienia jest tutaj „wymuszone” do wywołania przez użycie przycisku przepełnienia zamiast fizycznego przycisku menu. Istnieje biblioteka innej firmy o nazwie ActionBarSherlock, która pozwala „wymusić” menu przepełnienia. Ale to będzie działać tylko na poziomie API 14 lub niższym (przed ICS)

borislemke
źródło
2

Jeśli używasz paska narzędzi Toolbar , możesz wyświetlić przepełnienie na wszystkich wersjach i wszystkich urządzeniach, próbowałem na niektórych urządzeniach 2.x, działa.

TeeTracker
źródło
1

Przepraszamy, jeśli ten problem jest martwy.

Oto, co zrobiłem, aby rozwiązać błąd. Poszedłem do układów i stworzyłem dwa zawierające paski narzędzi. Jeden był układem dla sdk w wersji 8, a drugi dla sdk w wersji 21. W wersji 8 użyłem android.support.v7.widget.Toolbar, podczas gdy android.widget.Toolbar w układzie sdk 21.

Następnie nadmuchuję pasek narzędzi w mojej aktywności. Sprawdzam SDK, aby zobaczyć, czy było to 21 lub więcej. Następnie nadymam odpowiedni układ. Zmusza to przycisk sprzętowy do mapowania na faktycznie zaprojektowany pasek narzędzi.

Dolandlod
źródło
0

Dla każdego, kto używa nowego Toolbar:

private Toolbar mToolbar;

@Override
protected void onCreate(Bundle bundle) {
    super.onCreate(bundle);

    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);

    ...
}


@Override
public boolean onKeyUp(int keycode, KeyEvent e) {
    switch(keycode) {
        case KeyEvent.KEYCODE_MENU:
            mToolbar.showOverflowMenu();
            return true;
        }

    return super.onKeyUp(keycode, e);
}
Maksim Ivanov
źródło