Licence CC BY-NC-SA

La Toolbar, le composant Material Design pour la barre d'action

Publié :
Auteur :
Catégorie :

Si la Toolbar (doc) est un composant que vous ne connaissez pas, cette partie du tutoriel a de grandes chances de vous intéresser. Que vous soyez un débutant complet ou un familier des anciennes pratiques notamment avec l'usage de l'ActionBar (doc), lisez cette partie qui vous permettra de vous mettre à jour dans la gestion de la barre d'action de vos applications.

La section 1 se consacre à une explication du rôle de la Toolbar dans le Material Design : pourquoi est-ce que Google a jugé nécessaire de changer de composant et quelles sont les différences entre l'ancienne et la nouvelle barre d'action ? La section 2 explique comment intégrer basiquement une Toolbar, migrer de l'ActionBar vers la Toolbar et appliquer un style basique. Pour finir, la section 3 aborde les personnalisations communes que vous rencontrez régulièrement dans les autres applications Android.

A chaque situation, sa navigation

La navigation d'une application mobile peut devenir une tâche complexe lorsqu'elle doit afficher un grand nombre d'écrans avec des informations différentes. C'est pourquoi Android propose plusieurs composants visuels, chacun étant adapté à un type de navigation.

Tous ces composants sont utilisés au bon vouloir des développeurs. Il existe quand même des bonnes pratiques établies par Google pour garantir la meilleure expérience possible pour les utilisateurs finaux. Ce sont ces pratiques qui seront expliquées dans le cadre de ce tutoriel.

Parmi les navigations, nous retrouvons le screen slide. Il est utilisé pour mettre en place un enchainement d'écran par des onglets situés sous la barre d'action. Cette même barre d'action peut être utilisée pour la navigation. Par exemple, pour afficher les commentaires d'un article. Quant au menu latéral, il est utilisé pour les applications volumineuses où il y a besoin de distinguer plusieurs parties distinctes.

Si nous prenons l'exemple d'un client mail, un menu latéral est presque obligatoire pour une expérience utilisateur optimale. Cela serait anti ergonomique de naviguer à travers tous les écrans par un screen slide, par la barre d'action ou par d'autres moyens de navigation personnalisés ou non.

Si nous prenons un dernier exemple, une application de messagerie possède rarement un menu latéral. Elle se contente d'avoir une liste de conversations et un fil de discussion quand l'utilisateur sélectionne une conversation. Le menu latéral ne serait pas adapté pour afficher cette liste de conversations.

Rôle de la Toolbar au sein du Material Design

Avant le Material Design, la barre d'action était un composant fixe, difficilement personnalisable et nommée par une ActionBar. Ce composant est fortement lié à une Activity et totalement géré par le framework Android. Lollipop vient changer la donne et propose un nouveau composant. La Toolbar est une généralisation des barres d'action utilisée dans les layout des applications. Ce nouveau composant peut être placé n'importe où dans une interface au bon vouloir du développeur. Il en revient à lui de désigner une Toolbar comme la barre d'action d'une Activity.

Le Material Design amène de nouveaux besoins et change les réflexions quant à l'élaboration de vos interfaces. L'ajout de souplesse dans la Toolbar permet de coller au mieux à ces nouvelles contraintes :

  • Titre : comportement déjà largement adopté depuis les débuts de la barre d'action, la possibilité de placer un titre (et un sous-titre optionnellement) en haut à gauche de la barre.
  • Menu : placé en haut à droite de la barre d'action, les boutons destinés au menu de l'écran offre des actions sur le contenu affiché.
  • Navigation : une barre d'action peut déclarer un bouton de retour, un bouton d'ouverture d'un menu latéral, un bouton de fermeture d'un écran ou d'autres boutons plus personnels aux applications. Contrairement au menu, ces boutons devraient toujours être utilisés pour naviguer à travers des écrans.
  • Etiqueter des images couvertures : placer une image en couverture dans la barre d'action attire l'attention de l'utilisateur et lui permet d'attacher un visuel au contenu qu'il est en train de consulter.
  • Des vues personnalisées : la Toolbar n'est qu'un contenant comme les autres. Les développeurs ont le loisir d'y placer leurs vues personnalisées qui respectent leur charte graphique.

