Material Design Support

google-material2

Introduit lors de la Google IO 2014, le Material Design est la nouvelle norme graphique des applications Android. Malheureusement, depuis cette dernière conférence, très peu de modules officiels permettent de l’intégrer facilement, nous avions l’habitude utiliser des modules open-source afin de reproduire chaque composant.

Surprise totale, lors de la Google IO 2015, est annoncé une librairie de support permettant de réaliser facilement une belle application, en embarquant une bonne partie des nouveaux composants et layouts qui font l’identité du Material Design, tel que le Floating Action Button ou les Collapsing toolbar (comme dans leur exemple de fiche de contact).

J’ai réalisé un projet de test, contenant la plupart des nouveaux composants Material Design, disponible à l’adresse suivante : github, que je prendrai comme support tout au long de ce tutorial.

Introduction

Comme toutes les librairies de support, Google l’a mise à disposition sur Maven afin qu’elle soit facilement importée dans chaque projet, il suffit donc d’ajouter à votre build.gradle

compile 'com.android.support:design:22.2.0'

Composants

FloatingActionButton

Introduit lors de la Google IO 2014, le Floating Action Button est l’élément le plus représentatif du Material Design. Il centralise les actions principales en un seul et même endroit. Jusque maintenant il fallait utiliser des librairies externes afin de l’utiliser facilement, nous pouvons dès lors utiliser la version développée par Google :

components_buttons_usage1

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="@color/blue"
    android:src="@drawable/ic_plus" />

Par défaut si vous n’indiquez pas la couleur, cet élément utilisera la “colorAccent” définit dans votre style.xml


 SnackBar

Remplaçant des célèbres Toast, leur utilisation est aussi simple, mais quand même plus propre 🙂

Snackbar.make(viewClicked, "Archived", Snackbar.LENGTH_LONG)
                .setAction("Undo", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                }).show();;

TabBarLayout

Utilisé pour afficher des tabs à la mode Material Design, le TabBarLayout s’utilise avec un ViewPager afin d’afficher le titre de chaque onglet.

snackbar 3

<android.support.design.widget.TabLayout
      android:id="@+id/tabs"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />
viewPager.setAdapter(...);

TabLayout tabLayout = (TabLayout)findViewyId(R.id.tabs);
//indique au tablayout quel est le viewpager à écouter
tabLayout.setupWithViewPager(viewPager);

Layouts

CoordinatorLayout

Ce nouveau Layout permet de rajouter des dépendances entre plusieurs vues adjacentes. En fonction du comportement qu’on leur affecte, elles peuvent par exemple se déplacer ensembles (ce comportement est un CoordinatorLayout.Behavior).

L’exemple concret se trouve dans la vidéo suivante, lorsque la snackbar arrive, elle décale le Floating Action Button automatiquement.

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...VUES_INTERDEPENDANTES...

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

Notez que dans certains cas nous ne voulons pas que certaines vues soient affectées. Dans le cas d’une scrollview par exemple, il a peu de sens de la déplacer, vu qu’elle prend toute la place dont elle a besoin pour afficher du contenu. Dans ce cas il suffit de modifier son CoordinatorLayout.Behavior, comme l’exemple suivant :

<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.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    ...VUES_INTERDEPENDANTES...

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

Pour les plus curieux, voici le contenu de appbar_scrolling_view_behavior :

<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>

Ce qui indique que nous utilisons une scrollview, qui ne sera pas affectée par les transformations des vues adjacentes, mais fait remonter l’information au CoordinatorLayout qu’il faut écouter le scroll de cet élément, qui va décider du comportement à effectuer à toutes ses sous vues. Il peux par exemple déplacer une autre vue lors du scroll de ce ViewPager (ou des ScrollViews/RecyclerViews contenues dans ce ViewPager).

 

A la façon des RelativeLayout, le CoordinatorLayout ajoute des attributs à ses vues enfants : app:layout_scrollFlags et app:layout_collapseMode.

 

app:layout_scrollFlags

  • scroll : ce flag doit être ajouté aux vues dont vous voulez qu’elles suivent le scroll du CoordinatorLayout. Sans ce flag, les vues ne seront pas déplacées
  • enterAlways : ce flag permet d’activer le quick-return de cette vue. Elle disparait avec le scroll up et réapparait lors du scroll down
  • enterAlwaysCollapsed : Lorsque votre vue possède une minHeight et que vous utilisez ce flag, celle-ci va apparaître initialement avec sa taille minimale, puis va suivre le scroll pour atteindre sa taille finale (collapse -> visible)
  • exitUntilCollapsed : ce flag, à l’inverse du enterAlwaysCollapsed, fait apparaître la vue à sa taille initiale, puis la réduit jusque sa minHeight lors du scroll (visible -> collapse)

app:layout_collapseMode

  • pin : cette vue sera fixée en haut de l’écran lors du scroll
  • parallax : la vue disparaitra avec un effet parallax, donc avec une vitesse différente du scroll actuel
  • en l’absence d’un collapseMode, la vue disparait totalement de l’écran lors du scroll, comme vous en avez l’habitude

 

Cette vue n’en a pas l’air, mais c’est la plus importante de la support design librairie. Vous le verrez très bientôt, elle est utilisée dans plus de la moitié des composants Material Design.


AppBarLayout

Il faut voir l’AppBarLayout comme le principal conteneur du Toolbar. Il permet de relier cette vue avec d’autres composants, tels que le TabLayout afin de réagir facilement à des comportements de l’utilisateur. Par exemple il permet de cacher/afficher la toolbar lors du scroll de l’utilisateur, tout en laissant affiché les onglets :

