Jak stworzyć prosty rozdzielacz w nowym NavigationView?

154

Firma Google wprowadziła do NavigationViewbiblioteki Design Support Library w wersji 22.2.0, za pomocą której można bardzo łatwo utworzyć szufladę za pomocą zasobu menu.

Jak mogę utworzyć prostą linię podziału między dwoma elementami? Grupowanie elementów nie działa. Utworzenie sekcji elementów podrzędnych powoduje utworzenie linii podziału, ale wymaga tytułu, którego nie chcę.

Każda pomoc będzie mile widziana.

Longi
źródło
Jak stwierdzono w komentarzach, problem z zaakceptowaną odpowiedzią polega na tym, że sprawdzalne zachowanie nie działa w wielu (równoległych) grupach. Aby tego uniknąć, nie twórz równoległych grup, ale używaj podmenu lub podgrup, aby uzyskać dzielniki, jak opisano w mojej odpowiedzi tutaj: stackoverflow.com/questions/30766919/ ...
Do

Odpowiedzi:

319

Wszystko, co musisz zrobić, to zdefiniować groupza pomocą unikalnego identyfikatora , mam sprawdzone realizację jeśli grupa ma inny identyfikator będzie go utworzyć dzielnik.

Przykładowe menu, tworzenie separatora:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <group android:id="@+id/grp1" android:checkableBehavior="single" >
        <item
            android:id="@+id/navigation_item_1"
            android:checked="true"
            android:icon="@drawable/ic_home"
            android:title="@string/navigation_item_1" />
    </group>

    <group android:id="@+id/grp2" android:checkableBehavior="single" >
        <item
            android:id="@+id/navigation_item_2"
            android:icon="@drawable/ic_home"
            android:title="@string/navigation_item_2" />
    </group>
</menu>
NJ
źródło
9
Ciekawe, dlaczego (o ile wiem) nie jest to udokumentowane. Myślę, że będzie o to dużo pytać. Dzięki!
Gaëtan
16
Jedynym problemem związanym z tym podejściem jest to, że setCheckedwydaje się działać na poziomie grupy. Jeśli wybierzesz element z grupy A, a następnie ponownie otworzysz szufladę i wybierzesz element z grupy B, oba elementy pozostaną podświetlone. Być może Google naprawi to zachowanie w następnej wersji, ale na razie jest to wada używania wielu grup z NavigationView.
Hungryghost
3
Wspaniały. Co do podwójnie sprawdzonego, spróbuj android:checkableBehavior="none"dla swojej drugiej grupy (zakładam, że są to akcje, a nie nawigacja)
espinchi
12
Zabawne, jeśli usuniesz identyfikator z groupniego, skompiluje się i uruchomi, ale separatory są ukryte.
Vahid Amiri
5
U mnie to też nie działa. Chciałem linii między grupami menu, a nie po każdej pozycji.
Rich Morey
54

utwórz do rysowania drawer_item_bg.xml w ten sposób,

    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item>
        <shape android:shape="rectangle" >
            <solid android:color="#F4F4F4" />
        </shape>
    </item>

    <item android:top="-2dp" android:right="-2dp" android:left="-2dp">
        <shape>
            <solid android:color="@android:color/transparent" />
            <stroke
                android:width="1dp"
                android:color="#EAEAEA" />
        </shape>
        <!--
        android:dashGap="10px"
                android:dashWidth="10px"
                -->
    </item>

</layer-list>

i dodaj go do NavigationView jako aplikację: itemBackground = "@ drawable / drawer_item_bg"

<android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="#F4F4F4"
        android:layout_gravity="start"
        android:fitsSystemWindows="false"
        app:menu="@menu/activity_main_drawer"
        app:itemBackground="@drawable/drawer_item_bg"/>

i zaczynamy ...wprowadź opis obrazu tutaj

vbp
źródło
1
to działa, ale usuwa również lewe wypełnienie ikony przedmiotu. przynajmniej w moim kodzie, a nie na twoim zrzucie ekranu z jakiegoś powodu
luky
@vbp Potrzebuję górnej granicy dla pierwszego dziecka, dolnej granicy dla drugiego dziecka. Jak mogłem to osiągnąć. Jest to coś podobnego do id.first child w css
Prabs
@Prabs rozważ użycie fragmentu, widoku recyklingu lub widoków niestandardowych zamiast NavigationView w celu lepszego dostosowania
vbp
1
Zrobiłem navMenuView.addItemDecoration(new DividerItemDecoration(NavigationViewActivity.this, DividerItemDecoration.VERTICAL));dla idealnego obramowania przedmiotu
Prabs
19

Proste dodanie DeviderItemDecoration:

NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
NavigationMenuView navMenuView = (NavigationMenuView) navigationView.getChildAt(0);
navMenuView.addItemDecoration(new DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL));

Wygląda to tak:

wprowadź opis obrazu tutaj

SANAT
źródło
Czy mogę zmienić wysokość addItemDecorationlinii na 1 piksel?
Prabs
Dziękuję, że twoje rozwiązanie zadziałało dla mnie. Doceniam. Brat jeszcze jedno pytanie. Czy można usunąć górne obramowanie pierwszego elementu i ustawić obramowanie tylko na dole elementów?
viper
@viper, odnieś się do tego: bignerdranch.com/blog/…
SANAT
1
Ten rodzaj podziału dla każdej pozycji nie jest zgodny z Wytycznymi dotyczącymi materiałów
Boris Ruzanov
19

Możesz łatwo dodawać separatory, ustawiając Tło elementu menu za pomocą XML za pomocą app:itemBackground

<android.support.design.widget.NavigationView
    android:id="@id/drawer_navigation_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:menu="@menu/all_navigation_menu"
    app:itemIconTint="@color/colorPrimary"
    app:itemBackground="@drawable/bg_drawer_item"
    android:background="@color/default_background"/>

I użyj LayerDrawable jako tła. Zachowuje nakładkę marszczenia materiału projektowego dla kliknięć.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/white"/>
        </shape>
    </item>
    <item android:left="@dimen/activity_horizontal_margin">
        <shape android:shape="rectangle">
            <solid android:color="@color/divider"/>
        </shape>
    </item>
    <item android:bottom="1dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/white"/>
        </shape>
    </item>
</layer-list>

Wynik:

wprowadź opis obrazu tutaj

Stanislav Perchenko
źródło
2
wydaje się być najlepszą odpowiedzią i nie jest zaangażowana żadna grupa
Michał Ziobro
A jeśli chcesz użyć niestandardowego koloru tła dla wybranego elementu?
ataravati
Mam błąd na <shape>: Element jest tu niedozwolony
Sébastien REMY
11

Myślę, że mam jeszcze lepsze rozwiązanie problemu wielu zaznaczonych pozycji. Na mój sposób nie musisz się martwić o zmianę kodu podczas dodawania nowych sekcji do menu. Moje menu wygląda tak, jak zaakceptowane rozwiązanie napisane przez Jareda, z tą różnicą, że używa się andoid: checkableBehavior = " all " na grupach:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:id="@+id/first_section" android:checkableBehavior="all">
        <item
            android:id="@+id/frontpage"
            android:icon="@drawable/ic_action_home_24dp_light_blue"
            android:title="@string/drawer_frontpage" />
        <item
            android:id="@+id/search"
            android:icon="@drawable/ic_search"
            android:title="@string/drawer_search" />
    </group>
    <group android:id="@+id/second_section" android:checkableBehavior="all">
        <item
            android:id="@+id/events"
            android:icon="@drawable/ic_maps_local_movies_24dp_light_blue"
            android:title="@string/drawer_events" />
    </group>
</menu>

CheckableBehavior opcji „all” umożliwia dowolne zaznaczanie / odznaczanie pojedynczych elementów, niezależnie od tego, do jakiej grupy należą. Musisz tylko zapisać ostatnio zaznaczony element menu. Kod java wygląda następująco:

private MenuItem activeMenuItem;

@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
    // Update selected/deselected MenuItems
    if (activeMenuItem != null)
        activeMenuItem.setChecked(false);
    activeMenuItem = menuItem;
    menuItem.setChecked(true);

    drawerLayout.closeDrawers();
    return true;
}
splot
źródło
8

Tworzenie różnych grup, takich jak odpowiedź Nilesha, powoduje podział między nimi, ale stwarza problem dwóch zaznaczonych elementów w szufladzie nawigacji.

