web-dev-qa-db-fra.com

Conception matérielle: Largeur du tiroir de navigation

Selon les spécifications de conception matérielle la largeur du tiroir de navigation sur les appareils mobiles doit être

largeur de navigation latérale = largeur de l'écran - hauteur de la barre d'application

Comment implémentons-nous cela sur Android?

J'ai deux solutions partielles. La première est la manière hacky: dans l'activité contenant je mets ce code:

 if (Build.VERSION.SDK_INT> Build.VERSION_CODES.HONEYCOMB_MR1) {
 final Display display = getWindowManager (). getDefaultDisplay (); 
 final Point size = new Point () ; 
 display.getSize (taille); 
 
 final ViewGroup.LayoutParams params = mDrawerFragment.getView (). getLayoutParams (); 
 params.width = size. x - getResources (). getDimensionPixelSize (
 R.dimen.abc_action_bar_default_height_material 
); 
 mFragmentUserList.getView (). setLayoutParams (params); 
}

Cependant, cela provoque un deuxième cycle de mise en page et ne fonctionne pas dans Gingerbread: ce n'est pas optimal.

La deuxième solution consiste à ajouter un espace entre le fragment et le tiroir Layout. Cependant, il déplace l'ombre et l'endroit où l'utilisateur peut appuyer pour revenir à l'application principale. Il se bloque également lorsque vous appuyez sur l'icône "hamburguer". Pas optimal non plus.

Existe-t-il une meilleure solution, de préférence une qui implique des styles et du xml?

35
zubietaroberto

Avec la Android Design Support Library il est maintenant très simple d'implémenter un tiroir de navigation incluant un dimensionnement correct. Utilisez NavigationView et utilisez sa capacité à créer un tiroir à partir des ressources du menu (exemple ici ) ou vous pouvez simplement l'enrouler autour de la vue que vous utilisez actuellement pour montrer votre tiroir (par exemple ListView, RecyclerView). NavigationView se charge ensuite du dimensionnement des tiroirs pour vous.

Voici un exemple d'utilisation du NavigationView enroulé autour de ListView:

<?xml version="1.0" encoding="utf-8"?>
<Android.support.v4.widget.DrawerLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:id="@+id/navdrawer_layout"
    Android:fitsSystemWindows="true">

    <!-- Layout where content is shown -->
    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

        <include Android:id="@+id/toolbar"
            layout="@layout/toolbar" />

        <FrameLayout
            Android:id="@+id/content_frame"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:layout_below="@id/toolbar" />

        <!-- Toolbar shadow for pre-Lollipop -->
        <View style="@style/ToolbarDropshadow"
            Android:layout_width="match_parent"
            Android:layout_height="3dp"
            Android:layout_below="@id/toolbar" />

    </RelativeLayout>

    <Android.support.design.widget.NavigationView
        Android:id="@+id/navigation_view"
        Android:layout_width="wrap_content"
        Android:layout_height="match_parent"
        Android:layout_gravity="start">

        <ListView
            Android:id="@+id/navdrawer_list"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:choiceMode="singleChoice"
            Android:dividerHeight="0dp"
            Android:divider="@null"/>

    </Android.support.design.widget.NavigationView>

</Android.support.v4.widget.DrawerLayout>

De cette façon, vous pouvez utiliser le dimensionnement de NavigationViews tout en utilisant votre propre liste de tiroirs. Bien qu'il soit beaucoup plus facile de faire de la liste des tiroirs des ressources de menu (exemple ici ), vous ne pouvez pas utiliser des vues personnalisées pour les éléments de la liste.

28
Logan

J'ai réussi à faire une solution en utilisant des déclarations de style XML mais c'est aussi un peu hacky. Mon approche était d'utiliser des marges au lieu d'appliquer une largeur définie pour éviter d'écrire un code pour calculer la mise en page manuellement. J'ai créé une règle de style de base pour souligner comment faire fonctionner cela.

Malheureusement, DrawerLayout applique actuellement une marge minimale de 64dp. Pour que cette approche fonctionne, nous devons compenser cette valeur avec des marges négatives afin d'obtenir la largeur souhaitée pour le tiroir de navigation. J'espère que cela pourra être résolu à l'avenir ( Quelqu'un a déposé un problème à ce sujet ) afin que nous puissions simplement référencer le abc_action_bar_default_height_material référence de dimension pour la marge.

