Introduction
Le système Android utilise une mécanique MVC dont les vues sont entièrement générées par des librairies et des fichiers de configuration. L’idée est d’utiliser le plus possible les fichiers de configuration pour générer l’interface graphique et d’utiliser le moins possible la programmation pour cette tâche.
Les « layouts »
Android utilise un système de positionnement Android qu’on appelle « Layout ». Il s’agit d’une ressource qu’on peut utiliser et modifier à l’aide de la programmation d’Activité. Ces « layouts » sont configurés dans un fichier XML, et sont liés aux activités par la méthode « setContentView ».
Les « layouts » sont organisés en arbre dans lequel les feuilles sont des vues et les autres noeuds sont des groupes de vues.
Les vues sont les « widget » graphiques comme des boutons ou des boites de texte. Les groupes de vues sont des « layouts » en soi. Donc, les « layouts » contiennent d’autres « layouts » et des « widgets ».
L’éditeur Android Studio permet de modifier les « layouts » de manière graphique ou en éditant directement la configuration XML. La version graphique peut être plus facile, mais il est important de considérer que cette version n’est pas particulièrement fiable. L’affichage tel que visible dans Android Studio ne correspond pas nécessairement à l’affichage qui sera visible sur un appareil Android. L’édition XML peut sembler plus difficile à première vue, mais donne généralement un résultat plus fiable une fois sur l’appareil Android.
Voici le visuel graphique:
Voici le visuel d’édition de configuration:
Et voici un affichage « split »:
Cet affichage permet de voir graphiquement les modifications effectuées dans le code XML. La principale problématique de ce type d’affichage est l’espace nécessaire pour être à l’aise.
Les groupes de vues
Il existe une grande quantité de groupe de vues (« layouts »). Je ne ferai pas le tour de toutes ces classes et si vous voulez en savoir plus, je vous recommande d’aller voir la documentation ici. Les « layouts » présentés ici sont les plus généraux et permettent de faire une grande majorité des mises en pages standards.
Les « layouts » linéaires
Les « layouts » linéaires sont une manière standard, rapide et adaptative de faire des interfaces mobiles. Il existe deux (2) types de « layouts » linéaires: les « layouts » linéaires verticaux et les « layouts » linéaires horizontaux. Par exemple, voici une configuration de « layout » linéaire horizontal:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
</LinearLayout>
Et voici un exemple de « layout » linéaire vertical:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button" />
</LinearLayout>
Il est également possible d’emboiter des « layouts » dans d’autres « layouts ». Par exemple:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button1" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button2" />
</LinearLayout>
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button3" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button4" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button5" />
<Button
android:id="@+id/button6"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Button6" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
Les « layouts » de contraintes
Les « layouts » de contraintes sont des « layouts » qui sont beaucoup plus puissant que les « layouts » linéaires, mais qui sont également beaucoup plus complexe à monter. Ce type de « layouts » est basé sur la position relative des objets par rapport aux autres. Ce type de « layout » remplace les « layouts » relatifs qui était utilisés dans les précédentes versions d’Android. Également, les « layouts » de contraintes peuvent permettre des déplacements et animations d’objets.
Voici ce qui a à connaître dans l’utilisation des « layouts » de contrainte:
-
- Afin que l’objet apparaisse bel et bien à l’endroit précisé une fois exécuté sur l’appareil mobile, il faut s’assurer que chaque objet a une contrainte verticale et une contrainte horizontale;
- Les contraintes peuvent être par rapport aux autres objets graphiques, ou bien par rapport au bord de l’écran;
- Afin de créer une contrainte, on peut utiliser les poignées attachées à chaque objet dans l’éditeur visuel de « layout ».
-
- La contrainte peut être contrôlée plus précisément avec l’outil de contrainte:
-
- Vous pouvez également directement spécifier les contraintes dans la configuration XML en utilisant « android:layout_marginTop », « android:layout_marginBotton », « android:layout_marginLeft » et « android:layout_marginRight ».
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="88dp"
android:layout_height="wrap_content"
android:layout_marginStart="160dp"
android:layout_marginLeft="160dp"
android:layout_marginBottom="328dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
-
- Il y a trois types de contraintes possibles:
- La contrainte fixe permet de spécifier précisément la distance entre 2 objets. Représenté par le symbole: .
- La contrainte d’emballage (« wrap ») permet de s’adapter afin que le contenu d’un objet soit entièrement affiché. Représenté par le symbole .
- La contrainte d’étendue (« match ») permet de prendre le maximum d’espace possible dans l’espace disponible. Représenté par le symbole .
- On utilise généralement les contraintes afin de créer des chaines d’objets (vertical ou horizontal). Voici les types de chaines qu’on peut créer avec le système de contrainte:
- Il y a trois types de contraintes possibles:
-
-
- Chaine étendue (« Spread chain »): les objets s’étendent également sur toute la surface disponible.
- Chaine étendue à l’intérieur (« Spread inside chain »): Les objets sur les bords sont fixes, mais ceux à l’intérieur s’étendent également.
- Chaine avec poids (Weighted): Chaine dont les espaces entre les sous-chaines étendues ou étendues à l’intérieur sont remplis d’objets intermédiaires. Ces objets peuvent avoir un poids différent qui fait en sorte de s’étendre de manière plus ou moins importante en fonction de ce poids.
- À noter: pour établir un poids différent entre les objets, on peut utiliser l’attribut « layout_constraintHorizontal_weight » et « layout_constraintVertical_weight ».
- Chaîne en paquet (« Packed chain »): Les objets sont placés en paquet d’objets.
- Certains outils peuvent être très utiles dans l’éditeur visuel en utilisant le bouton de droit de la souris sur un ou plusieurs objets sélectionnés
- Effacer les contraintes;
- Aligner des objets;
- Créer des chaines;
- etc.
- Il est recommandé d’expérimenter afin de savoir tout ce que ce type de « layout » permet de faire.
-
La programmation
Malgré que la majorité du travail de création graphique Android repose dans l’utilisation des outils de « layout » d’Android Studio; il est important d’être capable d’utiliser les vues créées dans le code de l’activité. Voici donc ce que vous devez savoir pour utiliser les vues de vos « layouts » dans la programmation Java de vos activités Android.
Accéder à une vue
Pour accéder à une vue d’un « layout », il faut utiliser la méthode d’activité « findViewById ». Cette méthode prend un identificateur numérique de vue (ou ID) et retourne la vue en question. Pour avoir accès au ID de la vue, on utilise « R.id ». Par exemple, supposons que j’ai créé un TextView dans mon « layout » identifié par « texte_message ». Voici le code pour avoir accès à la vue:
TextView lTexteMessage = findViewById(R.id.texte_message);
Une fois qu’on a l’objet Vue en Java, on peut utiliser les getters et les setters de la classe afin d’avoir accès ou de modifier les attributs de cet objet:
lTexteMessage.setText("Salut tout le monde");
Les événements des vues
Certaines vues doivent envoyer des événements au code Java (par exemple, lorsqu’on clique sur un bouton). Les vues doivent être assignées à une méthode de l’activité afin de pouvoir être utilisées. En Android, toutes les méthodes d’événements de vue doivent avoir cette signature:
public void monEvenement(View vue)
Voici un exemple. Dans cet exemple, l’utilisateur doit entrer son nom dans une boite texte (EditText) identifiée par « entree_texte_nom » et appuyer sur un bouton. Le bouton est associé à l’événement Java « boutonAppuyezClique ». Ensuite, le message « Bonjour LeNomEntrée » sera affiché dans la vue de texte (TextView) idientifié « texte_message ». Noter que la chaine « Bonjour » est placée dans la ressource des constants textes « strings.xml » avec l’identificateur: « message ». Voici l’événement Java du bouton:
public void boutonAppuyezClique(View vue) {
TextView lTexteMessage = findViewById(R.id.texte_message);
CharSequence lMessage = getString(R.string.message);
EditText lEntreeNom = findViewById(R.id.entree_texte_nom);
CharSequence lNom = lEntreeNom.getText();
lTexteMessage.setText(lMessage.toString() + lNom.toString());
}
Voici les fichiers de ressources XML qui pourraient vous servir à tester cet exemple:
Le fichier « strings.xml »:
<resources>
<string name="app_name">Mon Application</string>
<string name="bouton_texte">Appuyez</string>
<string name="message">Bonjour\u0020</string>
<string name="label_nom">Nom:</string>
</resources>
Notez que le « \u0020 » représente le code Unicode pour un espace. C’est la seule façon de forcer une constante à débuter ou terminer une chaine par un (ou plusieurs) espace. Également, si vous désirez mettre plusieurs espaces consécutifs ou une tabulation dans une constante String, vous devrez utiliser des codes Unicodes similaires.
Le fichier de « layout »:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bouton_appuyez"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="boutonAppuyezClique"
android:text="@string/bouton_texte"
app:layout_constraintBottom_toTopOf="@+id/texte_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/label_nom" />
<TextView
android:id="@+id/label_nom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_nom"
app:layout_constraintBottom_toTopOf="@+id/bouton_appuyez"
app:layout_constraintEnd_toStartOf="@+id/entree_texte_nom"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/entree_texte_nom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="@+id/label_nom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/label_nom"
app:layout_constraintTop_toTopOf="@+id/label_nom" />
<TextView
android:id="@+id/texte_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bouton_appuyez" />
</androidx.constraintlayout.widget.ConstraintLayout>
Programmer des événements en Java
Il est également possible d’assigner un événement à une vue directement dans la programmation Java. Pour nous permettre d’effectuer cette tâche, Android utilise un système d’interface Java appelé « Listener ».
Nous allons donc refaire l’exemple plus haut en utilisant un « OnClickListener » au lieu d’assigner directement l’événement dans le « Layout ».
Il est possible de faire un événement « OnClickListener » de deux manières. Soit on implémente l’interface « OnClickListener » dans l’activité ou on crée une classe privée qui implémente « OnClickListener » dans l’activité.
Donc, pour présenter les deux exemples, nous allons modifier le XML du layout pour retirer la propriété « onCLick » du bouton:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bouton_appuyez"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bouton_texte"
app:layout_constraintBottom_toTopOf="@+id/texte_message"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/label_nom" />
<TextView
android:id="@+id/label_nom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_nom"
app:layout_constraintBottom_toTopOf="@+id/bouton_appuyez"
app:layout_constraintEnd_toStartOf="@+id/entree_texte_nom"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/entree_texte_nom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="@+id/label_nom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/label_nom"
app:layout_constraintTop_toTopOf="@+id/label_nom" />
<TextView
android:id="@+id/texte_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bouton_appuyez" />
</androidx.constraintlayout.widget.ConstraintLayout>
La première méthode consiste en l’implémentation de l’interface « OnClickListener » directement dans l’activité. L’interface « OnClickListener » nécessite la création de la méthode redéfinie « OnCLick(View) ». Voici l’activité utilisant l’implémentation de l’interface « OnClickListener »:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button lBoutonAppuyer = findViewById(R.id.bouton_appuyez);
lBoutonAppuyer.setOnClickListener(this);
}
@Override
public void onClick(View aVue) {
TextView lTexteMessage = findViewById(R.id.texte_message);
CharSequence lMessage = getString(R.string.message);
EditText lEntreeNom = findViewById(R.id.entree_texte_nom);
CharSequence lNom = lEntreeNom.getText();
lTexteMessage.setText(lMessage.toString() + lNom.toString());
}
}
Voici maintenant le même exemple qui utilise une classe privée qui implémente « OnClickListener ». De manière similaire, l’implémentation de « OnCLickListener » nécessite que la classe privée contienne la méthode « OnClick(View) »:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button lBoutonAppuyer = findViewById(R.id.bouton_appuyez);
lBoutonAppuyer.setOnClickListener(new BoutonAppuyerClickListener());
}
private class BoutonAppuyerClickListener implements View.OnClickListener {
@Override
public void onClick(View view) {
TextView lTexteMessage = findViewById(R.id.texte_message);
CharSequence lMessage = getString(R.string.message);
EditText lEntreeNom = findViewById(R.id.entree_texte_nom);
CharSequence lNom = lEntreeNom.getText();
lTexteMessage.setText(lMessage.toString() + lNom.toString());
}
}
}
Auteur: Louis Marchand
Sauf pour les sections spécifiées autrement, ce travail est sous licence Creative Commons Attribution 4.0 International.