Toutes ces responsabilités peuvent déstabiliser. A quel moment est-il plus judicieux d'étendre la barre d'action pour y placer une image ou dois-je développer une barre d'action avec un titre, des menus, des boutons de navigation, l'étendre et y placer une ou deux vues personnalisées ?

Bien entendu, il existe des bonnes et des mauvaises pratiques dont voici les commandements.

  1. Tu nommeras tes écrans avec un titre. Pour ne pas perdre votre utilisateur, c'est important d'afficher un titre à la barre d'action de vos applications et qui représente bien la fonctionnalité principale de l'écran courant. Il n'est pas interdit d'utiliser du jargon spécifique qui parle que à vos utilisateurs mais tentez d'être le plus clair et compréhensible possible.

  2. Tu ne cumuleras pas les barres en haut de ton écran. Comme vous pouvez spécifier autant de barres que vous voulez dans vos fichiers XML, vous pouvez techniquement en rajouter plusieurs, à des endroits différents et avec des styles spécifiques. C'est une mauvaise idée. Ne placez qu'une seule barre et trouvez d'autres composants natifs pour les actions que vous avez placé dans les autres barres. Les utilisateurs Android ont l'habitude d'avoir qu'une seule barre, il n'y a pas de raison que votre application déroge à cette règle.

  3. Tu économiseras de la place. Cette règle s'applique de moins en moins de nos jours avec les écrans qui grandissent de plus en plus mais il existe toujours des appareils avec des petits écrans. Pour ceux là, pensez qu'il faut exposer le plus possible le contenu de votre écran. Par exemple, prenons un écran qui affiche une liste et une barre d'action. Un comportement souhaité est de masquer la barre d'action dès que l'utilisateur commence à descendre dans la liste. Il n'est plus utile d'afficher la barre d'action si l'utilisateur est occupé avec autre chose. Pensez simplement à re-afficher la barre quand l'utilisateur remonte dans la liste. C'est le comportement habituel des barres avec des listes. Sachez que cette règle redevient importante depuis qu'un utilisateur peut afficher 2 applications en même temps.

  4. Tu utiliseras judicieusement une couverture. Dès que votre écran peut être illustré par une image, il est recommandé d'étendre votre barre et de placer une image qui prend toute la place. Dès que l'utilisateur commence à naviguer sur l'écran, réduisez cette barre et retirez peu à peu le visuel de cette image. Attention, des pièges peuvent vite arriver. Si vous disposez d'image en basse qualité ou pas assez grande, évitez de l'utiliser comme couverture. Le rendu sera médiocre et vos utilisateurs mécontents. Assurez vous que toutes vos images seront d'une qualité suffisante.

  5. Tu n'abuseras pas des composants de navigation. Une barre d'action peut être rendu compatible avec un menu latéral et définir un bouton de retour. Cette combinaison est un exemple typique de composants incompatibles. Si vous rendez compatible votre barre d'action avec le menu latéral, le bouton tout à gauche de la barre est utilisé pour ouvrir ce menu. Mais le bouton de retour se place exactement à la même place. Ce bouton ne pouvant pas avoir deux rôles, vous devez choisir le composant le plus approprié pour votre écran.

  6. Tu garderas une cohérence. Les écrans peuvent avoir des barres d'action avec des titres, des menus ou des navigations différentes mais dans cette diversité, c'est important de garder une certaine cohérence. Par exemple, une application avec un menu latéral qui rend accessible les grandes fonctionnalités de l'application et des sous-écrans qui sont plus spécifiques. Un comportement usuel est une barre d'action compatible avec le menu latéral pour les écrans des grandes fonctionnalités et un bouton de retour à la place pour tous les autres écrans plus spécifique.

  7. Tu feras preuve de bon gout dans tes styles. Une application possède une charte graphique qui se conserve à travers tous les écrans. Vous êtes libre de mettre au point la charte que vous souhaitez mais tentez de la respecter.

Ajouter une Toolbar à votre application