Prosty sposób, w jaki sobie z tym poradziłem, był następujący:

    public boolean onNavigationItemSelected(final MenuItem menuItem) {

    //if an item from extras group is clicked,refresh NAV_ITEMS_MAIN to remove previously checked item
    if (menuItem.getGroupId() == NAV_ITEMS_EXTRA) {


        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, false, true);
        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, true, true);
       }else{

        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, true, true);
        navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, false, true);


    }
    //Update highlighted item in the navigation menu
    menuItem.setChecked(true);
}
Zorawar Sachdev
źródło
Jeszcze prościej:android:checkableBehavior="none"
espinchi
2
@espinchi - To nie zadziała, jeśli inne grupy zawierają elementy menu nawigacji. To rozwiązanie może działać tylko w przypadku działań. Niezbyt dobra praktyka UX
Mushtaq Jameel
Doskonały! Może być bardziej zrozumiałe, jeśli zmienisz niezdefiniowane stałe NAV_ITEMS_MAIN i NAV_ITEMS_EXTRA za pomocą R.id.nav_items_group_main lub R.id.nav_items_group_extra i dodasz wymagany XML do swojego przykładu
ChrisPrime
IS onNavigationItemSelected (final MenuItem menuItem) {Metoda domyślna? gdzie mam zrobić ten kod?
Jan
a co to jest NAV_ITEMS_MAIN?
Jan
8

Nie jestem pewien, czy zostało to naprawione, API 26 (Android 8)czy było to możliwe przez cały czas. Nadal jestem noobem w programowaniu na Androida. Jednak dodałem grupy rodziców, jak poniżej. Testowanie na fizycznym telefonie z Androidem 6,

  1. Mam rozdzielacz
  2. Wybranie dowolnego elementu spośród nav_g1lub nav_g2spowoduje odznaczenie innych elementów.
  3. Nie dodano dodatkowego wypełnienia z powodu zagnieżdżonych grup.
  4. Nie dodałem żadnego Javakodu do słuchaczy

Kod menu

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <group
            android:id="@+id/nav_g1"
            android:checkableBehavior="single">
            <item
                android:id="@+id/nav_1"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_1"/>
            <item
                android:id="@+id/nav_2"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_2"/>
        </group>
        <group
            android:id="@+id/nav_g2"
            android:checkableBehavior="single">
            <item
                android:id="@+id/nav_3"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_3"/>
            <item
                android:id="@+id/nav_4"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_4"/>
        </group>
    </group>
    <item android:title="Actions">
        <menu>
            <item
                android:id="@+id/nav_7"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_7"/>
            <item
                android:id="@+id/nav_8"
                android:icon="@drawable/ic_menu_share"
                android:title="@string/nav_8"/>
        </menu>
    </item>
</menu>

Uwagi

  1. Jak wspomniano w tym odpowiedzi, każda grupa musi mieć unikalny identyfikator, aby pokazać podział.
  2. Zgodnie z odpowiedzią, przestrzenie pasują do specyfikacji Google Material Design.
  3. Posiadanie android:checkableBehavior="single"dla grupy rodzicielskiej jest wymagane, więc problem wielu wybranych pozycji menu w tej odpowiedzi (wspomniany w komentarzach głodnego gościa) nie występuje

A oto zrzut ekranu

Zrzut ekranu ekranu urządzenia

AaA
źródło
5

Chciałem, aby separatory znajdowały się powyżej pierwszej pozycji i po ostatniej pozycji. Korzystając z rozwiązania @Nilesh musiałem dodać atrapy pozycji menu i ustawić włączone na false, co było naprawdę brudne. A co jeśli chcę zrobić inne fajne rzeczy w moim menu?

Zauważyłem, że NavigationView rozszerza FrameLayout, dzięki czemu możesz umieścić w nim własną zawartość, tak jak w przypadku FrameLayout. Następnie ustawiam pusty plik XML menu, aby wyświetlał tylko mój niestandardowy układ. Rozumiem, że prawdopodobnie jest to sprzeczne ze ścieżką, którą Google chce, abyśmy podążali, ale jeśli chcesz mieć naprawdę niestandardowe menu, jest to łatwy sposób na zrobienie tego.

<!--Note: NavigationView extends FrameLayout so we can put whatever we want in it.-->
<!--I don't set headerLayout since we can now put that in our custom content view-->
<android.support.design.widget.NavigationView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:menu="@menu/empty_menu">

    <!--CUSTOM CONTENT-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- CUSTOM HEADER -->
        <include
            android:id="@+id/vNavigationViewHeader"
            layout="@layout/navigation_view_header"/>

        <!--CUSTOM MENU-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_below="@+id/vNavigationViewHeader">

            <View style="@style/NavigationViewLineSeperator"/>

            <Button android:text="Option 1"/>

            <View style="@style/NavigationViewLineSeperator"/>

            <Button android:text="Option 2"/>

            <View style="@style/NavigationViewLineSeperator"/>

        </LinearLayout>
    </RelativeLayout>
</android.support.design.widget.NavigationView>

Oto styl

<style name="NavigationViewLineSeperator">
        <item name="android:background">@drawable/line_seperator</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">1dp</item>
</style>

I do rysowania

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

    <solid android:color="@color/white"/>
    <size android:width="100sp"
          android:height="1sp" />
</shape>

I menu

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

edytować:

Zamiast Buttons możesz użyć TextView z drawable, który naśladuje oryginalny wygląd elementów menu:

 <TextView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:text="@string/menu_support"
     android:drawableLeft="@drawable/menu_icon_support"
     style="@style/MainMenuText" />

// style.xml
<style name="MainMenuText">
    <item name="android:drawablePadding">12dp</item>
    <item name="android:padding">10dp</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textColor">#fff</item>
</style>
Justin Fiedler
źródło
1
dzięki, najwyraźniej najwygodniejszy sposób posiadania niestandardowych widoków jako pozycji menu
Jan Rabe
używam tego rozwiązania również dlatego, że pozwala mi łatwiej ustawić niestandardową czcionkę elementów i zmienić kolor
separatora
1

Rozwiązanie powinno być zasługą Zorawar. Przepraszamy za powielenie odpowiedzi, ale nie mogłem dodać tak dużo sformatowanego tekstu w komentarzu.

Rozwiązanie Zorawar działa, kod można ulepszyć w następujący sposób:

public void onNavigationItemSelected(int groupId) {

    //if an item from extras group is clicked,refresh NAV_ITEMS_MAIN to remove previously checked item
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_MAIN, (groupId == NAV_ITEMS_MAIN), true);
    navigationView.getMenu().setGroupCheckable(NAV_ITEMS_EXTRA, (groupId == NAV_ITEMS_EXTRA), true);


    //Update highlighted item in the navigation menu
    menuItem.setChecked(true);
}
Mushtaq Jameel
źródło
co to jest NAV_ITEMS_MAIN i NAV_ITEMS_EXTRA i groupid?
John
Są to stałe dla grupy group_id
Mushtaq Jameel
0

