Big Tuto ANDROID

Chapitre 4 : L’écran de jeu, partie 1 : Graphismes de base

 

Tutoriel présenté par : Stéphane Barthélémy (Stephantasy)
Date d'écriture : 2 septembre 2017
Date de révision : -

 

   1.   Introduction

 

   Nous avons précédemment créé un projet avec une activité.

   Dans ce chapitre, nous allons créer la base de notre jeu. Nous commencerons par construire une interface graphique rudimentaire.

   À l’étape suivante, nous ajouterons le code du jeu Tic-Tac-Toe et nous verrons comment créer une interaction avec notre interface.

   Vous constaterez que cette partie du tutoriel peut être très détaillée et que nous avançons lentement.

   En effet, chaque fois que nous rencontrerons quelque chose de nouveau, je prendrai le temps d’expliquer pas à pas ce qu’il faut faire. Rassurez-vous (ou pas ! laugh), je ne me répèterai pas et on avancera de plus en plus vite ! angel

   L’application est téléchargeable sur Google Play ! Vous aurez ainsi un aperçu du projet fini. wink

 


 

 

 

   2. La grille de jeu (Interface)

 

   Bien, commençons par créer notre grille de jeu. Comme bien souvent, la construction de notre interface pourra se faire de multiples façons. J’en ai choisi une pour la grille mais il existe d’autres façons de procéder. Libre à vous d’expérimenter d’autres méthodes ! wink

   La construction d’interfaces peut être laborieuse lorsqu’on débute et il arrive qu’il nous soit impossible de créer ce que l’on souhaite, notamment à cause des centaines de paramètres que comporte chaque élément ! Parfois, utiliser simplement d’autres Layout permet de régler nos problèmes.

   Nous allons voir différentes manières de créer et modifier l’interface graphique.

 

 

      a. Les éléments de base


   L’élément de base de notre grille est un « Button ». Il contiendra « rien », un « X » ou un « O », selon l’état de la partie. Il nous faut placer ces 9 boutons de sorte à ce qu’ils forment un carré de 3x3.

 

 

   Pour cela, nous allons utiliser l’élément « TableLayout ». Ce dernier est une sorte de tableau qui permet de contenir d’autres éléments. Il se prête très bien lorsque ceux-ci sont de même taille.

 


      b. L’assemblage


   Affichons notre interface en double-cliquant sur notre activité « activity_game_screen.xml ». Sélectionnez l’onglet « Design » si ce n’est déjà fait.  

   Commençons par supprimer le « TextView » (qui contient le « Hello World! », il ne nous sert à rien. Cliquez sur « TextView » dans le Component Tree puis la touche « supprimer »).

 

 


   Ensuite, pour construire notre grille, nous insérons un « TableLayout ».

   Dans la fenêtre « Palette », cliquez sur « Layouts », puis sur « TableLayout » et glissez-le dans la fenêtre de droite. 

 


   Voyez que dans la fenêtre Component Tree, apparait notre Layout.

   Déployez-le en cliquant sur la petite flèche à sa gauche : il contient 4 TableRow, soit des lignes pour notre tableau. 

   Supprimez-en une, trois nous suffisent ! wink

 


   Nous allons placer un « Button » dans la première ligne du tableau.

   Cette fois, nous allons procéder différemment. Pour cela, cliquez sur « Images », puis prenez un « Button » et glissez-le dans le « Component Tree » cette fois.

   Déposez votre élément sur la première « TableRow ». 

 

 

   Vous venez ainsi de voir deux méthodes différentes pour insérer de nouveaux éléments dans une interface en mode Design.

 


       c. Corrections


   Avant de poursuivre notre grille, nous allons procéder à quelques ajustements. Vous remarquerez que notre interface graphique a quelques problèmes… : 

 



   Pour avoir des détails sur ces erreurs, cliquez sur le carré rouge juste au-dessus (« Show Warnings and Errors ») : 

 

 

   La fenêtre qui s’ouvre nous donne d’avantages d’informations sur les problèmes en cours. Il y a des erreurs (symbolisées par un rond rouge et un point d’exclamation) et des avertissements (symbolisées par un triangle jaune et un point d’exclamation). On peut parfois les corriger directement depuis cette interface, mais pas dans notre cas. 

   Vous remarquerez également qu’on a toujours la possibilité de supprimer l’avertissement : c’est rarement une bonne idée d’utiliser cette option, car s’il y a un problème, mieux vaut le régler que de l’ignorer ! Mais ce conseil ne vaut pas que pour Android ! wink

   Après avoir exploré les différents messages, fermez la fenêtre. 

   Pour corriger l’erreur du TableLayout, nous allons le contraindre à son parent. Pour cela, faites un clic droit sur l’élément et choisissez « Constraint Layout > Infer Constraints ».

   Cela a pour effet « d’accrocher » notre Layout à son parent (le « ConstraintLayout »). 

 

 

   Cela ne semble pas présenter d’intérêt pour le moment, mais c’est très important si on veut afficher correctement notre activité sur d’autres tailles d’écran ! Nous reviendrons sur ce point important qui fait que la création d’interface utilisateur n’est pas toujours triviale… wink

   Ensuite, Android nous avertit que notre bouton devrait utiliser une ressource pour son texte. Mais qu’est-ce que cela peut bien vouloir dire ?! surprise

 


      d. Les Strings


   Android suggère fortement de ne pas directement écrire dans nos éléments mais de toujours mettre nos textes dans un fichier XML à part et d’y faire référence.

   Ainsi, au lieu d’écrire « Button » dans le bouton (comme c’est le cas actuellement), nous devrions dire au bouton où se trouve le texte qu’il doit afficher ! 

   Cela peut paraitre être une charge de travail supplémentaire, voire inutile. Or, vous vous en doutez, ce n’est pas le cas ! cheeky

1. D’abord, cela permet d’avoir tous nos textes à la même place, dans un seul fichier. wink

2. Cela permet également de réutiliser ces textes au besoin. Ainsi, si on utilise un mot ou une phrase plusieurs fois dans notre application et qu’elle contient une faute, on la corrige dans notre fichier et tous les éléments y référant afficheront la bonne phrase ! cheeky

3. Enfin, et c’est la raison la plus importante, c’est indispensable pour créer une application multilingue ! angel Mais nous verrons cela plus tard.

   Comme presque toujours dans Android Studio, il existe plusieurs chemins pour se rendre au même endroit. Ainsi, ajouter un texte peut se faire de différentes façons.

 


         i. Ajout d’une ressource texte : méthode 1


   Cliquez sur le bouton dans l’interface (ou dans le Component Tree). Ses propriétés de base apparaissent dans la partie droite de l’écran.

   Dans la partie « TextView », en face de « Text », se trouve le texte du bouton : « Button ».

   Cliquez sur les points de suspension à droite.

 



   Apparait la fenêtre « Resources ». Cliquez sur « Add new resource » en haut à droite et choisissez « New string value.. »

 



   Dans la boite de dialogue qui apparait, entrez un identifiant pour la chaine de caractère, ainsi que sa valeur. Allons-y pour « case_tiret » et « - ». Validez avec Ok.

 



   À présent, dans la cellule « text » des propriétés, apparait la valeur « @string/case_tiret », signifiant que le texte du bouton fait référence à cette ressource.

   Vous remarquerez du même coup que l’avertissement en haut à droite du bouton a disparu ! Et le bouton affiche « - ».

 


         ii. Ajout d’une ressource texte : méthode 2


   Appuyez sur le carré rouge en haut à droite de la fenêtre de design Show Warnings and Errors »).

   Cliquez sur l’avertissement du bouton (Warning: [I18N] Hardcoded string "Button", should use ‘@string’ resource), puis cliquez sur « Extract string resource » qui apparait juste en dessous, dans la partie « Issue Explanation ». Remplissez les champs de la boite de dialogue comme précédemment. wink

 


         iii. Ajout d’une ressource texte : méthode 3


   Cette fois, nous allons directement modifier le fichier qui contient les ressources String. Et comme c’est quelque chose qu’on fera souvent, il est bon de s’en souvenir. wink

   Ouvrez le fichier de ressources : « app > res > values > strings.xml ». Le fichier contient déjà le nom de l’application. Nous allons ajouter cette ligne : 

