1. Les listes▲
Sous Android, une liste représente un ensemble d'éléments s'affichant les uns à la suite des autres.
Chaque élément d'une liste peut posséder de une à trois lignes maximum et chaque ligne d'une liste peut être personnalisée avec différents composants (TextView, Button, ImageView…).
- Soit hériter de la classe ListActivity.
- Soit hériter de la classe Activity.
Pour insérer des données dans une liste, on utilise un adapter(adaptateur). Il permet de lier des données à une vue qui étend de la classe AdapterView, ainsi il est facile d'accéder aux données st ockées (lire, ajouter, supprimer, modifier…) dans une vue. Android propose deux types d'adapters:
– ArrayAdapter: permet de remplir une liste à partir d'un tableau ou d'une collection.
– SimpleCursorAdapter: permet de remplir une liste à partir d'une base de données.
Vous pouvez aussi créer votre propre adapter en héritant simplement de la classe BaseAdapter ou d'un adapter déjà existant.
1-1. Création d'une liste▲
1-1-1. ListActivity▲
La première méthode consiste à créer une liste héritant de la classe ListActivity.
Pour commencer, créez un fichier XML représentant une vue contenant uniquement une liste (dossier layout).
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
>
<
ListView
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
id
=
"
@android:id/list
"
/
>
<
/
LinearLayout
>
Pour pouvoir manipuler une ListView grâce à une ListActivity, votre liste doit absolument avoir pour identifiant @android:id/list.
Maintenant, déclarez une activité qui hérite de la classe ListActivity.
public
class
ListActivityExampleActivity extends
ListActivity {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.main);
}
}
Pour l'instant la liste est vide et ne co ntient aucune donnée. Nous allons utiliser un tableau de chaînes de caractères comme source de données.
private
String[] androidVersion =
{
"
Cupcake
"
, "
Donut
"
, "
Eclair
"
,
"
Froyo
"
, "
Gingerbread
"
, "
Honeycomb
"
, "
Ice
Cream
Sandwich
"
"
Jelly
Bean
"
}
;
Créez un ArrayAdapter afin d'injecter dans la liste les données provenant du tableau déclaré précédemment.
ArrayAdapter adapter =
new
ArrayAdapter
(this
,
android.R.layout.simple_list_item_1,
androidVersion);
- Le premier paramètre représente le contexte de l'activité courante.
- Le deuxième paramètre représente le layout qui sera appliqué à chaque ligne de la liste. Vous pouvez soit créer un layout personnalisé, soit utiliser ceux fournis par Android.
- Le troisième paramètre représente le tableau de données à insérer dans la liste.
La dernière étape consiste à lier l'adapter contenant les données à injecter à la liste et cela à l'aide de la méthode setListAdapter:
SetListAdapter
(adapter);
Maintenant, vous pouvez tester cet exemple et vous devriez obtenir le résultat suivant:
1-1-2. ListView▲
La seconde manière de créer une liste est d'utiliser une simple activité.
Pour cela, il faut créer le fichier XML représentant la liste.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
>
<
ListView
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
id
=
"
@+id/myList
"
/
>
<
/
LinearLayout
>
Remarque:
Cette méthode offre la possibilité de spécifier l'identifiant de votre choix.
Modifiez l'activité afin de récupérer l'instance de la liste (à l'aide de la méthode findViewById).
ListView myList =
(ListView) findViewById
(R.id.myList);
Comme précédemment, l'exemple utilise un tableau de chaînes de caractères ainsi qu'un ArrayAdapter.
Pour finir, il faut associer l'adapter à la liste.
myList.setAdapter
(adapter);
Remarque:
L'utilisation d'une ListActivity est conseillée si votre vue contient uniquement une liste.
1-1-3. Adapter et liste personnalisée▲
Vous pouvez aussi créer des adapters personnalisés afin de mieux gérer l'affi-chage et les données d'une liste.
Voici un exemple permettant d'afficher une liste contenant le nom et le numéro des différentes versions d'Android et cela, à l'aide d'un adapter personnalisé.
Pour commencer, créez une classe AndroidVersion représentant tout simplement une version d'Android.
public
class
AndroidVersion {
private
String versionName;
private
String versionNumber;
public
String getVersionName
() {
return
versionName;
}
public
void
setVersionName
(String versionName) {
this
.versionName =
versionName;
}
public
String getVersionNumber
() {
return
versionNumber;
}
public
void
setVersionNumber
(String versionNumber) {
this
.versionNumber =
versionNumber;
}
}
L'exemple utilise un adapter personnalisé héritant de la classe ArrayAdapter(car il utilise un tableau pour in jecter des données dans la liste).
Pour cela, créez d'abord un fichier qui représentera la liste.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
>
<
ListView
android
:
id
=
"
@+id/myList
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
/
>
<
/
LinearLayout
>
Afin de personnaliser la liste et la rendre plus riche et plus agréable, il faut créer un layout personnalisé qui servira à spécifier l'interface correspondant à chaque ligne de la liste.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
?android:attr/listPreferredItemHeight
"
android
:
padding
=
"
6dip
"
>
<
ImageView
android
:
id
=
"
@+id/icon
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
wrap_content
"
android
:
layout_marginRight
=
"
6dp
"
android
:
src
=
"
@drawable/list_icon
"
/
>
<
LinearLayout
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
>
<
TextView
android
:
id
=
"
@+id/title
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
gravity
=
"
center_vertical
"
/
>
<
TextView
android
:
id
=
"
@+id/description
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
/
>
<
/
LinearLayout
>
<
/
LinearLayout
>
Chaque ligne de la liste est donc composée des éléments suivants:
–Un LinearLayout horizontal.
- L'image se situe dans le dossier drawable.
- L'image possède un espacement extérieur ( margin) droit de 6dp .
– Un second LinearLayout (vertical) utilisé pour afficher les deux zones de textes (titre et description).
– Un premier texte qui indiqu e le titre de la ligne.
– Un second texte qui indique la description de la ligne.
Maintenant, créez une classe représentant l'adapter personnalisé. Il possédera les spécificités suivantes:
– Il hérite de la classe ArrayAdapter car le remplissage des données sera ef-fectué à l'aide d'un tableau.
– Chaque élément de la liste représente une version d'Android.
- Un constructeur.
- Une méthode getView: chaque appel à cette méthode permet de récu-pérer une ligne (donnée et vue) de la liste se trouvant à une position donnée.
public
class
AndroidAdapter extends
ArrayAdapter<
AndroidVersion>
{
ArrayList<
AndroidVersion>
androidVer;
int
viewRes;
public
AndroidAdapter
(Context context, int
textViewResourceId,
ArrayList<
AndroidVersion>
versions) {
super
(context, textViewResourceId, versions);
this
.androidVer =
versions;
this
.context =
context;
this
.viewRes =
textViewResourceId;
}
@
Override
public
View getView
(int
position, View convertView, ViewGroup
parent) {
View v =
convertView;
if
(v =
=
null
) {
LayoutInflater vi =
(LayoutInflater)
context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
v =
vi.inflate
(viewRes, parent, false
);
}
AndroidVersion o =
androidVer.get
(position);
if
(o !
=
null
) {
TextView tt =
(TextView) v.findViewById
(R.id.title);
TextView bt =
(TextView)
v.findViewById
(R.id.description);
if
(tt !
=
null
) {
tt.setText
("
Nom
de
la
version
:
"
+
o.getVersionName
());
}
if
(bt !
=
null
) {
bt.setText
("
Numéro
de
la
version
:
"
+
o.getVersionNumber
());
}
}
return
v;
}
}
- Le contexte de la vue.
- Le layout correspondant à la vue personnalisée, appliqué à chaque ligne de la liste.
- Le tableau représentant les données à insérer dans la liste.
- Cette méthode permet de récupérer la vue à appliquer à une ligne donnée (argument position).
- La vue représentant la liste est passée en argument à la méthode (argument convertView).
- La vue parente est passée en argument (argument parent).
- La vue personnalisée doit être chargée à l'aide de la méthode inflate. Une fois chargée, elle sera passée en paramètre à la méthode getView(paramètre convertView) lors des prochains appels, ce qui réduit le nombre d'appels à la méthode inflate (appel assez coûteux).
- Puis il faut récupérer le texte correspondant à la ligne qu'on souhaite afficher.
- Enfin, il faut récupérer les deux TextView(titre et description d'une ligne) afin de les remplir avec les données récupérées dans l'étape précédente.
Vous pouvez aller plus loin dans l'optimisation d'une liste et cela à l'aide du système de holder.
Pour finir, créez l'activité qui permet d'initialiser la vue, l'adapter personnalisé et les données.
public
class
CustomAdapterExampleActivity extends
Activity {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.main);
ArrayList<
AndroidVersion>
androidList =
new
ArrayList<
AndroidVersion>
();
initList
(androidList);
AndroidAdapter adapter =
new
AndroidAdapter
(this
,
R.layout.list_layout, androidList);
final
ListView list =
(ListView)
findViewById
(R.id.myList);
list.setAdapter
(adapter);
}
private
void
initList
(ArrayList<
AndroidVersion>
androidList)
{
AndroidVersion version =
new
AndroidVersion
();
version.setVersionName
("
Cupcake
"
);
version.setVersionNumber
("
1.5
"
);
androidList.add
(version);
AndroidVersion versionDonut =
new
AndroidVersion
();
versionDonut.setVersionName
("
Donut
"
);
versionDonut.setVersionNumber
("
1.6
"
);
androidList.add
(versionDonut);
AndroidVersion versionEclair =
new
AndroidVersion
();
versionEclair.setVersionName
("
Eclair
"
);
versionEclair.setVersionNumber
("
2.0.x
"
);
androidList.add
(versionEclair);
AndroidVersion versionFroyo =
new
AndroidVersion
();
versionFroyo.setVersionName
("
Froyo
"
);
versionFroyo.setVersionNumber
("
2.2.x
"
);
androidList.add
(versionFroyo);
.........
}
}
- D'initialiser la liste des données (méthode initList).
- D'initialiser l'adapter en lui passant les arguments nécessaires à son bon fonctionnement (contexte, vue et données).
- Récupérer la liste et la lier à l'adapter.
Ce qui donnera le résultat suivant:
Gestion du clic sur une liste
Pour gérer le clic sur les différents éléments d'une liste, utilisez la méthode setOnItemClickListener sur l'instance de la liste cible.
list.setOnItemClickListener
(new
OnItemClickListener
() {
@
Override
public
void
onItemClick
(AdapterView<
?>
adapter, View v, int
position, long
id) {
AndroidVersion selectedItem =
(AndroidVersion)
adapter.getItemAtPosition
(position);
Log.v
("
CustomAdapterExemple
"
, "
Element
selectionne
:
"
+
selectedItem.getVersionName
());
}
}
);
- L'adapter sur lequel le clic est survenu.
- La vue sur laquelle le clic est survenu.
- La position du clic dans l'adapter.
- L'identifiant de la position du clic dans la vue.
Ensuite, récupérez l'instance de la classe AndroidVersion qui correspond à la zone sélectionnée par l'utilisateur afin de l'afficher dans un Log.
2. Fragment▲
Un fragment (cf. chapitre Principes de programmation - Composantes Android) est un composant qui doit être attaché à une activité pour être utilisé. Son cycle de vie ressemble à celui de l'activité parente mais possède quelques spécificités.
2-1. Cycle de vie d'un fragment▲
La première étape du cycle de vie d'un fragment correspond à l'instant où le fragment est attaché à l'activité parente (onAttach). Puis le fragment est initialisé lors de l'appel à la méthode onCreate, suivi de la créati on et du chargement de l'interface du fragment (méthode onCreateView).
Une fois que l'activité parente et le fragment sont créés, l'appel à la méthode onActivityCreated signifie la fin du cycle de création de l'interface.
La méthode onStart coïncide avec le passage du fragment au premier plan, suivi de l'appel à la méthode onResume (même fonctionnement que pour une activité - cf. chapitre Principes de programmation - Cycle de vie d'une activité).
Lorsqu'un fragment devient inactif, l'appel à la méthode onPause permet d'exécuter les actions adéquates (désactivation des mises à jour de l'interface, listener…). Cet appel est suivi par la méthode onStop (le fragment n'est plus au premier plan).
- onDestroyView: destruction de la vue.
- onDestroy: destruction du fragment.
- onDetach: le fragment est séparé de l'activité parente.
2-2. Exemple▲
Dans l'exemple ci-dessous, nous allons créer une vue composée de deux parties:
– L'application en mode portrait: elle sera composée de deux activités. La première représentera la liste des versions d'Android et la seconde représentera la vue de détails affichée lorsque l'utilisateur clique sur un élément de la liste.
– L'application en mode paysage: elle sera composée d'une seule activité mais de deux fragments. Au moment où l'utilisateur clique sur la liste qui se trouve sur le premier fragment, le second se met automatiquement à jour avec les détails.
Pour commencer, créez dans le dossier layout un fichier XML représentant la vue en mode paysage (vue composée de de ux fragments).
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
horizontal
"
>
<
fragment
android
:
id
=
"
@+id/listFragment
"
android
:
layout_width
=
"
150dip
"
android
:
layout_height
=
"
match_parent
"
class
=
"
com.eni.android.fragment.ListFragment
"
>
<
/
fragment
>
<
fragment
android
:
id
=
"
@+id/detailFragment
"
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
class
=
"
com.eni.android.fragment.DetailFragment
"
>
<
/
fragment
>
<
/
LinearLayout
>
- Fragment 1: représente une liste des versio ns d'Android. Il est déclaré dans le fichier ListFragment (cf. attribut class).
- Fragment 2: représente la vue détaillée d' une version d'Android. Il est déclaré dans le fichier DetailFragment.
Maintenant, implémentez le premier fragment (liste des versions d'Android) héritant de la classe ListFragment.
public
class
ListFragment extends
android.app.ListFragment {
private
String[] values =
{
"
Cupcake
"
, "
Donut
"
, "
Eclair
"
, "
Froyo
"
,
"
Gingerbread
"
, "
Honeycomb
"
, "
Ice
Cream
Sandwich
"
, "
Jelly
Bean
"
}
;
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
}
@
Override
public
void
onActivityCreated
(Bundle savedInstanceState) {
super
.onActivityCreated
(savedInstanceState);
ArrayAdapter<
String>
adapter =
new
ArrayAdapter<
String>
(getActivity
(),
android.R.layout.simple_list_item_1, values);
setListAdapter
(adapter);
@
Override
public
void
onListItemClick
(ListView l, View v, int
position,
long
id) {
String item =
(String) getListAdapter
().getItem
(position);
DetailFragment fragment =
(DetailFragment)
getFragmentManager
().findFragmentById
(R.id.detailFragment);
if
(fragment !
=
null
&
&
fragment.isInLayout
()) {
fragment.setText
(item);
}
else
{
Intent intent =
new
Intent
(getActivity
().getApplicationContext
(),
DetailActivity.class
);
intent.putExtra
("
value
"
, item);
startActivity
(intent);
}
}
- Dans cette méthode, l'adapter est initialisé à l'aide du tableau values.
- Puis la liste est liée à l'adapter.
- L'élément cliqué est récupéré à l'aide de l'adapter.
- Le fragment servant à afficher les détails est initialisé.
- Si aucun problème n'est rencontré lors de l'initialisation, cela signifie que l'application est en mode paysage et donc que l'utilisation du fragment des détails est possible, sinon l'application est en mode portrait.
- Si l'initialisation fonctionne correctement, cela permet de définir le texte à afficher dans le fragment des détails.
- Sinon, il faut exécuter l'activité des détails et non le fragment des détails.
Maintenant, il faut implémenter le fragment servant à afficher les détails:
public
class
DetailFragment extends
Fragment {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
}
@
Override
public
void
onActivityCreated
(Bundle savedInstanceState) {
super
.onActivityCreated
(savedInstanceState);
}
@
Override
public
View onCreateView
(LayoutInflater inflater, ViewGroup
container,
Bundle savedInstanceState) {
View view =
inflater.inflate
(R.layout.details,
container, false
);
return
view;
}
public
void
setText
(String item) {
TextView view =
(TextView)
getView
().findViewById
(R.id.detailsText);
view.setText
(item);
}
}
Une fois ces étapes effectuées, le mode paysage est donc correctement implémenté.
L'étape suivante consiste à implémenter les interfaces et les traitements pour le mode portrait. Pour cela, il faut créer un dossier layout-port qui contiendra une seconde version du fichier main.xml.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
horizontal
"
>
<
fragment
android
:
id
=
"
@+id/listFragment
"
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
class
=
"
com.eni.android.fragment.ListFragment
"
/
>
<
/
LinearLayout
>
Dans cette version, le fichier est composé d'un seul fragment (celui qui affiche la liste des versions d'Android). Les différences entre le fichier main.xml en mode portrait et paysage permettent d'identifier l'orientation à l'initialisation de la vue et donc d'adopter le bon comp ortement en fonction de ces deux cas.
Créez un fichier XML représentant la vue détails. Il contiendra le fragment permettant d'afficher les détails d'une version d'An droid en mode portrait.
<?
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
:
orientation
=
"
vertical
"
>
<
fragment
android
:
id
=
"
@+id/detailFragment
"
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
class
=
"
com.eni.android.fragment.DetailFragment
"
/
>
<
/
LinearLayout
>
Nous avons finalement simplement séparé les deux fragments qui se trouvaient dans le même fichier (mode paysage) en deux fichiers distincts (mode portrait).
Maintenant, il faut créer l'activité qui permet de gérer la vue de détails.
public
class
DetailActivity extends
Activity {
@
Override
protected
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.details_activity_layout);
Bundle extras =
getIntent
().getExtras
();
if
(extras !
=
null
) {
String s =
extras.getString
("
value
"
);
TextView view =
(TextView)
findViewById
(R.id.detailsText);
view.setText
(s);
}
}
}
Cette activité charge le nouveau layout et affiche la chaîne de caractères passée en argument (extras).
Pour finir, voici l'activité principale de l'exemple:
public
class
FragmentExempleActivity extends
Activity {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.main);
}
}
Et le fichier manifeste contenant les différentes déclarations:
<
application
android
:icon=
"
@drawable/ic_launcher
"
android
:label=
"
@string/app_name
"
>
<
activity
android
:name=
"
.FragmentExempleActivity
"
android
:label=
"
@string/app_name
"
>
<
intent-
filter>
<
action android:name=
"
android.intent.action.MAIN
"
/
>
<
category
android
:name=
"
android.intent.category.LAUNCHER
"
/
>
<
/
intent-
filter>
<
/
activity>
<
activity android:name=
"
.DetailActivity
"
>
<
/
activity>
<
/
application>
Vous pouvez maintenant tester l'exemple tout d'abord en mode portrait puis en mode paysage. Ce qui donnera les résultats suivants:
3. Passage en mode plein écran▲
Certaines applications nécessitent le passage en mode plein écran (lecture de vidéo par exemple). Un passage en mode plein écran peut signifier que l'appli-cation cache la barre de notification du téléphone et/ou la barre de navigation (sauf dans le cas d'une tablette).
Le cas d'une tablette est assez différent car les barres de notification et de navigation se trouvent toutes les deux sur la barre de système. Ce qui ne permet pas de les cacher mais juste de les obscurcir afin de les rendre moins visibles.
Voici la méthode permettant de cacher la barre de notification sur un smartphone et de les obscurcir sur une tablette:
onSystemUiVisibilityChange
(int
visibility)
Cette méthode possède un argument pouvant prendre les valeurs suivantes:
– SYSTEM_UI_FLAG_LOW_PROFILE: permet d'obscurcir la barre de navigation.
– SYSTEM_UI_FLAG_HIDE_NAVIGATION: permet de cacher la barre de navigation.
Remarque:
Vous pouvez aussi obscurcir la barre de navigation sur un smartphone.
Néanmoins, Android 4 introduit une limitation (due à l'absence de bouton physique): à la moindre interaction de l'utilisateur avec l'activité en cours d'exécution, les barres de navigation apparaissent à nouveau.
Si vous souhaitez plutôt cacher la barre de notification d'un smartphone (lecture vidéo ou jeux vidéo par exemple), il suffit d'utiliser la méthode suivante à l'initialisation d'une activité :
getWindow
().addFlags
(WindowManager.LayoutParams.FLAG_FULLSCREEN);
Remarque:
Ne cachez la barre de notification que si nécessaire car elle est importante pour l'interaction de l'utilisateur avec l'appareil (accès aux évènements importants arrivant sur l'appareil).
4. Interfaces dynamiques▲
Créer des interfaces plus riches peut passer par la création d'interfaces dynamiques. Une interface dynamique est une interface créée en Java, directement dans le fichier source d'une activité.
Remarque:
Une interface dynamique peut être combinée avec une interface statistique (fichier XML).
La création dynamique d'interface est très utile dans le cas où vous voulez rajouter des composants à la volée dans une interface.
Pour illustrer cette fonctionnalité, l'exemple suivant sert à créer une interface composée d'un bouton qui servira à rajouter des zones d'édition (EditText) dynamiquement dans l'interface.
Cette vue contient tout simplement un LinearLayout et un bouton possédant un identifiant.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
android
:
id
=
"
@+id/linearlayout
"
>
<
Button
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
text
=
"
@string/addEditText
"
android
:
id
=
"
@+id/addBtn
"
/
>
<
/
LinearLayout
>
Lors du clic sur le bouton, des zones d'édition seront rajoutées dynamiquement à la vue.
public
class
DynamicViewActivity extends
Activity {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.main);
final
LinearLayout linearLayout =
(LinearLayout)
findViewById
(R.id.linearlayout);
Button btn =
(Button) findViewById
(R.id.addBtn);
btn.setOnClickListener
(new
OnClickListener
() {
@
Override
public
void
onClick
(View v) {
EditText edit =
new
EditText
(DynamicViewActivity.this
);
edit.setHint
(R.string.newEditText);
LayoutParams layoutParams =
new
LayoutParams
(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
edit.setLayoutParams
(layoutParams);
linearLayout.addView
(edit);
}
}
);
}
}
- Il faut créer une zone d'édition à l'aide de son constructeur en lui passant le contexte nécessaire à son initialisation.
- Cette zone d'édition a un texte par défaut ( setHint).
- Les layoutParams servent à spécifier des valeurs et propriétés à un EditText(notamment sur la largeur et la hauteur).
- Puis pour finir, il faut ajouter la zone d'édition créée dans le LinearLayout récupéré précédemment (grâce à son identifiant).
Voici le résultat obtenu:
5. Création d'onglets▲
5-1. Principe▲
Les onglets permettent de basculer plus facilement entre plusieurs vues et d'améliorer ainsi l'expérience utilisateur d'une application.
Il existe trois types d'onglets:
– Les onglets scrollables (exemple de Google Play):
– Ils peuvent contenir un grand nombre d'onglets.
– La navigation entre les onglets s'effectue en glissant un doigt de gauche à droite ou inversement.
– Les onglets fixes (exemple de l'application Contacts):
– Les onglets sont affichés les uns à la suite des autres.
– Ils peuvent contenir jusqu'à trois onglets maximum.
– Les onglets empilés (exemple de l'application Youtube):
– Ils permettent de séparer ou fusionner des onglets avec une ActionBar (cf. chapitre Création d'interfaces simples - Barre d'actions (ActionBar)).
5-2. Implémentation d'onglets scrollables▲
Ce composant est utilisable à l'aide de la classe ViewPager qui est disponible dans le Compatibility Package. Ce dernier est téléchargeable dans la partie extras du SDK Android (cf. chapitre Environnement de développement -SDK Android).
Une fois téléchargé, vous devez intégrer la bibliothèque téléchargée dans le projet grâce au fichier jar se trouvant dans le dossier extras de votre SDK Android.
La première étape consiste à intégrer le ViewPager dans une interface XML.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
android
:
background
=
"
#fff
"
>
<
android
.
support
.
v4
.
view
.
ViewPager
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
android
:
id
=
"
@+id/viewPager
"
/
>
<
/
LinearLayout
>
Vous pouvez remarquer que le ViewPager n'est pas un élément natif du SDK mais provient bien du Compatibility Package.
La prochaine étape consiste à créer un adapter personnalisé pour la classeViewPager. Cet adapter doit hériter de la classe PagerAdapter et implémenter les méthodes suivantes:
– getCount: retourne le nombre de vues disponibles dans l'interface.
– instantiateItem: crée la page située à une position donnée.
– destroyItem: supprime une page située à une position donnée.
– isViewFromObject: détermine si la vue est associée à un objet.
– finishUpdate: appelle quand toutes les mises à jour sur la page actuelle sont effectuées.
– restoreState: restaure l'état d'une page.
– saveState: sauvegarde l'état d'une page.
– startUpdate: appelle pour mettre à jour la page actuelle.
private
class
ViewPagerAdapter extends
PagerAdapter {
@
Override
public
int
getCount
() {
return
NUMBER_OF_PAGES;
}
@
Override
public
Object instantiateItem
(View collection, int
position) {
TextView tv =
new
TextView
(ViewPagerExempleActivity.this
);
tv.setText
("
Page
numero
:
"
+
position);
tv.setTextColor
(Color.BLACK);
tv.setTextSize
(30
);
((ViewPager) collection).addView
(tv, 0
);
return
tv;
}
@
Override
public
void
destroyItem
(View collection, int
position,
Object view) {
((ViewPager) collection).removeView
((TextView) view);
}
@
Override
public
boolean
isViewFromObject
(View view, Object object) {
return
view=
=
((TextView)object);
}
@
Override
public
void
finishUpdate
(View arg0) {
}
@
Override
public
void
restoreState
(Parcelable arg0, ClassLoader arg1) {
}
@
Override
public
Parcelable saveState
() {
return
null
;
}
@
Override
public
void
startUpdate
(View arg0) {
}
}
Voici quelques explications sur l'adapter créé:
–La méthode getCount doit retourner le nombre de pages disponibles dans l'adapter.
– instantiateItem: permet d'instancier la vue actuelle. Dans l'exemple, une zone de texte dans laquelle il faut afficher le numéro de la page est créée. Puis cette zone de texte est ajoutée au ViewPager.
– destroyItem: permet de détruire l'élément actuel.
– isViewFromObject: compare la vue actuelle avec l'objet passé en argument.
Ce qui donnera:
6. Les popups▲
6-1. Les toasts▲
Les toasts servent à indiquer un message court, une indication ne nécessitant pas d'interaction utilisateur.
Créer un toast peut se faire depuis une activité, un service ou toute autre classe possédant un contexte.
Toast.makeText
(ToastExampleActivity.this
, R.string.toast,Toast.LENGTH_SHORT).show
();
Pour créer un toast, utilisez la méthode makeText qui prend pour arguments:
–Le contexte.
– La chaîne à afficher.
– La durée de l'affichage du toast.
– Vous disposez de deux valeurs prédéfinies : Toast.LENGTH_SHORT et Toast.LENGTH_LONG.
La méthodeshow sert à afficher le toast.
Ce qui donnera:
Remarque:
N'oubliez pas d'appeler la méthode show pour afficher le Toast.
6-2. AlertDialog▲
Une AlertDialog est une boîte de dialogue permettant d'afficher un message et d'effectuer une interaction avec l'utilisateur.
Elle se compose de trois parties:
–Un titre (optionnel): représente le titre de la boîte de dialogue.
–Un contenu: représente le contenu d'une boîte de dialogue. Elle peut contenir des zones de texte, des sliders, des cases à cocher, des boutons radio, etc.
–Un bouton ou plusieurs boutons d'actions: permet à l'utilisateur de choisir l'action à effectuer.
Voici un exemple pour une boîte de dialogue demandant à l'utilisateur de confirmer la suppression d'un message.
AlertDialog.Builder builder =
new
AlertDialog.Builder
(this
);
builder.setMessage
(R.string.alert_dialog_msg).setCancelable
(false
).setPositiveButton
("
Yes
"
, new
OnClickListener
() {
@
Override
public
void
onClick
(DialogInterface dialog, int
which) {
dialog.cancel
();
}
}
).setNegativeButton
("
Cancel
"
, new
OnClickListener
() {
@
Override
public
void
onClick
(DialogInterface dialog, int
which) {
dialog.cancel
();
}
}
);
builder.create
().show
();
Dans ce code:
– Vous utilisez un AlertDialog.Builder pour créer une boîte de dialogue.
– Vous spécifiez le message ainsi que les boutons à afficher. Vous pouvez aussi avoir un seul bouton à l'aide de la méthode setNeutralButton.
– Puis vous appelez la méthode create pour créer la boîte de dialogue.
– Pour finir, vous utilisez la méthode show pour afficher la boîte de dialogue.
Ce qui donnera:
6-3. ProgressDialog▲
Les barres de progression servent à informer un utilisateur de l'avancement d'une tâche.
Il existe deux types de barres de progression :
– Barre de progression bornée:si vous voulez, par exemple, connaître le pourcentage d'avancement d'une tâche (exemple de téléchargement d'une application sur Google Play).
– Barre de progression non bornée: si la durée du traitement ne peut pas être calculée, vous pouvez utiliser ce type de barre de progression:
6-3-1. 1 Implémentation▲
Pour implémenter une barre de progression, il faut utiliser la classe Progress-Dialog.
Voici un exemple de création d'une barre de progression:
ProgressDialog dialog =
ProgressDialog.show
(
ProgressDialogExampleActivity.this
,
getResources
().getString
(R.string.dialog_title), getResources
()
.getString
(R.string.dialog_load), true
);
La construction nécessite:
–Un contexte.
– Une chaîne de caractères servant de titre à la boîte de progression (optionnel).
– Une chaîne de caractères représentant le message affiché par la boîte de progression.
Ce qui donnera:
Une fois le traitement effectué, vous devez arrêter la barre de progression à l'aide de la méthode dismiss:
dialog.dismiss
();
- Créer une instance de la classe ProgressDialog.
- Définir le style de la barre de progression (style horizontal)
- Définir le message.
- Afficher la barre de progression.
ProgressDialog progressDialog;
progressDialog =
new
ProgressDialog
(ProgressDialogExampleActivity.this
);
progressDialog.setProgressStyle
(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage
("
Loading...
"
);
progressDialog.show
();
Pour mettre à jour l'avancement de la tâche en cours, utilisez la méthode set-Progress:
progressDialog.setProgress
(PROGRESS_VALUE);
6-4. Boîte de dialogue personnalisée▲
Vous pouvez créer des boîtes de dialogue personnalisées avec un layout spécifique. Pour cela, créez un fichier XML représentant le layout personnalisé.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
id
=
"
@+id/layout_root
"
android
:
orientation
=
"
horizontal
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
padding
=
"
10dp
"
>
<
ImageView
android
:
id
=
"
@+id/alert_img
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
fill_parent
"
android
:
layout_marginRight
=
"
10dp
"
/
>
<
TextView
android
:
id
=
"
@+id/alert_msg
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
fill_parent
"
android
:
textColor
=
"
#FFF
"
android
:
gravity
=
"
center_vertical
"
/
>
<
/
LinearLayout
>
- Créer la boîte de dialogue.
- Lier la boîte de dialogue au fichier XML représentant la vue personnalisée.
- Spécifier un titre, une image et une description pour la boîte de dialogue.
Dialog dialog =
new
Dialog
(CustomAlertDialogExampleActivity.this
);
dialog.setContentView
(R.layout.custom_alert);
dialog.setTitle
("
Popup
Personnalisée
"
)
TextView text =
(TextView) dialog.findViewById
(R.id.alert_msg);
text.setText
("
Ma
première
Popup
personnalisée
!!
"
);
ImageView image =
(ImageView)dialog.findViewById
(R.id.alert_img);
image.setImageResource
(R.drawable.nyan);
dialog.show
();
7. Préférences▲
Les préférences permettent de créer des écrans spécifiquement dédiés à la création et à la gestion des préférences d'un utilisateur dans une application ou de manière globale sur un appareil.
Depuis la version 3.0 d'Android, vous devez utiliser la classe PreferenceFragment pour créer les écrans de préférences d'une application.
Les préférences créées à l'aide de la classe PreferenceFragment seront automatiquement sauvegardées dans des SharedPreferences (cf. chapitre Persis-tance de données - SharedPreference).
Remarque:
Pour récupérer le SharedPreferences spécifique à la sauvegarde des préférences, appelez la méthode getDefaultSharedPreferences.
Pour créer un écran de préférences, vous disposez de plusieurs composants.
- Case à cocher/Switcher: utilisez ce composant pour les options possédant seulement deux états (activé ou non).
- Choix multiple: utilisez ce composant lorsqu'un utilisateur doit choisir une option parmi plusieurs possibles.
- Slider: utilisez ce composant lorsqu'un utilisateur doit choisir une valeur dans une rangée de valeurs possibles.
Pour créer un écran représentant des préférences d'une application, commencez par créer un dossier nommé xml dans les ressources d'une application. En effet, le dossier xml est l'emplacement naturel des interfaces de préférences.
Créez un fichier représentant une interface de préférences:
<?
xml
version="1.0"
encoding="utf-8"?
>
<
PreferenceScreen
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
>
<
PreferenceCategory
android
:
title
=
"
@string/notification
"
>
<
SwitchPreference
android
:
key
=
"
checkbox_preference
"
android
:
title
=
"
@string/enable_notification
"
/
>
<
/
PreferenceCategory
>
<
PreferenceCategory
android
:
title
=
"
@string/data
"
>
<
CheckBoxPreference
android
:
summary
=
"
@string/save_data_summary
"
android
:
title
=
"
@string/save_data_title
"
/
>
<
/
PreferenceCategory
>
<
/
PreferenceScreen
>
Un écran représentant une interface de préférences commence toujours par la balise PreferenceScreen.
Remarque:
Vous pouvez imbriquer plusieurs balises PreferenceScreen pour avoir plusieurs écrans de préférences imbriqués.
Chaque partie distincte d'une interface de préférences débute par la balise PreferenceCategory. Chaque catégorie peut conten ir un ou plusieurs éléments.
L'exemple abordé possède deux catégories:
Chaque partie distincte d'une interface de préférences débute par la balise PreferenceCategory . Chaque catégorie peut conten ir un ou plusieurs éléments.
L'exemple abordé possède deux catégories:
– La première contient un Switcher.
– La seconde contient une case à cocher.
Remarque:
Tous les éléments qui peuvent composer un écran de préférences possèdent un nom finissant par Preference(SwitchPreference, CheckBoxPreference…).
Modifiez l'activité principale afin d'intégrer le fragment représentant l'écran de préférences :
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
getFragmentManager
().beginTransaction
().replace
(android.
R.id.content, new
MyPreferenceFragment
()).commit
();
}
public
static
class
MyPreferenceFragment extends
PreferenceFragment {
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
addPreferencesFromResource
(R.xml.preference_screen);
}
}
Dans la méthode onCreate, l'insertion du fragment s'effectue en suivant les étapes ci-dessous:
– Récupérer une instance de la classe FragmentManager, afin d'interagir avec les fragments.
– Appeler la méthode beginTransaction qui permet de commencer une série d'opérations sur un fragment.
– Appeler la méthode replace qui permet d'intégrer la classe personnalisée (MyPreferenceFragment) à l'interface en tant que fragment principal.
– Appeler la méthode commit qui valide toutes les modifications effectuées précédemment.
Puis créez une classe qu i hérite de la classe PreferenceFragment et rajoutez le fragment déclaré dans le fichier XML.
Ce qui donnera:
8. WebView▲
Le framework Android permet, grâce à la classe WebView, d'inclure des pages HTML à l'intérieur d'une application.
Cette classe utilise le WebKit d'Android pour afficher des pages HTML, l'historique, traiter le code JavaScript, zoomer, etc.
Vous pouvez afficher une page web distante, une page locale stockée dans le projet ou simplement inclure du code HTML.
8-1. Exemple d'une page web distante▲
La première étape consiste à créer un fichier XML représentant une vue et y inclure le composant WebView.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
WebView
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
id
=
"
@+id/webview
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
/
>
La deuxième étape consiste à créer une activité permettant de lier la vue déclarée précédemment à l'activité et spécifier l'URL à charger par la WebView.
@
Override
public
void
onCreate
(Bundle savedInstanceState) {
super
.onCreate
(savedInstanceState);
setContentView
(R.layout.main);
WebView webView =
(WebView) findViewById
(R.id.webview);
webView.loadUrl
("
http://www.tutos-android.com
"
);
}
Vous pouvez spécifier l'URL à l'aide de la méthode loadUrlaccessible depuis l'instance de la classe WebView.
Sans oublier d'ajouter la permission d'accès à Internet dans le manifeste de l'application:
<
uses-
permission android:name=
"
android.permission.INTERNET
"
/
>
8-2. Paramètre de la WebView▲
Vous pouvez accéder aux paramètres d'une WebView afin de la personnaliser grâce à la méthode getSettings.
Voici un exemple servant à récupérer les paramètres d'une WebView afin d'afficher les boutons de zoom (setBuiltInZoomControls) et d'autoriser l'exécution du JavaScript (setJavascriptEnabled).
WebSettings settings =
webView.getSettings
();
settings.setBuiltInZoomControls
(true
);
settings.setJavaScriptEnabled
(true
);
8-3. Gestion du bouton retour▲
En incluant des pages Internet dans une application Android, vous devez changer la gestion du bouton retour afin de simuler le comportement du bouton retour d'un navigateur web.
@
Override
public
boolean
onKeyDown
(int
keyCode, KeyEvent event) {
if
((keyCode =
=
KeyEvent.KEYCODE_BACK) &
&
mWebView.canGoBack
()) {
mWebView.goBack
();
return
true
;
}
return
super
.onKeyDown
(keyCode, event);
}
Pour cela, surchargez la méthode onKeyDown. Vérifiez que le clic correspond bien au bouton retour et que la navigation ne se trouve pas à la première page (possibilité d'accéder à une page précédente).
8-4. Utilisation d'Android natif dans du JavaScript▲
Vous pouvez utiliser du code JavaScript dans des WebView et même combiner ce JavaScript avec du code natif.
Le but de l'exemple suivant est d'afficher un toast Android à l'aide de code JavaScript.
Pour commencer, créez une classe représentant l'interface JavaScript. Elle possédera une méthode permettant d'afficher un Toast.
public
class
JavaScriptToastInterface {
Context context;
JavaScriptToastInterface
(Context c) {
context =
c;
}
public
void
showToast
(String toastMsg) {
Toast.makeText
(context, toastMsg,
Toast.LENGTH_SHORT).show
();
}
}
Activez ensuite le JavaScript dans la WebView et liez l'interface JavaScript à la WebView à l'aide de la méthode addJavascriptInterface.
Le second argument de la méthode correspond à l'identifiant qu'aura cette interface dans le code HTML. Cela vous permet de rajouter plusieurs interfaces JavaScript possédant des identifiants différents.
WebView webView =
(WebView) findViewById
(R.id.webview);
WebSettings settings =
webView.getSettings
();
settings.setJavaScriptEnabled
(true
);
webView.addJavascriptInterface
(new
JavaScriptToastInterface
(this
), "
Android
"
);
webView.loadData
(WEB_CONTENT, "
text/html
"
, "
UTF-8
"
);
Nous allons avoir besoin d'un contenu web à afficher:
<
input type=
"
button
"
value=
"
Test
Javascript
"
onClick=
"
showAndroidToast('Un
Toast
cree
en
Javascript!')
"
/
>
<
script type=
"
text/javascript
"
>
function showAndroidToast
(toast) {
Android.showToast
(toast);
}
<
/
script>
Ce bout de code représente un bouton qui exécute la méthode showAndroid présente dans l'interface JavaScript.
Stockez le contenu de ce code HTML dans une variable de type String et chargez le dans la WebView à l'aide de la méthode loadData.
String WEB_CONTENT =
"
<input
type=\"button\"
value=\"Test
Javascript\" onClick=
\"showAndroidToast
(\'Un toast cree
en Javascript!
\')\" /
>
<
script type=
\"text/
javascript\">
function
showAndroidToast
(toast) {
Android.showToast
(toast);}
<
/
script>
"
;
Ce qui donnera:
9. Bonnes pratiques▲
Voici quelques conseils afin que vos applications soient adaptables aux différentes situations rencontrées lors du développement d'applications:
9-1. Être indépendant de la résolution de l'écran▲
– Dans votre application, utilisez les dp(independent pixel ) pour déclarer les tailles des différents éléments et composants ainsi que les sp( independent scale) pour déclarer la taille des différentes polices.
– Créez des dossiers drawable pour chaque résolution (cf. chapitre Création d'interfaces simples - Les ressources).
9-2. Être indépendant de la taille de l'écran▲
– Pour les différentes tailles d'éléments, privilégiez les tailles prédéfinies(wrap_ontent / match_parent), ce qui permet aux vues de s'adapter aux différentes tailles d'écrans.
– Utilisez le RelativeLayout qui permet de contrôler plus précisément l'emplacement des différents éléments d'une vue les uns par rapport aux autres et non par rapport à la taille de l'écran.
– Créez des layouts pour les différentes tailles d'écrans (small, normal, large, xlarge…) et les positions de l'écran (portrait et paysage).
– Spécifiez les tailles d'écrans gérées à l'aide de la balise supports-screens (cf.chapitre Principes de programmation - Manifeste).
– Utilisez des images étirables (outil 9-patch). L'outil draw9patch fourni avec le SDK Android (dossier tools) permet de spécifier la manière dont une image devra s'étirer selon les cas rencontrés à l'aide de points extensibles à positionner sur l'image cible.
9-3. Être indépendant de la version d'Android utilisée▲
Certaines API, fonctionnalités ou composants ne sont présents que dans certaines versions d'Android. Pensez à tester la version présente sur l'appareil avant d'utiliser une fonctionnalité qui peut ne pas être disponible sur cette version de l'OS.
if
(Build.VERSION.SDK_INT <
Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
//
Appareil
en
version
4.0.0
ou
supérieur
}
else
{
//
Appareil
en
version
inférieure
à
4.0.0
}
Créez des dossiers spécifiques pour vos interfaces si vous souhaitez personnaliser une interface en fonction des versions d'Android (menu - v14 / values -v11 / layout - v14...).
9-4. Être performant▲
– Ne créez pas d'objet inutilement.
– Les constantes doivent être statiques (le mot-clé final permet d'indiquer que la valeur stockée dans une variable ne peut être modifiée).
private
static
final
my_const =
42
;
– Évitez les Getters/Setters, préférez les accès directs aux attributs de classe si possible.
– N'utilisez pas n'importe quelle bibliothèque dans une application Android.
10. Optimiser ses interfaces▲
À chaque création d'une activité, le layout lié à cette activité est chargé (méthode setContentView ou inflate). Ce chargement peut être très coûteux si l'interface en question est mal conçue ou trop lourde. C'est pourquoi Android fournit plusieurs mécanismes afin d'optimiser les différentes interfaces qui composent votre application.
10-1. Inspecter la hiérarchie de ses interfaces▲
La première méthode pour optimiser des interfaces Android consiste à inspecter la hiérarchie de la vue cible. Pour cela, le SDK Android fournit un outil nommé HierarchyViewer(il se trouve dans le dossier tools du SDK).
- Lancez l'application cible sur un émulateur.
- Affichez l'activité cible (que vous souhaitez inspecter).
- Lancez le HierarchyViewer.
- Sélectionnez le processus correspondant à l'application cible, puis cliquez sur Load View Hierarchy.
Un diagramme sur l'interface cible s'affiche.
Voici un exemple simple d'un FrameLayout contenant une image et un texte.
<?
xml
version="
1.0
"
encoding="utf-8
"?
>
<
FrameLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
>
<
ImageView
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
src
=
"
@drawable/ic_launcher
"
/
>
<
/
FrameLayout
>
Ce qui donnera:
L'exécution de l'outil HierarchyViewer sur la vue déclarée précédemment donne le graphique suivant:
Vous pouvez remarquer que le FrameLayout que nous avons déclaré dans la vue suit un autre FrameLayout (ajouté automatiquement par Android lors de la création d'une vue).
La conclusion est que le FrameLayout que nous avons déclaré est inutile et nous pouvons utiliser la balise merge pour fusionner notre vue (sansFrameLayout)avec le FrameLayout déclaré automatiquement (cf. section Fusionner des layouts). Ce qui optimise la vue en évitant de charger un layout inutilement.
10-2. Fusionner des layouts▲
Android propose le tag merge afin de réduire et d'optimiser le nombre de niveaux d'une interface (cf. exemple précédent). Cette optimisation s'effectue en fusionnant les composants déclarés après le merge avec le layout situé au-dessus du merge.
Appliquez cette optimisation à l'exemple précédent:
<
merge
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
>
<
ImageView
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
scaleType
=
"
center
"
android
:
src
=
"
@drawable/ic_launcher
"
/
>
<
/
merge
>
- Les layouts fusionnés doivent être identiques ou posséder le même comportement.
- La balise ne peut être utilisée qu'à la racine d'un fichier XML.
- Lors de l'utilisation de la balise merge, vous devez spécifier le layout parent sur lequel la vue sera attachée et définir la méthode attachToRoot à vraie.
10-3. Inclure des vues▲
Vous pouvez modulariser des interfaces afin d'inclure et réutiliser des vues déjà créées dans d'autres vues. Cette fonctionnalité est possible grâce à la balise include fournie par Android.
Lors de l'inclusion, il faut spécifier:
– Un identifiant au layout inclus: afin de pouvoir l'initialiser dans l'activité et récupérer son contenu.
– Le layout à inclure.
L'exemple suivant inclut dans un LinearLayout un autre layout déclaré dans un autre fichier.
<?
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
:
orientation
=
"
vertical
"
>
<
include
android
:
id
=
"
@+id/included_layout
"
layout
=
"
@layout/main
"
/
>
<
Button
android
:
id
=
"
@+id/btn
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
wrap_content
"
android
:
text
=
"
@string/hello
"
/
>
<
/
LinearLayout
>
10-4. Chargement paresseux (Lazy Loading) des layouts▲
Le chargement de vue est très coûteux et plus la vue contient d'éléments, plus le temps de chargement est important. Afin de minimiser le nombre de vues chargées à la création d'une interface, vous pouvez utiliser la classe ViewStub.
Un ViewStub utilise un système de chargement paresseux. Un élément de l'interface déclaré à l'aide d'un ViewStub ne sera chargé que lorsque l'élément devient visible.
Voici un exemple d'une interface contenant deux boutons. Le second bouton est déclaré en ViewStub et ne s'affiche qu'au moment du clic sur le premier bouton.
<?
xml
version="
1.0
"
encoding="utf-8
"?
>
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
android
:
orientation
=
"
vertical
"
>
<
Button
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
id
=
"
@+id/enable
"
android
:
text
=
"
@string/enable_view_stub
"
/
>
<
ViewStub
android
:
id
=
"
@+id/view_stub
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
android
:
inflatedId
=
"
@+id/view_stub_visible
"
android
:
layout
=
"
@layout/view_stub_btn
"
/
>
<
/
LinearLayout
>
La déclaration d'un ViewStub se compose ainsi :
– Un identifiant.
– Un identifiant utilisé lorsque la ViewStub sera visible.
– Le layout qui sera chargé par le ViewStub.
Puis déclarez le layout spécifié par le ViewStub:
<?
xml
version="
1.0
"
encoding="utf-8
"?
>
<
Button
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
android
:
text
=
"
@string/btn_stub
"
/
>
Pour finir, il faut récupérer le ViewStub et le rendre visible au moment du clic sur le premier bouton :
final
View stub =
findViewById
(R.id.view_stub);
Button enable =
(Button) findViewById
(R.id. enable);
enable.setOnClickListener
(new
OnClickListener
() {
@
Override
public
void
onClick
(View v) {
stub.setVisibility
(View.VISIBLE);
}
}
);
Remarque:
- VISIBLE: la vue devient visible.
- INVISIBLE : la vue devient invisible mais occupe toujours l'espace qui lui est attribué.
- GONE: la vue est invisible mais n'occupe plus l'espace qui lui est attribué.