Disons-le, le prix à payer pour une plus grande maitrise de la barre d'action se fait au détriment d'une simplicité d'usage pour le développeur mais apporte une plus grande personnalisation et flexibilité. Auparavant, la barre d'action était automatiquement rajoutée pour les terminaux sous Android 3 et supérieur. La migration va demander du travail, parfois longue si votre application existante est complexe dans son interface.

Utiliser une Toolbar comme une ActionBar

Commencez par spécifier les dépendances nécessaires dans le fichier build.gradle de votre application ou module :

1
2
3
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:support-v4:24.2.0'
compile 'com.android.support:design:24.2.0'

Dépendances nécessaires pour la Toolbar et son design

Vous remarquerez que les versions des bibliothèques sont identiques. Sachez que Google tient à garder le plus possible cette cohérence entre ses bibliothèque pour ne pas perdre le développeur entre tous les numéros de version. Vous ne risquez donc pas grand chose à spécifier le même numéro pour toutes ces bibliothèques et n'hésitez pas à faire confiance à Android Studio quand il vous signale qu'il y a une nouvelle version plus à jour que celle que vous avez spécifié. La version 24.2.0 spécifiée dans ce tutoriel ne correspondra pas éternellement à la dernière version en date. Ne vous étonnez donc pas d'avoir un numéro de version différent du mien !

Une fois que vous avez accès à toutes les ressources indispensables à la déclaration d'une Toolbar, la première chose à faire est la création ou modification du style de votre application. Votre style doit obligatoirement hériter du thème parent Theme.AppCompat.*. La suite de ce style parent dépend alors de ce que vous désirez. Vous avez le choix entre un thème dit light ou dark, et pour ces deux thèmes, avec automatiquement une Toolbar ou non.

Choisir le thème avec une Toolbar me permet-il pas de faire une migration simple et rapide depuis l'ancien système de la barre d'action ? Oui, tout à fait mais il y a plusieurs choses à savoir.

  1. Si vous utilisez qu'un seul style pour toute votre application mais que votre application comporte des écrans sans barre d'action, vous devrez spécifier un style particulier pour ces écrans. Typiquement, le même que le style général mais dans son mode sans Toolbar.
  2. Vous n'avez aucune maitrise sur la hiérarchie des vues autour de la barre d'action. Nous verrons plus loin dans ce chapitre que cette Toolbar peut vite montrer ses limites.

En conclusion, utilisez la Toolbar automatique que si vous êtes certain de n'avoir jamais besoin d'étendre la barre d'action ou d'y ajouter des comportements spéciaux.

Vous l'aurez compris, dans le cadre de ce tutoriel, nous allons déclarer un style qui ne place aucune barre d'action automatiquement pour des raisons évidentes de pédagogie.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- Base application theme. -->
<style name="AppTheme" parent="Base.Theme.ZdS">
</style>

<style name="Base.Theme.ZdS" parent="Theme.AppCompat.Light.NoActionBar">
  <item name="colorPrimary">@color/primary</item>
  <item name="colorPrimaryDark">@color/dark_primary</item>
  <item name="colorAccent">@color/accent</item>
  <item name="android:textColorPrimary">@color/primary_text</item>
  <item name="android:windowBackground">@color/window_background</item>
</style>

Style Material Design issu de la bibliothèque de compatibilité

Expliquons un peu les personnalisations placées dans notre style qui ont été introduites depuis la venue du Materiel Design pour la plupart.

  • colorPrimary, colorPrimaryDark et colorAccent définissent les couleurs principales de votre application. Votre barre d'action prendra la couleur de colorPrimary, la barre d'état prendra la couleur de colorPrimaryDark et toutes les actions secondaires (comme les boutons flottants) prendront la couleur de colorAccent. Pensez à choisir des couleurs harmonieuses pour ces 3 propriétés.
  • android:textColorPrimary définit la couleur de votre texte en dehors de la barre d'action et android:windowBackground définit la couleur du fond de votre application. Si vous choissiez un fond clair, pensez à une couleur de texte foncé et inversément si vous êtes sur un fond foncé.
Personnalisation d'un thème Material Design

Personnalisation d'un thème Material Design. Crédit : Portail développeur Android