<string name="case_tiret">-</string>

 

 

   Si vous n’êtes pas familier avec le XML, cette ligne signifie simplement qu’un élément nommé « case_tiret » contient la valeur « - ». Android doit également savoir que cet élément est de type « string ».

   Ensuite, retournez dans les propriétés du bouton et cliquez sur « … » à la ligne « text ». Dans la fenêtre « Resources », notre texte se trouve dans la liste en haut : choisissez-le puis validez.

 

 


      e. Assemblage : suite

 

   Bien, revenons à nos moutons : la grille ! cheeky

   Nous avons notre première case, mais il en manque encore 8 ! surprise Nous allons donc voir une nouvelle façon de modifier l’interface graphique : cliquez sur l’onglet « Text » en bas de l’écran.

 



   Tel que précédemment évoqué, nous avons ici la version XML de l’interface graphique. Il est tout à fait possible de créer ou modifier l’interface à partir du code XML. C’est ce que nous allons faire. (Euh, si vous ne connaissez ou n’aimez pas le XML, vous allez pleurer ! Désolé…) cheeky

   Rapide présentation : on retrouve l’élément parent « ConstraintLayout » qui contient notre « TableLayout ». Ce dernier contient 3 « TableRow » et le premier contient notre « Button ».

   Pour le moment, on ne s’occupera pas des propriétés de chacun des éléments. Sachez néanmoins que ces derniers contiennent seulement les propriétés qui ont été modifiées. Ainsi, plus on modifie de propriétés, plus ce fichier grossit ! Il va rapidement devenir très long au fur et à mesure de notre avancé. Mais rassurez-vous, on s’habitue assez vite à son contenu, qui reste (relativement) lisible et compréhensible.

   Par exemple, vous pouvez voir dans les propriétés du bouton que son texte a été modifié et qu’il contient à présent un lien vers « @string/case_tiret ».

 


         i. Duplication


   Nous allons dupliquer notre bouton pour créer ceux manquants. Pour cela, il suffit de sélectionner l’élément « Button » et de le copier juste en dessous, deux fois.

 

<TableRow
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/case_tiret" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/case_tiret" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/case_tiret" />

</TableRow>

 

   Voyez dans la partie droite que l’interface graphique se met à jour automatiquement ! surprise

   Si vous n’avez pas la représentation graphique de l’interface, cliquez sur le bouton vertical nommé « Preview » tout à droite de l’écran. Sinon vous pouvez également repasser en mode Design pour voir le résultat. wink

   Nous avons notre première ligne de trois cases. Il nous faut à présent créer les 2 autres lignes. Pour cela, nous allons procéder de la même manière en sélectionnant non plus un bouton, mais la ligne au complet, c’est-à-dire la « TableRow » ! Remplacez les deux autres « TableRow » vides par la première, qui contient les 3 boutons.

   Et voilà le résultat !

 



         ii. Aïe, des erreurs !


   Toujours dans le fichier XML, vous constaterez que la première ligne de chaque bouton est soulignée de rouge. Cela signifie qu’il y a une erreur ! angry

   Il est important de noter que chaque élément d’une interface doit avoir un identifiant unique. En effet, Android a besoin de pouvoir différencier chacun de ses éléments.

   En faisant un copier/coller nous avons répété l’identifiant. Nous allons donc corriger en donnant un identifiant différent à chacun de nos éléments. wink

   Lorsque nous interagirons avec ces éléments depuis le code Java, nous utiliserons cet identifiant pour en prendre le contrôle. Il est donc important de bien nommer nos objets ! cheeky

   À ma connaissance, il n’existe pas de règle de nommage. Personnellement, j’utilise la méthode suivante :