<android.support.design.widget.AppBarLayout
    android:id="@+id/appBarLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <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:layout_scrollFlags="scroll|enterAlways" />

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

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

Comme indiqué précédement, cette vue a besoin d’être englobée dans un CoordinatorLayout afin de suivre les évènements de scroll :

<?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:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <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:layout_scrollFlags="scroll|enterAlways" />

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

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

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

CollapsingToolbarLayout

Un des premiers exemples de material design présenté lors de la Google IO 2014, le CollapsingToolbar est un élément qui permet d’afficher en entête d’une page contenu une image, qui disparait avec un effet parallax lors du scroll pour se transformer progressivement en toolbar. Je vous laisse visionner la vidéo de présentation :

Voici le résultat du rendu actuel du CollapsingToolbarLayout :

 

<android.support.design.widget.AppBarLayout
    android:id="@+id/appBarLayout"
    android:layout_width="match_parent"
    android:layout_height="300dp">

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

        <!-- l'image affichée en fond, ici la tour eiffel -->
        <ImageView
            android:id="@+id/backgroundImageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:scaleType="centerCrop"
            app:layout_collapseMode="parallax" />

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

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

</android.support.design.widget.AppBarLayout>
  • app:contentScrim : la couleur finale de la toolbar
  • app:layout_scrollFlags=”scroll|exitUntilCollapsed” : indique au Coordinator que cette vue va scroller en même temps que les autres (scroll), mais ne pas sortir de l’écran (exitUntilCollapsed), elle va juste réduire de taille
  • app:layout_collapseMode=”parallax” : indique que la vue va disparaitre avec un effet parallax lors du scroll
  • app:layout_collapseMode=”pin” : indique que la vue va suivre le scroll mais va restée fixée en haut de l’écran par la suite (équivalent à sticky)

Veuillez noter que cette vue doit s’utiliser dans un AppBarLayout, qui indiquer que ce groupe est bien le conteneur de l’ActionBar.

Comme la plupart des vues de cette support librarie, il est nécessaire de l’utiliser à l’intérieur d’un CoordinatorLayout afin qu’il soit actif lors du scroll de l’élément suivant (la scrollview)

<android.support.design.widget.CoordinatorLayout 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/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        ...
    <android.support.design.widget.AppBarLayout/>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

       ...

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

device-2015-05-29-234740 2 copie

Les plus curieux auront remarqué dans la seconde vidéo que le FloatingActionButton se réduit lors du scroll, ce comportement est aussi réalisé automatiquement avec le CollapsingToolbar

    ...
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_margin="10dp"
         android:clickable="true"
         android:src="@drawable/ic_discuss"
         app:layout_anchor="@id/appBarLayout"
         app:layout_anchorGravity="bottom|right|end" />

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

Le app:layout_anchor permet d’indiquer au CoordinatorLayout de faire le lien entre cette vue et le CollapsingToolbarLayout.


Drawer

NavigationView

Le NavigationView permet une implémentation rapide d’un NavigationDrawer Material. Il permet de définir depuis les layouts à quoi va ressembler le menu et son entête, puis de remplir les éléments de navigation depuis les fichiers menu/ (les mêmes que nous utilisons pour remplir les menus de la toolbar).

drawer

Il suffit d’utiliser le NavigationView avec le DrawerLayout

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true">

    <LE_CONTENU_PRINCIPAL>

    <!-- le drawer -->
    <android.support.design.widget.NavigationView
        android:id="@+id/navigationView"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/navigation_view_header"
        app:menu="@menu/navigation_view_elements"/>

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

Comme vous pouvez le constater, il faut déclarer

  • app:headerLayout : la vue qui sera utilisée en tant qu’entête du drawer
  • app:menu : les entrées du drawer

layout/navigation_view_header.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="@drawable/header"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:layout_gravity="bottom"
        android:text="@string/email"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

</FrameLayout>

menu/navigation_view_elements.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/ic_dashboard"
            android:title="Menu 1" />
        <item
            android:id="@+id/nav_messages"
            android:icon="@drawable/ic_event"
            android:title="Menu 2" />
        <item
            android:id="@+id/nav_friends"
            android:icon="@drawable/ic_headset"
            android:title="Menu 3" />
        <item
            android:id="@+id/nav_discussion"
            android:icon="@drawable/ic_forum"
            android:title="Menu 4" />
    </group>

    <item android:title="Second Menu">
        <menu>
            <item
                android:icon="@drawable/ic_dashboard"
                android:title="Second menu 1" />
            <item
                android:icon="@drawable/ic_forum"
                android:title="Second menu 2" />
        </menu>
    </item>

</menu>

Nous n’avons plus grand chose à faire du coté Java, à part récupéré l’évènement onNavigationItemSelected

NavigationView navigationView = (NavigationView)findViewById(R.id.navigationView);
navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        menuItem.setChecked(true);
                        //votre action
                        drawerLayout.closeDrawers();
                        return true;
                    }
                });


Voici le lien officiel des librairies de support Android

Sans oublier le lien vers mon projet de test sur Github

3 commentaire sur “Material Design Support

  1. Man i would have loved it, had tutorials been in English. Even though i will use chrome but you can do better and get famous by making up some English video tutorials!

  2. I found a bug in the CollapsingToolbarLayout for devices running android 5.0 the actionbar unsually shifts upwards and becomes half invisible. Can you tell me hwo to hix this?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *