Material Design Support
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 :
<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.
<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>
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).
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
Looking Great,
Awesome man. explained nicely 🙂
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!
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?