Suivez ces étapes:

  1. Ajoutez les définitions de dimension et de style suivantes:

    values ​​/ dimens.xml

    <!-- Match 56dp default ActionBar height on portrait orientation -->
    <dimen name="nav_drawer_margin_offset">-8dp</dimen>
    

    values-land/dimens.xml

    <!-- Match 48dp default ActionBar height on landscape orientation -->
    <dimen name="nav_drawer_margin_offset">-16dp</dimen>
    

    values ​​/ styles.xml

    <!-- Nav drawer style to set width specified by Material Design specification -->
    <style name="NavDrawer">
        <item name="Android:layout_width">match_parent</item>
        <item name="Android:layout_marginRight">@dimen/nav_drawer_margin_offset</item>
    </style>
    

    values-sw600dp/styles.xml

    <!-- Margin already matches ActionBar height on tablets, just modify width -->
    <style name="NavDrawer">
        <item name="Android:layout_width">320dp</item>
        <item name="Android:layout_marginRight">0dp</item>
    </style>
    
  2. Une fois que vous avez ajouté les règles ci-dessus dans votre projet, vous pouvez référencer le style NavDrawer dans la vue de votre tiroir de navigation:

    layout/navigation_drawer.xml (ou toute autre vue appropriée utilisée pour votre tiroir de navigation)

    <?xml version="1.0" encoding="utf-8"?>
    <ListView xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:id="@+id/navigation_drawer"
        style="@style/NavDrawer"
        Android:layout_height="match_parent"
        Android:layout_gravity="start"
        Android:choiceMode="singleChoice"
        Android:divider="@Android:color/transparent"
        Android:dividerHeight="0dp"
        Android:background="#FFF"
    />
    
32
Matthew Kairys

Après avoir cherché une solution plus simple, j'ai trouvé un article très clarifiant: Dimensionnement du tiroir de navigation des matériaux .

Ici:

L'écran du Nexus 5 est un Nice 640 x 360 dp (xxhdpi), et une barre d'application dessus mesure 56 dp. Le tiroir de navigation doit donc être:

[largeur en dp] = 360dp - 56dp = 304dp

Un Nexus 4 possède un écran 640 x 384 dp (xhdpi) à la place. Même hauteur de barre d'application 56dp. Son tiroir nav?

[largeur en dp] = 384dp - 56dp = 328dp

Alors, comment les concepteurs de Google ont-ils trouvé des largeurs de 288dp et 304dp, respectivement? Je n'ai aucune idée.

Les applications Google, en revanche, semblent être d'accord avec mes calculs. Oh, et tu sais ce qui est le plus drôle dans tout ça? L'iPhone (qui a différentes hauteurs d'écran, mais une largeur constante de 320 dp) est correctement marqué comme ayant un tiroir de navigation de 264dp.

Fondamentalement, cela montre que certaines directives concernant le tiroir de navigation se contredisent et que vous pouvez utiliser la règle suivante pour éviter les calculs:

Vous pouvez en principe toujours utiliser 304dp sur -sw360dp et 320dp sur -sw384dp pour votre tiroir de navigation, et vous aurez raison.

8
jmart

Ils ont déjà mis à jour les spécifications, maintenant la largeur du tiroir de navigation est:

Math.min(screenWidth — actionBarSize, 6 * actionBarSize);
4
vovkab

Eh bien, j'ai trouvé cela très difficile à comprendre et à mettre en œuvre. Malheureusement, la solution de Matthew a rendu mon tiroir de navigation trop large pour le paysage, et cela semblait contraire aux pratiques de Google, c'est-à-dire que la largeur de navigation est déterminée par la plus petite largeur de l'appareil. En tout cas, cela ne fonctionnerait pas dans mon cas car j'ai désactivé la configuration dans mon manifeste. J'ai donc décidé de changer la largeur de navigation dynamiquement avec le code suivant.

Il convient de noter que mon application est uniquement destinée aux téléphones et j'ai choisi 320dp comme largeur maximale. C'est aussi pourquoi j'ai choisi une hauteur de barre d'outils de 56dp pour les deux orientations.

J'espère que cela aide quelqu'un et qu'il peut éviter le stress inutile qu'il m'a causé.

navDrawLayout.post(new Runnable()
    {

        @Override
        public void run() {

            Resources r = getResources();

            DisplayMetrics metrics = new DisplayMetrics();

            if(r.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){

                getWindowManager().getDefaultDisplay().getMetrics(metrics);
                int height = metrics.heightPixels;

                float screenWidth = height / r.getDisplayMetrics().density;
                float navWidth = (screenWidth - 56); 

                navWidth = Math.min(navWidth, 320); 

                int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());

                DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams(); 
                params.width = newWidth; 
                navDrawLayout.setLayoutParams(params);      


            }

            if(r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {

            getWindowManager().getDefaultDisplay().getMetrics(metrics);
            int width = metrics.widthPixels;

            float screenWidth = width / r.getDisplayMetrics().density;
            float navWidth = screenWidth - 56; 

            navWidth = Math.min(navWidth, 320); 

            int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());

            DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams(); 
            params.width = newWidth; 
            navDrawLayout.setLayoutParams(params); 


            }
        }

    }
    ); 
0
JupiterT

La largeur du tiroir de navigation dépend de la plus petite largeur de l'appareil, de son orientation et de la version Android.

Consultez cet article sur le dimensionnement du tiroir de navigation.

0
Sotti