Une propriété préfixée par android: est une propriété qui existait bien avant le Material Design et disponible directement dans le framework Android. S'il n'y a aucun préfixe, cela veut dire que le style a été déclaré en dehors du framework Android. Dans notre cas précent, ces propriétés ont été déclarées dans la bibliothèque de compatibilité sur le design.

La Toolbar est une vue comme les autres. Si votre thème dit explicitement que vous ne voulez pas automatiquement ce composant sur vos écrans, comme c'est le cas pour nous, vous devez la placer vous même dans les layout de vos écrans. L'exemple le plus basique est le suivant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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="match_parent"
  android:orientation="vertical">

  <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"/>

  <FrameLayout
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

</LinearLayout>

Exemple basique du placement d'une Toolbar

Nous plaçons en haut de l'écran le composant, nous lui spécifions la hauteur standard du composant grâce à la valeur ?attr/actionBarSize et nous lui donnons la couleur primaire, ?attr/colorPrimary, que nous avons précédemment configuré dans notre style. Mais même si visuellement, vous disposez bien d'une barre d'action en haut de votre écran, elle n'est pas encore vue comme telle. Pour qu'elle soit considérée comme une ActionBar, vous devez le dire explicitement à votre Activity qui doit obligatoirement hériter de AppCompatActivity.

1
2
3
4
5
6
7
public class ToolbarActivity extends AppCompatActivity {
  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_toolbar);
    setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
  }
}

Renseigne la Toolbar comme l'ActionBar de l'écran

Vous disposez dès lors d'un écran avec une Toolbar considérée comme une ActionBar. Vous pouvez y ajouter tous les comportement supplémentaires que vous voulez et que nous allons abordé dans la suite de ce chapitre.

Appliquer un style

Le style général que nous avons appliqué à notre application est un premier pas vers la personnalisation de la barre d'action mais nous pouvons aller plus loin. Nous pouvons personnaliser le titre, la couleur du texte des menus, la couleur des retours utilisateurs sur les boutons du menu et le popup des sous-menus. Reprenons notre Toolbar et appliquons lui 3 nouvelles propriétés.

1
2
3
4
5
6
7
8
<android.support.v7.widget.Toolbar
  android:id="@+id/toolbar"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
  app:theme="@style/ThemeOverlay.Randoomz.Toolbar"
  app:titleTextAppearance="@style/TextAppearance.Randoomz.Toolbar.Title"/>

Applique un style spécifique à une Toolbar

  • app:popupTheme et app:theme applique, respectivement, le style pour le popup des sous menus et pour la Toolbar. Ces deux propriétés doivent renseigner comme thème ThemeOverlay.AppCompat.* ou un style personnel qui hérite de ce thème.
  • app:titleTextAppearance applique le style pour le titre de la barre d'action. Cette propriété doit renseigner comme thème TextAppearance.Widget.AppCompat.Toolbar.Title ou un style personne qui hérite de ce thème.

Dans notre exemple, nous avons créer des thèmes personnels pour app:theme et app:titleTextAppearance, sur lesquelles nous allons rapidement revenir. Quant à app:popupTheme, nous avons spécifié le thème ThemeOverlay.AppCompat.Light. Cela permettra d'avoir des popup avec un fond clair, plus précisément blanc cassé.

Maintenant, regardons nos thèmes personnels dans nos styles.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<style name="TextAppearance.Randoomz.Toolbar.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
  <item name="android:textSize">21sp</item>
  <item name="android:textStyle">italic</item>
</style>

<style name="ThemeOverlay.Randoomz.Toolbar" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
  <item name="android:textColorPrimary">@android:color/holo_blue_light</item>
  <item name="actionMenuTextColor">@android:color/holo_green_light</item>
  <item name="colorAccent">@android:color/holo_orange_dark</item>
  <item name="colorControlNormal">@android:color/holo_purple</item>
  <item name="colorControlActivated">@android:color/holo_red_dark</item>
  <item name="colorControlHighlight">@android:color/holo_red_dark</item>
</style>

Style utilisé pour la Toolbar

