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.


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>

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


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


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


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

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

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


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>

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

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


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)


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>


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)
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.