Abordons maintenant un second système de navigation très populaire sur Android : le Navigation Drawer.
Pour cela, nous partirons d'une nouvelle mini-application (MyNavDrawer), afin de voir ensemble et pas à pas le fonctionnement de ce pattern.
Vous trouverez l'application vierge de départ à ce lien.
Introduction
Très récent puisque créé à partir de l'année 2013, le Navigation Drawer combine à lui seul plusieurs patterns que nous avons précédemment étudié. Ce menu de navigation est principalement composé :
D'un DrawerLayout, utilisé comme layout racine principal
D'une Toolbar, utilisée notamment pour afficher le bouton de navigation "Hamburger"
D'une NavigationView, représentant visuellement le menu du Navigation Drawer
D'un FrameLayout, contenant les fragments (pages) du Navigation Drawer
Oh la la ! Il y a beaucoup de classes différentes et entremêlées là non ?
Je dirais plutôt que les choses sont bien découpées... ;) Ne vous inquiétez pas, l'implémentation du Navigation Drawer n'est pas si compliquée, comme nous allons le voir tout de suite !
Implémentation
Reprenons maintenant notre mini-application, MyNavDrawer. Comme vous vous en doutez, nous allons lui créer un Navigation Drawer !
Avant toute chose, nous allons vérifier que la librairie Design Support est bien installée et gérée par notre gestionnaire de dépendance Gradle. En effet, la vue NavigationView se trouve à l'intérieur :
dependencies {
...
compile 'com.android.support:design:26.1.0'
}
Puis, nous allons modifier le layout de notre activité principale MainActivity afin d'y ajouter les premiers composants graphiques du NavigationDrawer.
Fichier activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
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"
android:id="@+id/activity_main_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start"
tools:context="com.openclassrooms.mynavdrawer.MainActivity">
<!-- 1 | MainActivity RootView -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<!-- Toolbar -->
<android.support.v7.widget.Toolbar
android:id="@+id/activity_main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/ToolBarStyle"/>
<!-- FrameLayout for our fragments -->
<FrameLayout
android:id="@+id/activity_main_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<!-- 2 | NavigationView -->
<android.support.design.widget.NavigationView
android:id="@+id/activity_main_nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/activity_main_nav_header"
app:menu="@menu/activity_main_menu_drawer" />
</android.support.v4.widget.DrawerLayout>
Explications : Dans ce layout, nous avons implémenté toutes les vues nécessaires à la création de notre NavigationDrawer :
DrawerLayout : Il représente la vue racine principale de notre NavigationDrawer puisqu'il contiendra toutes les sous-vues nécessaires au fonctionnement de ce dernier, comme la Toolbar, le FrameLayout ainsi que le NavigationView.
Toolbar : Son intégration au sein du DrawerLayout permettra entre autres, l'affichage et la gestion du bouton de navigation appelé "Hamburger" (les 3 barres horizontales).
FrameLayout : Notre conteneur habituel qui contiendra l'ensemble de nos fragments
NavigationView : Cette vue représente le menu en lui-même, qui s'affichera ou se cachera lorsqu'on appuiera sur le bouton Hamburger de la Toolbar, et qui contiendra la liste de l'ensemble des pages (fragments) accessibles sur notre application.
Intéressons-nous de plus près à la NavigationView. Cette dernière dispose de deux attributs qui permettront de la configurer assez simplement :
app:headerLayout
permet de définir, via un layout XML, un header (en-tête) à notre NavigationViewapp:menu
permet de définir, via un layout XML, un menu qui affichera la liste de nos différents écrans sur la NavigationView
Ces deux attributs pointant sur deux fichiers layouts différents, il faut maintenant penser à les créer... :)
Fichier /res/layout/activity_main_nav_header.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="#B2B9E1"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_openclassrooms" />
</LinearLayout>
Explications : Rien de compliqué ici, nous créons juste un layout pour le header de notre NavigationView. Nous lui ajoutons une image (que vous pouvez retrouver ici), ainsi qu'une dimension de 176 dip (créée dans le fichier dimens.xml).
Fichier /res/menu/activity_main_menu_drawer.xml :
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/activity_main_drawer_news"
android:icon="@drawable/ic_dashboard_white_24dp"
android:title="Fil d'actualité" />
</group>
<item android:title="Configuration">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/activity_main_drawer_profile"
android:icon="@drawable/ic_account_box_white_24dp"
android:title="Profil" />
<item
android:id="@+id/activity_main_drawer_settings"
android:icon="@drawable/ic_settings_applications_white_24dp"
android:title="Paramètres" />
</group>
</menu>
</item>
</menu>
Explications : Nous définissons ici le menu de notre NavigationView en XML, qui va contenir l'ensemble des pages que l'on souhaite gérer dans notre Navigation Drawer. Chaque item représente une ligne, contenant un titre et une image. Vous pouvez retrouver l'ensemble des icônes utilisées à ce lien.
Pour finir, nous allons modifier quelques fichiers de ressources, afin notamment d'y ajouter la hauteur de l'en-tête de notre NavigationView, et personnaliser notre Toolbar (comme nous avons pu le faire dans les précédents chapitres).
Fichier dimens.xml :
<resources>
<!-- Default NavigationView Header height -->
<dimen name="nav_header_height">176dp</dimen>
</resources>
Explications : Cette dimension est utilisée dans le fichier activity_main_nav_header.xml créé précédemment, afin de définir la hauteur de l'en-tête (header) de la NavigationView. C'est la valeur par défaut proposée par Android. Libre à vous ensuite de l'adapter en fonction de vos besoins et vos envies.
Fichier styles.xml :
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Toolbar theme. -->
<style name="ToolBarStyle" parent="AppTheme">
<item name="android:textColorPrimary">@android:color/white</item>
<item name="colorControlNormal">@android:color/white</item>
</style>
</resources>
Explications : Vous vous souvenez sûrement de ces lignes... et c'est normal ! Elles nous servent ici à modifier l'apparence de notre Toolbar, utilisée dans le NavigationDrawer à travers le layout activity_main.xml. N'oubliez également pas de faire hériter votre thème principal de NoActionBar, afin de masquer la Toolbar par défaut.
Extrait du fichier strings.xml :
<resources>
...
<!-- Used in NavigationDrawer -->
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
</resources>
Explications : Ce fichier contient l'ensemble des textes de notre application. Ces deux lignes nous serviront à la création d'un objet utilisé par le NavigationDrawer. Ne vous inquiétez pas si vous ne le comprenez pas tout de suite, nous y reviendrons en détail dans un autre cours.
Il ne nous manquerait pas quelque chose là ? :-°
Tout à fait ! Car comme d'habitude, qui va faire le lien entre tous ces éléments ? Et bien c'est notre activité MainActivity, bien sûr... ;)
Fichier MainActivity.java :
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
//FOR DESIGN
private Toolbar toolbar;
private DrawerLayout drawerLayout;
private NavigationView navigationView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 6 - Configure all views
this.configureToolBar();
this.configureDrawerLayout();
this.configureNavigationView();
}
@Override
public void onBackPressed() {
// 5 - Handle back click to close menu
if (this.drawerLayout.isDrawerOpen(GravityCompat.START)) {
this.drawerLayout.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// 4 - Handle Navigation Item Click
int id = item.getItemId();
switch (id){
case R.id.activity_main_drawer_news :
break;
case R.id.activity_main_drawer_profile:
break;
case R.id.activity_main_drawer_settings:
break;
default:
break;
}
this.drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
// ---------------------
// CONFIGURATION
// ---------------------
// 1 - Configure Toolbar
private void configureToolBar(){
this.toolbar = (Toolbar) findViewById(R.id.activity_main_toolbar);
setSupportActionBar(toolbar);
}
// 2 - Configure Drawer Layout
private void configureDrawerLayout(){
this.drawerLayout = (DrawerLayout) findViewById(R.id.activity_main_drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawerLayout.addDrawerListener(toggle);
toggle.syncState();
}
// 3 - Configure NavigationView
private void configureNavigationView(){
this.navigationView = (NavigationView) findViewById(R.id.activity_main_nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
}
Explications : Notre activité jouant le rôle de contrôleur, celle-ci va récupérer toutes les vues de son layout afin de les configurer et les faire fonctionner harmonieusement ensemble :
Ligne 1 : Nous récupérons et configurons la Toolbar.
Ligne 2 : Nous récupérons le DrawerLayout, et créons à partir de lui et de la toolbar, le fameux bouton "Hamburger".
Ligne 3 : Nous récupérons la NavigationView afin qu'elle puisse s'enregistrer au listener de l'activité (via l'interface
NavigationView.OnNavigationItemSelectedListener
), nous permettant ainsi de récupérer les clics du menu.Ligne 4 : Comme nous avons implémenté l'interface
NavigationView.OnNavigationItemSelectedListener
, nous devons déclarer la méthode chargée de récupérer les clics sur le menu, à savoironNavigationItemSelected
. Celle-ci nous permettra, en fonction de l'identifiant d'un item (déclaré dans le fichier activity_main_menu_drawer.xml) du menu, d'effectuer une action spécifique, comme l'affichage d'une page par exemple.Ligne 5 : Nous redéfinissons ici la méthode
onBackPressed()
afin de faire en sorte de fermer (s'il est ouvert) le NavigationDrawer, dès lors que l'utilisateur appuie sur la touche retour de son téléphone.Ligne 6 : Enfin, on appelle toutes nos méthodes de configuration depuis la méthode
onCreate()
de notre activité.
Lancez maintenant votre application. Félicitations ! Vous avez un Navigation Drawer parfaitement fonctionnel ^^.
N'hésitez pas à parcourir un à un l'ensemble des fichiers que nous avons créé, afin de vous familiariser avec la logique du NavigationDrawer. Cette approche et ce fonctionnement sont valables sur énormément de Pattern Android différents.
Conclusion
Notre navigation Drawer s'affiche correctement et fonctionne plutôt bien, super ! Cependant, lorsque l'on clique sur un élément du menu, rien ne se passe... o_O
Et c'est normal ! Dans le prochain chapitre, nous étudierons ensemble la manière de gérer l'affichage de différentes pages à partir du NavigationDrawer.
Vous pouvez retrouver le code source terminé de MyNavDrawer abordé dans ce chapitre en suivant ce lien.