Nous avons un premier thème qui est destiné à personnaliser le titre de notre barre d'action. Vous devrez aisément comprendre son contenu, nous spécifions simplement la taille et la mise en italique du titre. Le second thème est plus intéressant puisque c'est lui qui va personnaliser toutes les couleurs de la barre.

  • android:textColorPrimary donne la couleur au titre de la barre.
  • actionMenuTextColor donne la couleur des textes du menu affiché sur la barre.
  • colorAccent donne la couleur des checkboxes que vous pouvez avoir dans vos menus.
  • colorControlNormal donne la couleur de l'icône à 3 points destiné à afficher les menus cachés.
  • colorControlActivated donne la couleur de certaines vues à l'état activé (comme les switch ou les checkboxes).
  • colorControlHighlight donne la couleur des retours utilisateurs sur les boutons du menu.

Le résultat de ce style sur un écran avec un menu sera le suivant :

Personnalisation d'une Toolbar

Personnalisation d'une Toolbar

Mais bien entendu, vous ferez quelque chose de plus joli pour vos écrans. :)

Onglets attachés à la Toolbar

L'onglet est un composant de navigation répandu et utilisé depuis les toutes premières versions d'Android, mais son usage a évolué quasiment entre chaque version majeure d'Android. Aujourd'hui, vous allez découvrir son usage à travers la bibliothèque de compatibilité sur le design avec le composant TabLayout et couplé avec un ViewPager.

Un TabLayout peut déclarer directement dans son fichier XML des TabItem pour avoir des onglets fixes. Pourquoi n'allons-nous pas enseigner cette pratique dans ce tutoriel ? Simplement parce que l'usage le plus courant aujourd'hui est de rendre les onglets utilisables grâce à des swipe de l'utilisateur pour passer d'un onglet à l'autre et pour gérer plus beaucoup plus facilement toutes les interactions utilisateurs !

Partons du principe que notre application utilise un thème qui place automatiquement une Toolbar à notre écran (comme l'explique la section précédente), nous avons besoin que de placer un TabLayout et un ViewPager qu'il nous faudra lier dans l'Activity ou le Fragment hôte.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?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="match_parent"
  android:orientation="vertical">

  <android.support.design.widget.TabLayout
    android:id="@+id/tabs"
    style="@style/Widget.Randoomz.TabLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

  <android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="0px"
    android:layout_weight="1"/>
</LinearLayout>

Spécifier un TabLayout et un ViewPager

Notez qu'un style est placé sur le TabLayout. Il est en effet possible de personnaliser l'espace qui accueillera vos onglets et les onglets eux-même. Pour ce faire, il suffit de créer un style qui étend Widget.Design.TabLayout pour personnaliser l'espace des onglets et TextAppearance.Design.Tab pour les onglets eux-même.

Voici un exemple de style que vous pourriez utiliser pour les onglets, Tab :

1
2
3
4
5
<style name="TextAppearance.Randoomz.Tab" parent="TextAppearance.Design.Tab">
  <item name="android:textSize">14sp</item>
  <item name="android:textColor">?android:textColorSecondary</item>
  <item name="textAllCaps">true</item>
</style>

Style d'un onglet

Dans cet exemple, nous personnalisons la taille, la couleur du texte des onglets et nous spécifions que toutes leurs lettres doivent être en majuscule. Plus intéressant maintenant, voici un exemple de style que vous pourriez utiliser sur un TabLayout et qui utilise notre précédent style sur les onglets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<style name="Widget.Randoomz.TabLayout" parent="Widget.Design.TabLayout">
  <item name="tabMaxWidth">@dimen/tab_max_width</item>
  <item name="tabIndicatorColor">?attr/colorAccent</item>
  <item name="tabIndicatorHeight">4dp</item>
  <item name="tabPaddingStart">12dp</item>
  <item name="tabPaddingEnd">12dp</item>
  <item name="tabBackground">?attr/selectableItemBackground</item>
  <item name="tabTextAppearance">@style/TextAppearance.Randoomz.Tab</item>
  <item name="tabSelectedTextColor">?android:textColorPrimary</item>
</style>

Style du conteneur des onglets

  • tabMaxWidth donne la hauteur maximale de vos onglets.
  • tabIndicatorColor donne la couleur à l'indicateur placé sous un onglet quand il est sélectionné.
  • tabIndicatorHeight donne la hauteur de l'indicateur.
  • tabPaddingStart place un espace à gauche de l'onglet pour éviter de coller son texte sur son bord.
  • tabPaddingEnd place un espace à droite de l'onglet pour éviter de coller son texte sur son bord.
  • tabBackground donne la couleur du fond des onglets. Gardez en tête que la couleur renseignée doit aussi gérer le retour visuel. A la place d'une simple couleur, préférez un selector pour renseigner les couleurs aux différents états de l'onglet.
  • tabTextAppearance donne le style du texte des onglets. Nous renseignons ici notre précédent style.
  • tabSelectedTextColor donne la couleur du texte de l'onglet quand il est sélectionné.

Personnalisation des onglets d'une Toolbar

Vous avez là toutes les cartes en main pour personnaliser à fond vos onglets. Il ne nous reste plus qu'à lier le tout dans un écran en Java. Commençons par le ViewPager. Pour ceux qui ne connaissent pas ce composant, le ViewPager permet de glisser d'une page à l'autre. Pour initialiser ce composant, il suffit de lui fournir un FragmentPagerAdapter qui aura la responsabilité de créer nos pages et nos onglets. Un adaptateur simple pourrait être la création de trois onglets, nommés "Tab1", "Tab2" et "Tab3", et qui va instancier un fragment pour chaque onglet.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PagerAdapter extends FragmentPagerAdapter {
  private final String tabTitles[] = new String[]{"Tab1", "Tab2", "Tab3"};
  private final static int PAGE_COUNT = 3;

  PagerAdapter(FragmentManager fm) {
    super(fm);
  }

  @Override
  public int getCount() {
    return PAGE_COUNT;
  }

  @Override
  public Fragment getItem(int position) {
    return new FixeFragment();
  }

  @Override
  public CharSequence getPageTitle(int position) {
    return tabTitles[position];
  }
}

