I. Migrer son ActionBar vers la ToolBar▲
I-A. Mise en place du style▲
Tout d'abord, il suffit de définir votre style dans le fichier res/values/styles.xml (ci-dessous). Il hérite de Theme.AppCompat et affecte la valeur true à la balise windowNoTitle et false à la valeur windowActionBar, ce qui signifie que je ne souhaite ni la barre de titre ni l'ActionBar pour mon activité.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<resources>
<style
name
=
"AppBlankTheme"
parent
=
"Theme.AppCompat.Light.DarkActionBar"
>
<!-- Main theme colors -->
<!-- your app branding color for the app bar -->
<item
name
=
"colorPrimary"
>
@color/primary</item>
<!-- darker variant for the status bar and contextual app bars -->
<item
name
=
"colorPrimaryDark"
>
@color/primary_dark</item>
<!-- theme UI controls like checkboxes and text fields -->
<item
name
=
"colorAccent"
>
@color/accent</item>
</style>
<!--Both themes below are those accepted to make the ToolBar works-->
<!-- Base application thème. -->
<style
name
=
"AppTheme"
parent
=
"AppBlankTheme"
>
<!-- Customize your theme here. -->
<!-- Base application thème. -->
<item
name
=
"windowNoTitle"
>
true</item>
<item
name
=
"windowActionBar"
>
false</item>
<item
name
=
"spinnerDropDownItemStyle"
>
@style/mydropdown</item>
</style>
</resources>
J'ai défini aussi les couleurs de mon activité. Cela vient de la version Lollipop de la Support Library qui (grâce à Chris Banes) nous permet simplement de définir un jeu de couleur uni pour notre activité. Ces couleurs seront automatiquement utilisées par le Theme.AppCompat au niveau de votre application de manière à avoir une charte colorimétrique uniforme. Vous pouvez aller vous faire plaisir ici pour choisir vos couleurs et voir l'effet que cela produit : http://www.materialpalette.com/.
Maintenant que nous avons défini notre style, appliquons-le. Comme d'habitude cela s'effectue au niveau du fichier Manifest :
2.
3.
4.
5.
6.
7.
8.
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
package
=
"com.android2ee.formation.lollipop.toolbar"
>
<application
android
:
allowBackup
=
"true"
android
:
icon
=
"@mipmap/ic_launcher"
android
:
label
=
"@string/app_name"
android
:
theme
=
"@style/AppTheme"
>
I-B. Modification du layout de l'activité▲
C'est ici que tout se joue (oui, tout, la survie de l'univers, l'existence de Dieu, tout) dans votre layout. L'ActionBar n'est plus ajouté par défaut par le framework (on a demandé explicitement d'utiliser un thème pour notre application qui n'a pas l'ActionBar), c'est nous qui la positionnons dans notre layout. C'est un changement profond dans la philosophie de l'ActionBar, sommes-nous suffisamment matures pour la positionner où il se doit, en respect des nouvelles règles de Design ?
Pour les règles de Design, c'est ici : http://www.google.com/design/spec/material-design/introduction.html.
Et puis le site est tellement bien fait, tellement riche que ce serait du gâchis de ne pas l'utiliser.
Donc tout ça, pour vous dire de mettre à jour votre layout comme suit :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
orientation
=
"vertical"
tools
:
context
=
".MainActivity"
>
<android.support.v7.widget.Toolbar
android
:
id
=
"@+id/toolbar"
android
:
minHeight
=
"?attr/actionBarSize"
android
:
background
=
"#2196F3"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
>
</android.support.v7.widget.Toolbar>
<TextView
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/hello_world"
>
</TextView>
<ListView
android
:
id
=
"@+id/sampleList"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
android
:
layout_margin
=
"5dp"
android
:
animateLayoutChanges
=
"true"
android
:
drawSelectorOnTop
=
"true"
>
</ListView>
</LinearLayout>
Alors clairement, je vous montre le layout du tutoriel, la chose importante ici est le linearLayout de premier niveau suivi directement par la ToolBar, puis le contenu réel de votre activité.
La seule chose qui diffère d'un composant normal est le minHeight qui récupère la taille de l'ActionBar du thème habituel.
I-C. Mise à jour du fichier Java de l'activité (ou du fragment)▲
Vous avez quasiment fini de migrer votre application, il ne vous reste que deux petites choses à faire :
- Votre activité hérite d'AppCompatActivity.
- Définir la ToolBar comme étant l'ActionBar de l'activité.
Et cela s'effectue tout simplement :
2.
3.
4.
5.
6.
7.
8.
9.
10.
public
class
MainActivityextends AppCompatActivity {
ListView sampleList;
@Override
protected
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.activity_main);
Toolbar toolbar =
(
Toolbar) findViewById
(
R.id.toolbar);
setSupportActionBar
(
toolbar);
getSupportActionBar
(
).setSubtitle
(
"Using ToolBar"
);
I-D. Vérification de la version de la Support Library▲
Pour que votre code s'exécute proprement, vous devez utiliser la Support Library de niveau (au moins) 22.2.0 :
compile 'com.android.support:appcompat-v7:22.2.0'
I-E. Rappel sur votre fichier XML de menus▲
Depuis que vous utilisez l'appCompat (et avant cela la SherlockBar), vos fichiers de menus définissent un nouveau namespace pour être interprétables par la Support Library, sinon la balise showAsAction est juste ignorée et vos icônes absentes.
Il vous faut donc définir ce namespace et l'utiliser pour toutes les balises qui n'appartiennent pas aux vieilles versions :
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.
<menu
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
actionbarcompat_mse
=
"http://schemas.android.com/apk/res-auto"
>
<item
android
:
id
=
"@+id/action_one"
android
:
icon
=
"@drawable/ic_action_one"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"One"
>
</item>
<item
android
:
id
=
"@+id/action_two"
android
:
icon
=
"@drawable/ic_action_two"
android
:
orderInCategory
=
"1"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"Two"
>
</item>
<item
android
:
id
=
"@+id/action_one_duplicate"
android
:
icon
=
"@drawable/ic_action_one"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"One2"
>
</item>
<item
android
:
id
=
"@+id/action_two_duplicate"
android
:
icon
=
"@drawable/ic_action_two"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"ifRoom"
android
:
title
=
"Two2"
>
</item>
<item
android
:
id
=
"@+id/action_settings"
android
:
orderInCategory
=
"100"
actionbarcompat_mse
:
showAsAction
=
"never"
android
:
title
=
"@string/action_settings"
>
</item>
</menu>
II. Action Mode▲
Continuons notre découverte de la ToolBar et en particulier regardons comment mettre l'ActionMode en place.
Tout d'abord qu'est-ce que l'ActionMode ? C'est la capacité de changer notre ToolBar pour n'afficher que des actions lors d'un événement utilisateur particulier. L'exemple compréhensible est quand je sélectionne un mail, la ToolBar affiche de nouvelles actions.
C'est ce changement que l'on nomme l'ActionMode et grosso modo, cela correspond juste à demander à notre ToolBar d'afficher un nouveau fichier XML de menus. Oui, en fait c'est trivial. Avant ça l'était vraiment, maintenant à deux ou trois petits détails près, ça l'est toujours. Regardons comment le mettre en place :
Le code Java et la définition des menus n'ont pas changé (non, toi non plus tout n'a pas changé - Julio Iglesias )
II-A. Fichiers XML des menus▲
Comme vous l'avez compris, il nous faut deux menus XML pour mettre en place l'ActionMode. Le premier est l'OptionMenu naturel de votre activité (fragment) et le second celui que vous souhaitez afficher lorsque l'ActionMode est activé.
Il est évident que c'est vos menus, vous mettez les items que vous souhaitez afficher, mes fichiers sont des fichiers d'exemples :
2.
3.
4.
5.
6.
7.
<menu
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
>
<item
android
:
id
=
"@+id/action_settings"
android
:
orderInCategory
=
"100"
actionbarcompat
:
showAsAction
=
"never"
android
:
title
=
"@string/action_settings"
>
</item>
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.
<menu
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
actionbarcompat_mse
=
"http://schemas.android.com/apk/res-auto"
>
<item
android
:
id
=
"@+id/action_one"
android
:
icon
=
"@drawable/ic_action_one"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"One"
>
</item>
<item
android
:
id
=
"@+id/action_two"
android
:
icon
=
"@drawable/ic_action_two"
android
:
orderInCategory
=
"1"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"Two"
>
</item>
<item
android
:
id
=
"@+id/action_one_duplicate"
android
:
icon
=
"@drawable/ic_action_one"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
title
=
"One2"
>
</item>
<item
android
:
id
=
"@+id/action_two_duplicate"
android
:
icon
=
"@drawable/ic_action_two"
android
:
orderInCategory
=
"0"
actionbarcompat_mse
:
showAsAction
=
"ifRoom"
android
:
title
=
"Two2"
>
</item>
<item
android
:
id
=
"@+id/action_settings"
android
:
orderInCategory
=
"100"
actionbarcompat_mse
:
showAsAction
=
"never"
android
:
title
=
"@string/action_settings"
>
</item>
</menu>
Comme vous le constatez, il n'y a aucun changement dans ces fichiers XML par rapport aux fichiers XML que nous utilisions pour l'ActionBarCompat.
II-B. Fichier du layout▲
Comme expliqué dans le chapitre précédent, il faut que vous déclariez votre ToolBar dans le fichier de layout de votre activité pour que celle-ci apparaisse :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
orientation
=
"vertical"
tools
:
context
=
".ActionModeActivity"
>
<include
layout
=
"@layout/my_toolbar"
></include>
<LinearLayout
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
orientation
=
"vertical"
android
:
paddingBottom
=
"@dimen/activity_vertical_margin"
android
:
paddingLeft
=
"@dimen/activity_horizontal_margin"
android
:
paddingRight
=
"@dimen/activity_horizontal_margin"
android
:
paddingTop
=
"@dimen/activity_vertical_margin"
>
où le fichier res\layout\my_toolbar.XML est :
2.
3.
4.
5.
6.
7.
<android.support.v7.widget.Toolbar
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
id
=
"@+id/toolbar"
android
:
minHeight
=
"?attr/actionBarSize"
android
:
background
=
"?attr/colorPrimary"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
>
</android.support.v7.widget.Toolbar>
II-C. Code Java▲
Comme toujours avec la ToolBar, vous devez étendre l'AppCompatActivity et définir votre ToolBar comme étant l'ActionBar de votre activité :
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.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
public
class
ActionModeActivity extends
AppCompatActivity {
/**
* The actionMode
*/
ActionMode mMode;
/**
* The action Bar
*/
private
ActionBar actionBar;
/**
* The ToolBar
*/
private
Toolbar toolbar;
/***
* The ActionMode callBack
*/
Callback actionModeCallBack;
/***
* Boolean to know which version is running
*/
private
boolean
postICS,postLollipop;
@Override
protected
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.activity_action_mode);
// Find the toolbar
toolbar =
(
Toolbar) findViewById
(
R.id.toolbar);
// Cstomize the toolbar
toolbar.setNavigationIcon
(
R.drawable.ic_action_custom_up);
postICS =
getResources
(
).getBoolean
(
R.bool.postICS);
postLollipop =
getResources
(
).getBoolean
(
R.bool.postLollipop);
if
(
postLollipop) {
toolbar.setElevation
(
15
);
}
// Define the toolbar as the ActionBar
setSupportActionBar
(
toolbar);
actionBar=
getSupportActionBar
(
);
// You could also hide the action Bar
// getSupportActionBar().hide();
// Show the Up button in the action bar.
actionBar.setDisplayUseLogoEnabled
(
false
);
actionBar.setDisplayHomeAsUpEnabled
(
true
);
setListeners
(
);
// Initialize the actionMode callback
initializeActionModeCallBack
(
);
}
Ensuite il vous faut initialiser votre ActionModeCallBack qui est le callback du cycle de vie de l'ActionMode :
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.
private
void
initializeActionModeCallBack
(
) {
actionModeCallBack =
new
android.support.v7.view.ActionMode.Callback
(
) {
@Override
public
boolean
onPrepareActionMode
(
ActionMode mode, Menu menu) {
return
false
;
}
@Override
public
void
onDestroyActionMode
(
ActionMode mode) {
}
@Override
public
boolean
onCreateActionMode
(
ActionMode mode, Menu menu) {
getMenuInflater
(
).inflate
(
R.menu.action_mode, menu);
return
true
;
}
@Override
public
boolean
onActionItemClicked
(
ActionMode mode, MenuItem item) {
Toast.makeText
(
ActionModeActivity.this
, "Got click: "
+
item, Toast.LENGTH_SHORT).show
(
);
mode.finish
(
);
return
true
;
}
}
;
}
Ce callback possède quatre méthodes :
- la méthode onCreateActionMode qui permet de créer le menu de l'ActionMode. Il suffit ainsi d'inflater le fichier XML que l'on souhaite ;
- la méthode onPrepareActionMode qui nous permet de mettre à jour le menu avant que celui-ci soit affiché ;
- la méthode onDestroyActionMode qui nous permet de faire le ménage si besoin est ;
- la méthode onActionItemClicked qui nous permet de savoir qu'un menu item du menu de l'ActionMode à été cliqué et lequel.
Il ne nous reste plus qu'à savoir activer ou désactiver ce mode :
Pour l'activation (remarquez que mMode est une variable de classe) :
mMode =
startSupportActionMode
(
actionModeCallBack);
Pour la désactivation :
mMode.finish
(
);
Et voilà, c'est tout, vous pouvez activer ou désactiver l'ActionMode quand bon vous semble maintenant.
III. Gestion du style▲
Il faut spécifiquement rajouter au style de la ToolBar les valeurs suivantes :
2.
3.
4.
5.
<!-- The Theme for the Actvity that have actionMode -->
<style
name
=
"ActionModeAppTheme"
parent
=
"AppTheme"
>
<item
name
=
"windowActionModeOverlay"
>
true</item>
<item
name
=
"actionModeBackground"
>
@color/primary_dark</item>
</style>
C'est-à-dire, je ne souhaite pas voir l'ActionMode usuel (parce que sinon il apparaît au-dessus de votre ToolBar et c'est très laid), je souhaite avoir une couleur particulière de fond pour ma ToolBar quand l'ActionMode est activé et j'utilise le Theme.Light pour surcharger mon thème actuel. D'où le style de la ToolBar qui ressemble à :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<style
name
=
"AppBlankTheme"
parent
=
"Theme.AppCompat.Light.DarkActionBar"
>
<!-- Main theme colors -->
<!-- Your app branding color for the app bar -->
<item
name
=
"colorPrimary"
>
@color/primary</item>
<!-- Darker variant for the status bar and contextual app bars -->
<item
name
=
"colorPrimaryDark"
>
@color/primary_dark</item>
<!-- Theme UI controls like checkboxes and text fields -->
<item
name
=
"colorAccent"
>
@color/accent</item>
</style>
<!-- Both themes below are those accepted to make the ToolBar works -->
<!-- Base application thème. -->
<style
name
=
"AppTheme"
parent
=
"AppBlankTheme"
>
<!-- Customize your theme here. -->
<!-- Base application thème. -->
<item
name
=
"windowNoTitle"
>
true</item>
<item
name
=
"windowActionBar"
>
false</item>
<item
name
=
"spinnerDropDownItemStyle"
>
@style/mydropdown</item>
</style>
qu'il faut appliquer à votre activité (dans votre Manifest) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<activity
android
:
name
=
".sample.ActionModeActivity"
android
:
label
=
"@string/title_activity_actionmode"
android
:
parentActivityName
=
".MainActivity"
android
:
theme
=
"@style/ActionModeAppTheme"
>
<meta-data
android
:
name
=
"android.support.PARENT_ACTIVITY"
android
:
value
=
"com.android2ee.formation.lollipop.toolbar.MainActivity"
>
</meta-data>
<intent-filter>
<action
android
:
name
=
"android.intent.action.MAIN"
/>
<category
android
:
name
=
"com.android2ee.formation.lollipop.toolbar.EXAMPLE"
/>
</intent-filter>
</activity>
IV. Remarques concernant la Support Library et l'ActionBarCompat▲
Si vous pensez que vous pouvez encore utiliser l'ActionBarCompat, vous faites une erreur, il vous faut migrer.
Pourquoi ? Pour ça :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
compileSdkVersion 21
...
defaultConfig {
applicationId "com.android2ee.formation.librairies.actionbar.compat"
minSdkVersion 9
targetSdkVersion 21
...
}
dependencies {
compile 'com.android.support:appcompat-v7:20.0.0'
}
Si vous souhaitez que l'ActionBar marche, il vous faut figer le compileSdk, le TargetSdk et la SupportLib à 21.
Mais si vous souhaitez que l'Overlay (votre contenu passe sous l'Action), que le SplitWhenNarrow ou que les ProgressBar marchent encore, vous devez figer tout ce petit monde à 20.
Ce qui veut dire qu'en fait, vous devez migrer
V. ActionView▲
Alors pour continuer à visiter l'ActionBar… euh non, la ToolBar, je vous propose d'apprendre à afficher une vue dans votre ToolBar. L'exemple que j'utilise souvent pour ça est de mettre une zone de recherche, même si ce n'est pas très pertinent, car la SearchWidget est un peu là pour ça.
Mais, ce n'est pas grave, je le fais quand même pour vous expliquer le comportement.
Tout d'abord, à quoi cela ressemble-t-il ?
Lorsque j'appuie sur l'icône (le MenuItem) "<" dans la ToolBar, celui-ci se déplie et affiche une vue avec une fléche <-, une EditText et une ImageButton qui montre une loupe. Lorsque j'appuie sur le bouton loupe, cela replie la vue et récupère la valeur contenue dans l'EditText. Dans mon exemple, je l'affiche dans un Toast.
La flèche "<-" est nativement ajoutée par le système, elle ne dépend pas de vous.
Dans les copies d'écrans ci-dessous, je vous montre le résultat sur un émulateur avec GingerBread.
Ce principe est le principe de base de l'ActionView : il remplace une icône de la ToolBar par un layout.
V-A. Le fichier de Menu▲
Commençons par examiner le fichier de menu (car c'est lui qui porte l'information principale) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<menu
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
actionbarcompat_mse
=
"http://schemas.android.com/apk/res-auto"
>
<item
android
:
id
=
"@+id/menu_item_actionview"
actionbarcompat_mse
:
actionLayout
=
"@layout/actionview_view"
actionbarcompat_mse
:
showAsAction
=
"always|collapseActionView"
android
:
icon
=
"@drawable/ic_action_provider_extends"
android
:
title
=
"ActionView"
>
</item>
<item
android
:
id
=
"@+id/action_one"
actionbarcompat_mse
:
showAsAction
=
"always"
android
:
icon
=
"@drawable/ic_action_one"
android
:
orderInCategory
=
"0"
android
:
title
=
"One"
>
</item>
</menu>
Comme d'habitude, je définis mon propre namespace XML pour que le système ne zappe pas purement et simplement les balises de la Support Library et je l'utilise pour ajouter deux propriétés à mon MenuItem menu_item_actionview qui sont :
- actionLayout qui me permet de définir quel est le layout qui sera affiché à la place de l'item lorsque celui-ci sera déplié ;
- showAsAction qui me permet de définir le comportement de mon MenuItem, à savoir, toujours visible et au démarrage affiche l'item et non pas le layout.
V-B. Les fichiers de layout ▲
Le layout qui remplacera le MenuItem est un layout rien de plus normal. Il faut quand même faire attention à un truc important, ce n'est pas lui qui définit la taille de la ToolBar. C'est-à-dire que s'il doit s'afficher avec 128 dp de haut, il vous faudra changer la hauteur de la ToolBar vous-même lors de l'inflation du menu (quand on affichera le layout) dans le code Java (n'oubliez pas de rétablir cette taille lors du collapse du layout).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
layout_gravity
=
"center"
android
:
focusable
=
"true"
>
<EditText
android
:
id
=
"@+id/edtActionView"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
ems
=
"12"
android
:
hint
=
"I am an EditText but I could be whatever"
android
:
textSize
=
"12sp"
>
</EditText>
<ImageButton
android
:
id
=
"@+id/btnActionView"
android
:
layout_width
=
"32dip"
android
:
layout_height
=
"32dip"
android
:
layout_gravity
=
"center"
android
:
adjustViewBounds
=
"true"
android
:
background
=
"@drawable/ic_action_search"
android
:
scaleType
=
"fitCenter"
>
</ImageButton>
</LinearLayout>
Le fichier de layout de l'activité est toujours le même, en particulier, il n'oublie pas de déclarer la ToolBar :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<RelativeLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
tools
:
context
=
"com.android2ee.formation.lollipop.toolbar.sample.ActionViewActivity"
>
<include
layout
=
"@layout/my_toolbar"
/>
<TextView
android
:
text
=
"@string/hello_world"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
/>
</RelativeLayout>
2.
3.
4.
5.
6.
7.
<android.support.v7.widget.Toolbar
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
android
:
id
=
"@+id/toolbar"
android
:
minHeight
=
"?attr/actionBarSize"
android
:
background
=
"?attr/colorPrimary"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
>
</android.support.v7.widget.Toolbar>
V-C. Le code Java▲
Le code Java de l'activité est assez simple. Il y a trois étapes auxquelles il faut faire attention :
- instancier la ToolBar dans la méthode onCreate ;
- récupérer les pointeurs vers les composants graphiques qui seront affichés dans la ToolBar quand l'ActionView s'affiche (pour les mettre à jour, rajouter des listeners…) ;
- écouter dans le onOptionItemSelected, le menuItem de votre ActionView et le laisser être traité par le système.
Ainsi, comme d'habitude, dans la méthode onCreate, on instancie la ToolBar :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
protected
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.activity_action_view);
// Find the toolbar
toolbar =
(
Toolbar) findViewById
(
R.id.toolbar);
postICS =
getResources
(
).getBoolean
(
R.bool.postICS);
postLollipop =
getResources
(
).getBoolean
(
R.bool.postLollipop);
if
(
postLollipop) {
toolbar.setElevation
(
15
);
}
// Define the toolbar as the ActionBar
setSupportActionBar
(
toolbar);
actionBar=
getSupportActionBar
(
);
}
Ensuite dans la onCreateOptionsMenu, on instancie le menu et on met en place l'ActionView, en particulier on récupère les pointeurs vers les composants graphiques qui nous intéressent, on leur rajoute des listeners au besoin :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
public
boolean
onCreateOptionsMenu
(
Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater
(
).inflate
(
R.menu.menu_action_view, menu);
// Find your menuItem that handles your actionView
menuItemActionView =
menu.findItem
(
R.id.menu_item_actionview);
// Find the root layout encapsulated by the MenuItem
lilActionView =
(
LinearLayout) MenuItemCompat.getActionView
(
menuItemActionView);
// Then find your graphical elements
edtActionView =
(
EditText) lilActionView.findViewById
(
R.id.edtActionView);
btnActionView =
(
ImageButton) lilActionView.findViewById
(
R.id.btnActionView);
btnActionView.setOnClickListener
(
new
View.OnClickListener
(
) {
public
void
onClick
(
View v) {
actionOfTheActionView
(
);
}
}
);
Où la méthode actionOfTheActionView est assez triviale, elle affiche un Toast et ferme l'ActionView :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
/**
* Handling Action from the btnActionView contained by the ActionView
*/
private
void
actionOfTheActionView
(
) {
Log.e
(
"ActionViewActivity"
, "ActionView edt "
+
edtActionView.getText
(
).toString
(
));
Toast.makeText
(
this
, "ActionView edt = "
+
edtActionView.getText
(
).toString
(
), Toast.LENGTH_SHORT).show
(
);
MenuItemCompat.collapseActionView
(
menuItemActionView);
// What ever is the version, hide the keyboard:
InputMethodManager imm =
(
InputMethodManager)getSystemService
(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow
(
edtActionView.getWindowToken
(
), 0
);
}
Et enfin, dans la méthode onOptionItemSelected, on ne gère pas le MenuItem qui porte l'ActionView (mais on gère le up, ce qui n'a rien à voir avec notre exemple) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
public
boolean
onOptionsItemSelected
(
MenuItem item) {
switch
(
item.getItemId
(
)) {
case
android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask
(
this
);
return
true
;
case
R.id.menu_item_actionview:
// Because the system expands the action view when the user selects the action, you do
// not need to respond to the item in the onOptionsItemSelected() callback. The system
// still calls onOptionsItemSelected(), but if you return true (indicating you've
// handled the event instead), then the action view will not expand.
Log.e
(
"ActionViewActivity"
, "menu_item_actionview get"
);
return
false
;
}
return
super
.onOptionsItemSelected
(
item);
}
Et voilà, c'est tout, en fait, ce n'était pas compliqué.
Ah oui, j'oubliais, dans les tutoriels, les gars qui oublient de déclarer leurs attributs de classes, c'est assez embêtant car tu ne sais jamais ce qu'il a déclaré (je vous mets aussi les imports qui utilisent la Support Library, pas les autres) :
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.
import
android.support.v4.app.NavUtils;
import
android.support.v4.view.MenuItemCompat;
import
android.support.v7.app.ActionBar;
import
android.support.v7.app.AppCompatActivity;
import
android.support.v7.widget.Toolbar;
public
class
ActionViewActivity extends
AppCompatActivity {
// The action Bar
private
ActionBar actionBar;
// The ToolBar
private
Toolbar toolbar;
// The menuItem that handles the ActionView
MenuItem menuItemActionView;
// The root Layout of the View of the ActionView
LinearLayout lilActionView;
// The EditText to listen for the user input
EditText edtActionView;
// The searchButton
ImageButton btnActionView;
// Boolean to know which version is running
private
boolean
postICS,postLollipop;
}
V-D. Ajouter des animations▲
J'avais envie d'ajouter quelques animations quand l'ActionView s'affiche pour les versions sorties depuis HoneyComb (nommée également Android 3). J'aurais pu le faire pour les versions précédentes, mais quand je l'ai fait, j'ai trouvé ça trop laid. Parfois, il faut savoir s'abstenir.
Donc, pour ça, la première chose à faire est de savoir sur quelle version on se trouve. Et comme je n'aime pas le BuildConfig et que je préfère les booléens, je me fais mon petit fichier de versions.
2.
3.
4.
5.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool
name
=
"postICS"
>
false</bool>
<bool
name
=
"postLollipop"
>
false</bool>
</resources>
2.
3.
4.
5.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool
name
=
"postICS"
>
true</bool>
<bool
name
=
"postLollipop"
>
false</bool>
</resources>
2.
3.
4.
5.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool
name
=
"postICS"
>
true</bool>
<bool
name
=
"postLollipop"
>
true</bool>
</resources>
Et donc, dans mon code Java, je fais :
2.
postICS =
getResources
(
).getBoolean
(
R.bool.postICS);
postLollipop =
getResources
(
).getBoolean
(
R.bool.postLollipop);
Et voilà, j'ai mes beaux booléens. Je n'ai pas inventé cette technique, j'ai regardé la conférence suivante : https://www.youtube.com/watch?v=amZM8oZBgfk (GoogleIo 2012, MultiVersionning par Bruno Oliveira, Adam Powell). [Alors surtout dans cette conférence ils vous disent d'utiliser le parrallel activity pattern, NE LE FAITES PAS, avec les fragments, utilisez toujours la Support Library si vous codez pour Gingerbread. Pas de duplication de code, surtout pas de duplication de code.]
Et maintenant, à moi les animations. Pour ce faire, je souhaite les lancer :
- quand l'ActionView s'affiche ;
- quand j'appuie sur l'ImageButton search (la loupe).
Ainsi, je rajoute un listener pour écouter l'ouverture de l'ActionView au moment de la création du menu :
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.
@Override
public
boolean
onCreateOptionsMenu
(
Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater
(
).inflate
(
R.menu.menu_action_view, menu);
// Find your menuItem that handles your actionView
menuItemActionView =
menu.findItem
(
R.id.menu_item_actionview);
// Find the root layout encapsulated by the MenuItem
lilActionView =
(
LinearLayout) MenuItemCompat.getActionView
(
menuItemActionView);
// Then find your graphical elements
edtActionView =
(
EditText) lilActionView.findViewById
(
R.id.edtActionView);
btnActionView =
(
ImageButton) lilActionView.findViewById
(
R.id.btnActionView);
btnActionView.setOnClickListener
(
new
View.OnClickListener
(
) {
public
void
onClick
(
View v) {
actionOfTheActionView
(
);
}
}
);
// When using the support library, the setOnActionExpandListener() method is
// static and accepts the MenuItem object as an argument
MenuItemCompat.setOnActionExpandListener
(
menuItemActionView, new
MenuItemCompat.OnActionExpandListener
(
) {
@Override
public
boolean
onMenuItemActionCollapse
(
MenuItem item) {
// Do something when collapsed, it's too late for animation
Log.e
(
"ActionViewActivity"
, "menu_item_actionview collapsing"
);
return
true
; // Return true to collapse action view
}
@SuppressLint
(
"NewApi"
)
@Override
public
boolean
onMenuItemActionExpand
(
MenuItem item) {
//the first time, elements are not expanded, It's too soon to have their size
if
(
postICS) {
btnActionView.animate
(
).rotationBy
(
360
f).alpha
(
1
f).setDuration
(
300
);
edtActionView.animate
(
).rotationBy
(
360
f).alpha
(
1
f).y
(
0
).setDuration
(
300
);
}
// Do something when expanded
return
true
; // Return true to expand action view
}
}
);
return
super
.onCreateOptionsMenu
(
menu);
}
Et quand l'ActionView est affichée, j'anime mon ImageButton de loupe et mon EditText en les faisant tourner et bouger.
L'autre lancement s'effectue sur le clic de l'ImageButton :
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.
46.
/**
* Handling Action from the btnActionView contained by the ActionView
*/
@SuppressLint
(
"NewApi"
)
private
void
actionOfTheActionView
(
) {
Log.e
(
"ActionViewActivity"
, "ActionView edt "
+
edtActionView.getText
(
).toString
(
));
Toast.makeText
(
this
, "ActionView edt = "
+
edtActionView.getText
(
).toString
(
), Toast.LENGTH_SHORT).show
(
);
collapsing=
true
;
if
(
postICS) {
// This is called when the user press the search icon, so the
edtActionView.animate
(
).alpha
(
0
).rotationBy
(-
360
f).setDuration
(
300
);
btnActionView.animate
(
).alpha
(
0
).rotationBy
(-
360
f).setDuration
(
300
).setListener
(
new
Animator.AnimatorListener
(
) {
// The listener is set for all the animations (further animations)
@Override
public
void
onAnimationStart
(
Animator animation) {
}
@Override
public
void
onAnimationEnd
(
Animator animation) {
if
(
collapsing) {
MenuItemCompat.collapseActionView
(
menuItemActionView);
collapsing =
false
;
}
}
@Override
public
void
onAnimationCancel
(
Animator animation) {
if
(
collapsing) {
MenuItemCompat.collapseActionView
(
menuItemActionView);
collapsing =
false
;
}
}
@Override
public
void
onAnimationRepeat
(
Animator animation) {
}
}
);
}
else
{
MenuItemCompat.collapseActionView
(
menuItemActionView);
collapsing =
false
;
}
// What ever is the version, hide the keyboard:
InputMethodManager imm =
(
InputMethodManager)getSystemService
(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow
(
edtActionView.getWindowToken
(
), 0
);
}
Et là, je fais pareil, j'anime mon ImageButton et mon EditText. Mais surtout, ce qui est important ici, c'est que j'écoute l'animation afin de, quand elle se termine, mettre à jour mon IHM, à savoir fermer l'ActionView.
Ce qui complexifie un peu quand même ma méthode, mais est nettement plus agréable pour mon utilisateur.
VI. ToolBar et TabLayout▲
Pour finir cette série sur la ToolBar, il ne nous reste plus qu'à mettre en place la ToolBar avec une ligne d'onglets et/ou avec un ViewPager.
Commençons par la ligne d'onglets.
Nous voulons simplement avoir une activité avec des onglets et la ToolBar :
Pour ajouter cette ligne d'onglets, rien de plus facile, il faut tout d'abord définir en XML sa ligne d'onglets et construire ceux-ci côté Java. Aucune surprise ne vous attend. Nous utilisons uniquement les composants de la DesignLibrary pour la mise en place des onglets et tout se déroule alors facilement.
Il y a deux paramètres que nous pouvons modifier pour le comportement de la ToolBar : la notion de gravité et la notion de mode.
La gravité possède deux valeurs Center ou Fill. Center rassemble les onglets au centre de l'écran en mode wrapContent alors que Fill répartit uniformément l'espace disponible pour chaque onglet.
Le mode permet simplement de dire si vous souhaitez que vos onglets soient déroulants ou pas. S'ils ne sont pas déroulants, ils s'affichent tous dans l'espace disponible, ce qui veut dire qu'ils sont compressés et peuvent ne pas avoir la place de s'afficher complètement.
VI-A. Le fichier de Layout▲
Commençons par examiner le fichier de layout :
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.
46.
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
orientation
=
"vertical"
tools
:
context
=
".ActivityWithTabsNav"
>
<android.support.v7.widget.Toolbar
android
:
id
=
"@+id/toolbar"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
android
:
background
=
"?attr/colorPrimary"
android
:
minHeight
=
"?attr/actionBarSize"
>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android
:
id
=
"@+id/tabLayout"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
android
:
background
=
"#FF00FF"
android
:
fillViewport
=
"true"
>
</android.support.design.widget.TabLayout>
<TextView
android
:
id
=
"@+id/txvState"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/hello_world"
>
</TextView>
<Button
android
:
id
=
"@+id/btnChangeMode"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/btnChangeTabMode"
>
</Button>
<Button
android
:
id
=
"@+id/btnChangeGravity"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/btnChangeGravity"
>
</Button>
<Button
android
:
id
=
"@+id/btnAddTab"
android
:
layout_width
=
"wrap_content"
android
:
layout_height
=
"wrap_content"
android
:
text
=
"@string/btnAddTab"
>
</Button>
</LinearLayout>
Il n'y a pas grand-chose à dire, si ce n'est que TabLayout appartient à la DesignLibrary et la ToolBar au niveau de la version 7.
VI-B. Le code Java▲
Le code Java de l'activité est assez simple. Il y a trois étapes auxquelles il faut faire attention :
- instancier la ToolBar et le TabLayout ;
- construire les onglets du TabLayout ;
- ajouter un listener au TabLayout pour être averti des changements d'onglets.
Tout se fait dans la méthode onCreate de votre activité (ou dans la méthode onCreateView de votre fragment).
Ainsi, comme d'habitude, dans la méthode onCreate, on instancie la ToolBar :
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.
protected
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
// Set your view
setContentView
(
R.layout.activity_activity_with_tabs_nav);
// Find the Toolbar
Toolbar toolbar =
(
Toolbar) findViewById
(
R.id.toolbar);
// Use it as your action bar
setSupportActionBar
(
toolbar);
// Add subtitle
getSupportActionBar
(
).setSubtitle
(
"Using ToolBar"
);
// Find the Tab Layout
tabLayout =
(
TabLayout) findViewById
(
R.id.tabLayout);
// Define its gravity and its mode
tabLayout.setTabGravity
(
TabLayout.GRAVITY_FILL);
tabLayout.setTabMode
(
TabLayout.MODE_FIXED);
// Define the color to use (depending on the state a different color should be disaplyed)
// Works only if done before adding tabs
tabLayout.setTabTextColors
(
getResources
(
).getColorStateList
(
R.color.tab_selector_color));
// Populate your TabLayout
tabLayout.addTab
(
tabLayout.newTab
(
).setText
(
"Tab 1"
).setIcon
(
R.drawable.ic_tab));
tabLayout.addTab
(
tabLayout.newTab
(
).setText
(
"Tab 2"
));
tabLayout.addTab
(
tabLayout.newTab
(
).setText
(
"Tab 3"
));
// Add a listener
tabLayout.setOnTabSelectedListener
(
new
TabLayout.OnTabSelectedListener
(
) {
@Override
public
void
onTabSelected
(
TabLayout.Tab tab) {
/*do your navigation*/
}
@Override
public
void
onTabUnselected
(
TabLayout.Tab tab) {
/*do nothing*/
}
@Override
public
void
onTabReselected
(
TabLayout.Tab tab) {
/*do nothing*/
}
}
);
...
}
Comme d'habitude, on récupère la ToolBar et on l'affecte à l'activité comme étant son ActionBar.
Puis on s'occupe du TabLayout. Ici, il n'y a plus besoin d'appeler la méthode setup sur le TabLayout, l'initialisation est automatique. Il vous suffit de le récupérer (findViewById), de lui affecter son mode et sa gravité, puis de lui ajouter ses onglets via la méthode addTab, en fournissant pour chaque onglet un texte ainsi qu'une icône optionnelle.
Enfin, il ne vous reste plus qu'à ajouter le TabListener pour interagir avec l'utilisateur et changer le fragment affiché dans l'onglet. Vous pouvez aussi mettre des vues en utilisant la méthode addView(View view, int index, LayoutParameter layoutParam).
Mais attention, la philosophie à changé, il n'y a plus besoin que votre TabLayout contienne les vues pour chaque onglet. Il n'est qu'un conteneur d'onglets ; uniquement une ligne qui affiche les boutons, à vous de gérer la mise à jour de la vue / du fragment sous cette ligne. Je pense que c'est la bonne manière d'aborder ce composant avec les fragments en utilisant ces derniers de manière dynamique.
Et voilà, c'est tout, en fait, ce n'était pas compliqué.
Ah oui, j'oubliais, les imports utilisés pour cette activité sont :
2.
3.
import
android.support.design.widget.TabLayout;
import
android.support.v7.app.AppCompatActivity;
import
android.support.v7.widget.Toolbar;
VI-C. Gestion des couleurs des onglets▲
Une dernière précision sur l'affectation des couleurs à vos boutons d'onglets. Pour effectuer cela, on s'appuie sur un fichier de couleurs un peu particulier, un SelectorColor (identique à un SelectorDrawable dans l'idée). Un SelectorColor est une couleur qui en fonction de l'état du composant (enabled/selected/hover…) possède une valeur spécifique :
2.
3.
4.
5.
<selector
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
>
<item
android
:
state_enabled
=
"false"
android
:
color
=
"@color/testcolor2"
/>
<item
android
:
state_selected
=
"true"
android
:
color
=
"@color/testcolor3"
/>
<item
android
:
color
=
"@color/testcolor5"
/>
<selector>
Il ne reste plus qu'à affecter cette couleur à nos onglets qui automatiquement (s'ils sont sélectionnés ou pas) s'afficheront de manière différente (avec une couleur différente).
tabLayout.setTabTextColors
(
getResources
(
).getColorStateList
(
R.color.tab_selector_color));
Vous devez affecter les couleurs à vos onglets AVANT d'ajouter des boutons à votre ligne d'onglets (bref avant d'appeler la méthode addTab).
VII. ToolBar et ViewPager▲
Ces composants sont faits pour simplement pour fonctionner ensemble. Il suffit de déclarer la TabLayout, le ViewPager et la ToolBar dans votre fichier de layout, puis côté Java, d'ajouter la TabLayout comme précédemment et de lier le ViewPager au TabLayout.
VII-A. Le fichier de Layout▲
Commençons par examiner le fichier de layout :
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.
<!--XML version="1.0" encoding="utf-8"-->
<LinearLayout
xmlns
:
android
=
"http://schemas.android.com/apk/res/android"
xmlns
:
tools
=
"http://schemas.android.com/tools"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"match_parent"
android
:
orientation
=
"vertical"
tools
:
context
=
"com.android2ee.formation.lollipop.toolbar.tabsnav.ActivityTabsNavViewPager"
>
<android.support.v7.widget.Toolbar
android
:
id
=
"@+id/toolbar"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
android
:
background
=
"?attr/colorPrimary"
android
:
minHeight
=
"?attr/actionBarSize"
>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android
:
id
=
"@+id/tabLayout"
android
:
layout_width
=
"match_parent"
android
:
layout_height
=
"wrap_content"
android
:
background
=
"#FF00FF"
android
:
fillViewport
=
"true"
>
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android
:
layout_width
=
"fill_parent"
android
:
layout_height
=
"fill_parent"
android
:
id
=
"@+id/viewpager"
android
:
background
=
"#FF00F0F0"
android
:
fillViewport
=
"true"
>
</android.support.v4.view.ViewPager>
</LinearLayout>
Il n'y a pas grand-chose à dire, si ce n'est que TabLayout appartient à la DesignLibrary, la ToolBar au support V7 et le ViewPager au support V4 (inclus dans le support V7).
VII-B. Le code Java▲
Le code Java de l'activité est assez simple. Il y a quatre étapes auxquelles il faut faire attention :
- instancier la ToolBar, le ViewPager et le TabLayout ;
- construire les onglets du TabLayout ;
- ajouter un listener au TabLayout pour être averti des changements d'onglets (ce n'est ici pas obligatoire, je le conseille pour la restauration de l'onglet courant quand l'utilisateur revient dans votre activité) ;
- lier le ViewPager au TabLayout.
Tout se fait dans la méthode onCreate de votre activité (ou dans la méthode onCreateView de votre fragment).
Ainsi, comme d'habitude, dans la méthode onCreate, on instancie la ToolBar :
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.
46.
@Override
protected
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.activity_tabs_nav_view_pager);
// Find the Toolbar
Toolbar toolbar =
(
Toolbar) findViewById
(
R.id.toolbar);
// Use it as your action bar
setSupportActionBar
(
toolbar);
// Add subtitle
getSupportActionBar
(
).setSubtitle
(
"Using ToolBar"
);
// Find the Tab Layout
tabLayout =
(
TabLayout) findViewById
(
R.id.tabLayout);
// Define its gravity and its mode
tabLayout.setTabGravity
(
TabLayout.GRAVITY_CENTER);
tabLayout.setTabMode
(
TabLayout.MODE_SCROLLABLE);
// Instanciate the PageAdapter
pagerAdapter=
new
MyPagerAdapter
(
this
);
// Define the color to use (depending on the state a different color should be disaplyed)
// Works only if done before adding tabs
tabLayout.setTabTextColors
(
getResources
(
).getColorStateList
(
R.color.tab_selector_color));
// Find the viewPager
viewPager =
(
ViewPager) super
.findViewById
(
R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter
(
pagerAdapter);
viewPager.setClipToPadding
(
false
);
viewPager.setPageMargin
(
12
);
// Add animation when the page are swiped
// this instanciation only works with honeyComb and more
// if you want it all version use AnimatorProxy of the nineoldAndroid lib
// @see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
if
(
Build.VERSION.SDK_INT>=
Build.VERSION_CODES.HONEYCOMB){
viewPager.setPageTransformer
(
true
, new
MyPageTransformer
(
this
));
}
// AND CLUE TABLAYOUT AND VIEWPAGER
tabLayout.setupWithViewPager
(
viewPager);
}
Comme d'habitude, on récupère la ToolBar et on l'affecte à l'activité comme étant son ActionBar.
Puis on s'occupe du TabLayout (voir paragraphe précédent).
Enfin, il ne vous reste plus qu'à ajouter le TabListener pour vous souvenir de l'onglet dans lequel est l'utilisateur. Ainsi, quand l'utilisateur revient dans votre activité, si elle est encore dans la liste des applications récentes (dans votre LRUCach pour être exact), vous pouvez alors le replacer dans l'onglet qu'il vient de quitter.
Enfin, pour rappel, le code du ViewPager est le suivant :
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.
46.
47.
48.
49.
50.
51.
public
class
MyPagerAdapter extends
FragmentPagerAdapter {
// The list of ordered fragments
private
final
ArrayList fragments;
/***
* The constructor
*
*
@param
ctx
* The Context
*/
public
MyPagerAdapter
(
ActivityTabsNavViewPager ctx) {
super
(
ctx.getSupportFragmentManager
(
));
fragments =
new
ArrayList
(
);
// A stuff I never did before, instanciate my fragment
Fragment frag =
new
MyFragment1
(
);
fragments.add
(
frag);
frag =
new
MyFragment2
(
);
fragments.add
(
frag);
frag =
new
MyFragment3
(
);
fragments.add
(
frag);
frag =
new
MyFragment4
(
);
fragments.add
(
frag);
frag =
new
MyFragment5
(
);
fragments.add
(
frag);
}
/**
* This method may be called by the ViewPager to obtain a title string to
* describe the specified page. This method may return null indicating no
* title for this page. The default implementation returns null.
*
*
@param
position
* The position of the title requested
*
@return
A title for the requested page
*/
@Override
public
CharSequence getPageTitle
(
int
position) {
return
"This is the Page "
+
position;
}
@Override
public
Fragment getItem
(
int
position) {
return
fragments.get
(
position);
}
@Override
public
int
getCount
(
) {
return
5
;
}
}
Ah oui, j'oubliais, les imports utilisés pour cette activité sont :
2.
3.
4.
import
android.support.design.widget.TabLayout;
import
android.support.v4.view.ViewPager;
import
android.support.v7.app.AppCompatActivity;
import
android.support.v7.widget.Toolbar;
VIII. Conclusion et remerciements▲
Je finis cet article, concernant la ToolBar, en remerciant Chris Banes (@chrisbanes), car c'est à lui que l'on doit cette simplification et surtout cette libération de l'ActionBar,
so Thanks a billion Mr Banes.
Nous tenons à remercier Jacques Jean pour la relecture de cet article puis milkoseck et Mickael Baron pour la mise au gabarit.
IX. Le tutoriel▲
Comme tous mes articles, celui-ci est associé à un code source, vous le trouverez ici :https://github.com/MathiasSeguy-Android2EE/ToolBar4Github.