[Initiales de l’élément] + « _ » + [Description de son rôle]

   Ainsi, je nommerai mon TableLayout « tl_grille », mes TableRow « tr_grille_1 » et mes boutons « bt_case_0 ». 

   Vous aurez remarqué cependant que le TableLayout et les TableRow n’ont pas d’identifiant. En fait, cela n’est pas indispensable (en fait, ils en ont un, mais Android ne nous l’affiche pas). Par contre, si vous voulez y faire référence depuis le code, vous n’aurez pas le choix d’en ajouter un ! angel

   Personnellement, je trouve cela plus pratique d’identifier chacun de mes objets, car l’identifiant sera affiché dans le Component Tree en mode Design. Lorsque l’activité devient très grande, c’est beaucoup plus facile de naviguer avec des noms évocateurs qu’avec une plâtré de Layouts ayants tous le même nom !

   Pour ajouter un identifiant au TableLayout, soit vous ajoutez une ligne dans le XML, soit vous le faites depuis les propriétés de l’élément depuis le mode Design.

 

<TableLayout
    android:id="@+id/tl_grille"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    ...

 

OU


   Le résultat est évidemment le même ! laugh

   Voici la version finale de notre grille de jeu :

 



         iii. Les Warnings

 

   Il nous reste encore des Warnings non résolus : « Buttons in button bars should be borderless… ».

   Nous les ignorons pour le moment, car nous reviendrons sur le design des boutons plus tard. wink

 


      f. Assemblage : fin


   Maintenant que nous avons notre grille de jeu, nous allons ajouter deux autres éléments : 

• un bouton de remise à zéro du jeu

• un texte pour afficher le résultat de la partie

   Afin de créer un seul bloc pour ces 2 éléments, nous allons les grouper avec un « LinearLayout ». Cet élément permet de mettre côte à côte (version horizontale) ou de superposer (version verticale) les éléments dont il est le parent.

   Ajoutez un « LinearLayout (vertical) » sous la grille en glissant/déposant le Layout dans le Component Tree. Une fois que c’est fait, recommencez l’opération avec un « TextView » et un « Button » que vous placez dans le LinearLayout.

   Vous pouvez modifier l’ordre des éléments en les déplaçant à l’aide de la souris dans le Component Tree.

   Les identifiants pour les deux objets sont « tv_result » et « bt_new_game ».

   Tout comme nous l’avons fait pour le TableLayout, nous devons contraindre le LinearLayout à son parent. Pour cela, faites un clic droit sur l’élément et choisissez « Constraint Layout > Infer Constraints ».

   Nous devons également définir des ressources de type texte pour notre TextView et pour le bouton. Je vais en profiter pour commencer à mettre de l’ordre dans mon fichier strings.xml en y mettant des commentaires : 

 

<resources>

    <!-- Application -->
    <string name="app_name">My Little TicTacToe</string>

    <!-- Game Screen Activity -->
    <string name="case_tiret">-</string>
    <string name="game_result">Result</string>
    <string name="new_game">New Game</string>