Adaptateur d'un ViewPager

Maintenant que nous avons un adaptateur qui va s'occuper de créer nos pages et nos onglets, il nous suffit de l'attacher au ViewPager et de l'attacher lui-même à notre TabLayout. Le tout se faisait très simplement de la manière suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_toolbar_tabs);

  final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
  viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));

  final TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
  tabLayout.setupWithViewPager(viewPager);
}

Lie le ViewPager au TabLayout

Cette simplicité est déconcertante. Ces quelques lignes nous permettent de mettre en place facilement et rapidement un écran avec des onglets et des fragments indépendants pour chacun de ces onglets. Le voici suivant correspond à nos onglets avec le style précédemment expliqué.

Toolbar avec des onglets

Toolbar avec des onglets

Toolbar étendue et gestion du scroll

Une fois n'est pas coutume, nous allons d'abord voir ce que nous allons développer pour que vous comprenez bien le résultat attendue. Vous connaissez sans aucun doute cet effet. C'est le plus souvent utilisé pour des applications de musique ou de films, là où afficher une image dans la Toolbar prend tout son sens pour illustrer le contenu de l'écran. Par exemple, dans un écran qui affiche les détails d'un album de musique, la couverture de cet album pourrait figurer dans la Toolbar.

Nous, nous allons tenter de développer l'écran ci-dessous composé d'une Toolbar, d'une image de couverture facultative et d'une liste dans le composant RecyclerView qui va nous permettre de réagir avec la barre d'action.

Toolbar étendue avec une couverture

Toolbar étendue avec une couverture

La première chose à faire consiste à renseigner un CoordinatorLayout comme conteneur racine à notre écran. Ce conteneur est une sous classe d'un FrameLayout et est utilisé pour spécifier les interactions avec ses vues filles. Dans notre cas présent, il va nous permettre de renseigner le comportement à adopter d'une liste par rapport à une Toolbar, que nous allons aussi devoir spécifier dans notre layout. Pour cette liste, nous allons utiliser un RecyclerView mais libre à vous de choisir une autre vue du moment qu'elle est scrollable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
  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="match_parent">

  <!-- Toolbar here. -->

  <android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"
    app:layoutManager="LinearLayoutManager"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>

Déclaration d'un CoordinatorLayout et d'une liste pour réagir avec la barre d'action