Odpowiedź Nileha jest prawidłowa, z wyjątkiem tego, że ma jedną wadę podwójnego sprawdzenia.

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

    <group android:checkableBehavior="single" android:id="@+id/grp1">
        <item
            android:id="@+id/nav_register_customer"
            android:icon="@drawable/ic_menu_camera"
            android:checkableBehavior="none"
            android:title="Register Customer" />
    </group>
    <group  android:id="@+id/grp2"
        android:checkableBehavior="single"  >
        <item
            android:id="@+id/nav_slideshow"
            android:icon="@drawable/ic_menu_slideshow"
            android:checkableBehavior="none"
            android:title="Slideshow" />
    </group>
</menu>

Więc używając

android: checkableBehavior = "pojedynczy"

z unikalnym identyfikatorem rozwiąże problem

Ajji
źródło
0

Jeśli chcesz dynamicznie dodać dzielnik , możesz po prostu dodać puste podmenu w następujący sposób:

Menu menu = navigationView.getMenu();
menu.addSubMenu(" ").add(" ");

doda to rozdzielacz.

po prostu pamiętaj, aby przekazać właściwy indeks podczas używania menu.getItem(int index)

Moh Mah
źródło
0

Oprócz poprzednich odpowiedzi, jeśli chcesz używać separatorów dekoracyjnych, ale nie chcesz tworzyć wielu grup z powodu problemów z „sprawdzalnym zachowaniem” - możesz przypisać ten sam identyfikator grupy do wszystkich swoich grup.

Przykład:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
    android:id="@+id/group_nav_common" <!-- New group id-->
    android:checkableBehavior="single">

    <item
        android:id="@+id/menu_item_nav_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/nav_menu_main" />
    <item
        android:id="@+id/menu_item_nav_articles"
        android:icon="@drawable/ic_art_track_black_24dp"
        android:title="@string/latest_news" />

    <item
        android:id="@+id/menu_item_nav_group_categories"
        android:title="@string/nav_menu_sections">
        <menu>
            <group
                android:id="@id/group_nav_common" <!-- Existing group id -->
                android:checkableBehavior="single">
                <!-- Programmatically populated section -->
            </group>
        </menu>
    </item>

    <item
        android:id="@+id/menu_item_nav_group_sites"
        android:title="@string/nav_menu_sites">
        <menu>
            <group
                android:id="@id/group_nav_common" <!-- Existing group id -->
                android:checkableBehavior="single">
                <item
                    android:id="@+id/menu_item_nav_select_site"
                    android:icon="@drawable/ic_account_balance_black_24dp"
                    android:title="@string/nav_menu_select_site" />
            </group>
        </menu>
    </item>
</group>

ievgen
źródło
0

Jeśli wybrana odpowiedź nie działa dla Ciebie, możesz spróbować dodać ją do onCreateOptionsMenuoprócz podania unikalnego identyfikatora grupy:

if (Build.VERSION.SDK_INT >= 28) {
    menu.setGroupDividerEnabled(true)
}
user806696
źródło