</resources>

 

   Je vais mettre tous les textes en anglais pour le moment. Nous verrons comment les mettre en Français dans un chapitre ultérieur. wink

   Appliquez les textes aux deux éléments comme nous l’avons vu précédemment.

 



   Voici le résultat de notre activité pour le moment : 

 


   Cependant, il y a encore un problème… Le texte et le bouton n’apparaissent pas à l’écran ! surprise

   Malheureusement, l’organisation des éléments composant une interface graphique sera souvent un vrai casse-tête ! angry Le plus souvent, il suffit de paramétrer correctement nos éléments pour que tout rentre dans l’ordre. Parfois, il faut revoir toute l’organisation… sad

   Toujours en mode Design, sélectionnez le TableLayout (tl_grille) et passez votre souris dans le rendu de votre interface. 

 



   Voyez-vous le rectangle bleu ? frown C’est la place que prend l’élément à l’écran. Ben oui, il prend toute la place, ainsi on ne peut voir le texte et le bouton ajoutés ! crying

   Nous allons donc dire au TableLayout d’occuper moins de place et même d’en occuper le moins possible. wink

   En bas de la fenêtre « Properties », cliquez sur « View all properties ».

   Tout en haut, les propriétés « layout_width » et « layout_height » affichent « 0dp ». Cliquez dans la cellule et choisissez « wrap_content », c’est-à-dire « mets-toi à la taille des éléments contenus ».

   Faites la même chose avec le LinearLayout.

 


   Vous noterez au passage la loooongue liste de paramètres qui contiennent les objets !

   À présent, on voit nos deux groupes d’objets, mais ils ne sont pas à la bonne place :

 



         i. Le placement


   Bon, le « ConstraintLayout » est un nouvel élément et je dois vous avouer que je le découvre en même temps que vous ! cheeky Ça a l’air d’être un objet fort intéressant, mais parfois un peu capricieux. Voilà comment j’ai procédé pour arranger nos affaires :

• Faites un clic droit sur LinearLayout dans le Component Tree et faire : « Constraint Layout > Clear all constraints »

• Dans la fenêtre graphique, placez votre souris au-dessus du groupe : un cadre bleu apparait avec 4 points

• Cliquez sur le point du bas (qui devient vert) et descendez : une flèche apparait :

 



• Emmenez la flèche en bas de l’écran et lâchez : le groupe se place en bas.

 



• Répétez l’opération avec les points de chaque côté : tirez les flèches vers le bord de l’écran (point gauche vers le bord gauche, point droit vers le bord droit).

   À présent, le groupe est centré et vous obtenez cette représentation dans la fenêtre Properties à gauche : 

 

 

   Le « 8 » en dessous du carré signifie que le groupe sera toujours à 8dp* de distance minimum du bas, et ce, quel que soit la taille de l’écran.

   Les traits de chaque côté centre le groupe à l’horizontal. Si on clique sur « tl_grille », on s’aperçoit que l’objet est centré, car il est « accroché » de tous les côtés ! wink

 

 

   Voici un excellent lien pour expliquer les différentes unités utilisées dans Android Studio (nous n’utiliserons que dp) ;

   Allez, on pourrait faire bien mieux, mais on va laisser cela comme ça pour le moment ! cheeky

   Dans le cas où vous auriez eu de la misère à placer correctement les éléments (va falloir vous habituer, ça arrive tout le temps ! cheeky), voici le code XML de l’activité : 

 

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.android.meruvia.mylittletictactoe.GameScreen"
    tools:layout_editor_absoluteY="81dp"
    tools:layout_editor_absoluteX="0dp">

    <TableLayout
        android:id="@+id/tl_grille"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TableRow
            android:id="@+id/tr_grille_1"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:id="@+id/bt_case_0"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

        </TableRow>

        <TableRow
            android:id="@+id/tr_grille_2"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:id="@+id/bt_case_3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

        </TableRow>

        <TableRow
            android:id="@+id/tr_grille_3"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:id="@+id/bt_case_6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_7"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

            <Button
                android:id="@+id/bt_case_8"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/case_tiret" />

        </TableRow>

    </TableLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent">

        <TextView
            android:id="@+id/tv_result"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/game_result" />

        <Button
            android:id="@+id/bt_new_game"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/new_game" />
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

 

   Voyez comme il commence à être long ! Et ce n’est que le début, car il fera près de 400 lignes à la fin… frown

 


   3. En résumé

 

   Notre interface de base est maintenant réalisée. Bon ok, elle est laide…

   Mais soyez assuré que nous allons arranger cela très bientôt ! Au préalable, nous allons programmer notre interface afin de la faire vivre.

   Avant de rendre belle notre application, faisons en sorte qu’elle fonctionne ! angel

   A bientôt pour le chapitre 4 ! angel

         Stephantasy

   

 

 

This site uses cookies to enable you to log in. We do not store or sell any personal data. By continuing to use this website, you agree to their use. Thanks!