Constatez l'attribut app:layout_behavior avec comme valeur @string/appbar_scrolling_view_behavior. Cette dernière ressource est détenue par la bibliothèque de compatibilité et permet de notifier la barre d'action des scroll sur la liste. Ainsi, la barre peut réagir de manière appropriée.

La seconde chose à faire consiste à renseigner un AppBarLayout comme conteneur parent de notre Toolbar. Ce conteneur est une sous classe d'un LinearLayout vertical. Il gère les mouvements de scrolling de la Toolbar. Ses enfants doivent fournir le comportement de scrolling désiré à travers l'attribut app:layout_scrollFlags. Cet attribut peut prendre les valeurs scroll, enterAlways, enterAlwaysCollapsed, exitUntilCollapsed et snap :

  • scroll indique que nous voulons réagir au scroll.
  • enterAlways permet d'afficher la barre d'action directement quand l'utilisateur scroll vers le haut. Normalement, le comportement par défaut est d'afficher la barre d'action dès que l'utilisateur se trouve en haut de la liste et non pas n'importe où dans la liste.
  • enterAlwaysCollapsed permet d'afficher une hauteur supplémentaire à la Toolbar si elle est renseignée (pour afficher une couverture par exemple). Cette valeur va vous permettre d'étendre jusqu'au bout la barre d'action avant de continuer le scroll.
  • exitUntilCollapsed permet de faire disparaitre entièrement la barre d'action quand l'utilisateur scroll dans la liste.
  • snap permet d'avoir un petit effet de rebond quand l'utilisateur scroll qu'un petit peu dans la liste. Cet effet est montré sur notre écran présenté en début de section.

Je vous laisse à votre bon soin pour faire des tests en appliquant ces valeurs sur votre Toolbar mais nous, nous voulons en plus étendre la barre d'action et y placer une couverture. Pour parvenir à cet effet, nous devons placer un CollapsingToolbarLayout entre notre AppBarLayout et notre Toolbar. Ce conteneur est une sous classe d'un FrameLayout et gère tous les attributs de la barre étendue.

  • app:contentScrim est la couleur affiché quand la barre d'action atteint une certaine hauteur.
  • app:expandedTitleMarginEnd et app:expandedTitleMarginStart sont les marges à gauche et à droite du titre de la barre d'action quand elle est étendue.
  • app:layout_collapseMode se place sur les vues filles du conteneur et peut prendre comme valeur pin ou parallax. Ils signifient respectivement que la barre d'action doit être fixé en haut de l'écran et qu'il doit y avoir un effet de parallaxe sur la vue.

Pour finir, si vous désirez placer une couverture, il suffit de mettre une ImageView au même niveau que la Toolbar. La totalité du layout qui correspond à notre écran voulu correspond donc au code XML suivant.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
  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="match_parent">

  <android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="150dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.CollapsingToolbarLayout
      android:id="@+id/collapsing_toolbar"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:expandedTitleMarginEnd="64dp"
      app:expandedTitleMarginStart="30dp"
      app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

      <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_scrollFlags="scroll|enterAlways"/>

      <!-- Optional: Used to display a cover. -->
      <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/android_landscape"
        app:layout_collapseMode="parallax"
        app:layout_scrollFlags="scroll|enterAlways"/>
    </android.support.design.widget.CollapsingToolbarLayout>
  </android.support.design.widget.AppBarLayout>

  <android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"
    app:layoutManager="LinearLayoutManager"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

Layout utilisé pour une barre d'action étendue et une couverture

Recherche

La recherche est accessible par un bouton du menu qui va s'étendre sur toute la largeur de la barre quand l'utilisateur cliquera dessus. Après quoi, lors de la saisie d'une recherche, nous pourrons écouter chaque caractère rentré ou écouter la soumission de sa recherche. C'est au choix et nous allons apprendre à gérer ces 2 options.

Dans un premier temps, nous allons devoir créer un menu pour notre recherche. Renseignez l'icône qui vous semble la plus appropriée, la valeur collapseActionView dans l'attribut app:showAsAction pour que le menu réagisse à son ouverture sur tout le long de la barre et enfin la valeur android.support.v7.widget.SearchView dans l'attribut app:actionViewClass pour spécifier la vue standard de la recherche.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<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="org.randoomz.zdsmessenger.ui.search.SearchActivity">
  <item
    android:id="@+id/action_search"
    android:icon="@drawable/ic_search"
    android:title="@string/menu_search"
    app:actionViewClass="android.support.v7.widget.SearchView"
    app:showAsAction="ifRoom|collapseActionView"/>
</menu>

Menu pour la recherche

Dans un second temps, nous allons renseigner la configuration de notre écran de recherche grâce à un fichier xml qui doit être rangé dans le dossier res/xml. Ce fichier xml n'est pas spécifique au Material Design, ni à la Toolbar. Il n'a pas changé depuis les débuts d'Android et renseigne le message qui invite à la recherche dans le composant et des modes complémentaires si vous le désirez. Pour ce dernier cas, vous pouvez demander au framework Android de rajouter automatiquement la reconnaissance de voix grâce aux valeurs showVoiceSearchButton et launchRecognizer. C'est ce que nous renseignons dans notre recherche.

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<searchable
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:hint="@string/menu_search"
  android:label="@string/app_name"
  android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"/>

Configuration de la recherche

Dans un troisième temps, nous devons lier ce dernier fichier de configuration à notre Activity qui contient notre écran de recherche. De plus, si nous le désirons, nous pouvons informer le système que notre application contient un écran de recherche. Cela permettra à d'autres applications de lancer notre Activity pour effectuer la recherche souhaitée. Si nous partons de l'hypothèse que notre Activity se nomme ToolbarSearchActivity, nous renseignerons la déclaration suivante de notre écran dans notre manifest.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<activity
  android:name="org.randoomz.demo.design.toolbar.ToolbarSearchActivity"
  android:label="@string/title_design_toolbar_search">
  <intent-filter>
    <action android:name="android.intent.action.SEARCH"/>
  </intent-filter>

  <meta-data
    android:name="android.app.searchable"
    android:resource="@xml/searchable"/>
</activity>

Déclaration de notre Activity de recherche dans notre fichier manifest

Pour finir, il nous suffit d'écouter toute cette configuration dans notre Activity. Dans la méthode onCreateOptionsMenu(Menu), vous allez pouvoir désérialiser le menu que nous avons confectionné plus tôt, récupérer le MenuItem de notre recherche et utiliser la classe MenuItemCompat, issue de la bibliothèque de compatibilité, pour appeler la méthode setOnActionExpandListener et spécifier que nous voulons bien étendre la vue sur toute la largeur de la barre d'action. Le début de la méthode pourrait ressembler à l'exemple suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
getMenuInflater().inflate(R.menu.menu_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
  @Override public boolean onMenuItemActionExpand(MenuItem item) {
    // Method called when the search view is expand.
    return true;
  }

  @Override public boolean onMenuItemActionCollapse(MenuItem item) {
    // Method called when the search view is closed.
    return true;
  }
});

Configure le menu de recherche pour l'étendre sur toute la largeur de la Toolbar

Il est bien inutile de configurer une barre de recherche si nous pouvons pas récupérer le résultat. Pour y parvenir, il suffit de récupérer la SearchView à partir du MenuItem de la recherche et de lui attacher le listener SearchView.OnQueryTextListener pour écouter chaque caractère rentrée dans la barre ou quand l'utilisateur soumet la recherche.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
final SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
  @Override public boolean onQueryTextSubmit(String query) {
    // Method called when the search is submitted.
    querySubmit(query);
    searchView.clearFocus();
    return true;
  }

  @Override public boolean onQueryTextChange(String newText) {
    if (newText == null || newText.length() < 3) {
      return false;
    }
    // Method called when the text in the search view changes.
    return true;
  }
});

Écoute les caractères rentrées dans la barre de recherche

Dernière chose importante, il faut récupérer la configuration XML de la recherche attachée à notre Activity avec notre vue de recherche grâce au SearchManager.

1
2
final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));

Liaison entre la configuration de la recherche et la vue de la recherche

Le tout donnera le résultat suivant quand vous cliquerez sur le bouton du menu.

Recherche dans la Toolbar

Recherche dans la Toolbar