Ce tutoriel vise à vous faire réaliser une petite application sur Android : la recherche d'itinéraires dans le réseau ferré régional d'Île-de-France, le RER. Un noyau fonctionnel vous est fourni, qui comprend une description partielle des lignes A et B, ainsi qu'un moteur de calcul d'itinéraires : vous pourrez ainsi vous concentrer sur la réalisation de l'interface utilisateur. Nous allons afficher la liste des étapes d'un trajet, et proposer la lecture de chacune des étapes par synthèse vocale.
Durée indicative : 2 à 4 heures.
Ce tutoriel a été créé dans le cadre de l'enseignement de « mineure » Interaction homme-machine, en troisième année à Supélec, mais il peut convenir à toute personne désireuse de se mettre au développement sur Android.
C'est tout !
Une archive JAR appelée transport.jar
vous est
fournie ; elle contient le noyau fonctionnel de calcul
d'itinéraires. Les classes qu'elle contient sont organisées en deux
packages :
transport
est le package avec lequel vous interagirez.
Il contient des classes pour représenter un réseau de transport. Le
réseau lui-même est représenté par la classe Network
, dont il existe une instance, Network.PARIS_RER
. Un réseau est constitué de lignes (Line
), chaque ligne étant composée de stations (Station
). Pour rechercher un itinéraire, on utilise la méthode calculateTrip()
de Network
qui renvoie une liste ordonnée d'étapes (TripSection
). Vous n'avez pas besoin d'en savoir beaucoup plus ; vous pouvez néanmoins consulter la Javadoc.graph
est une bibliothèque de recherche de chemins dans un graphe abstrait, en utilisant l'algorithme de Dijkstra. Cette bibliothèque est utilisée uniquement en interne par transport
; vous n'aurez pas à l'utiliser directement.Ce premier exemple, en Java standard (indépendant d'Android), montre comment s'utilise le package de calcul d'itinéraires.
Station gif = Network.PARIS_RER.getStationForName("Gif-sur-Yvette"); Station nanterre = Network.PARIS_RER.getStationForName("Nanterre-Université"); System.out.println(Network.PARIS_RER.calculateTrip(gif, nanterre));
Votre application doit permettre de sélectionner une station de départ, une station de destination, et afficher un itinéraire possible. Pour cette première interface, on envisage les trois écrans suivants :
Android est fondé sur la notion d'activité, qui correspond à une tâche élémentaire, ce qui se traduit en général par une page écran. Notre application comportera donc trois activités, de deux sortes :
Sur Android, il est facile de faire en sorte qu'une activité donnée appelle avant son propre affichage une ou plusieurs autres activités, que l'on qualifiera de sous-activités.
Nous nous proposons donc d'organiser l'enchaînement des activités comme suit :
PlanTrip
(dont l'affichage correspondra à la description d'un itinéraire)PlanTrip
, et avant même son affichage, celle-ci lance successivement deux activités :
SelectStation
une première fois, pour sélectionner la station de départ,SelectStation
une second fois, pour sélectionner la station de destination.PlanTrip
finit de s'initialiser, et appelle le noyau fonctionnel pour calculer l'itinéraire (calculateTrip
)PlanTrip
affiche enfin son contenu, qui correspond à l'itinéraire qui vient d'être calculéLancez votre environnement Eclipse disposant de la chaîne d'outils Android, et créez un nouveau projet :
fr.supelec.guiderer
La copie d'écran ci-dessous résume les informations à saisir dans la boîte de dialogue :
Laissez tous les choix par défaut dans la boîte de dialogue suivante. Notamment, la case Create activity est cochée, ce qui créera une première activité pour notre application.
Vous êtes ensuite invité à choisir une icône pour votre application. Vous pouvez laisser le choix par défaut.
La boîte de dialogue suivante est consacrée au choix du type d'activité à créer. Le choix par défaut, Blank Activity, convient bien à notre application.
Enfin, une dernière étape vous demande de donner un nom à cette activité. Saisissez donc PlanTrip
dans la champ Activity Name. Vous pouvez alors cliquer sur Finish.
La capture d'écran ci-contre montre la structure du projet nouvellement créé :
src
contient les fichiers « source » Java que vous écrirez
PlanTrip
correspond à votre activité principale. Vous pouvez l'ouvrir et vérifier qu'il s'agit bien d'une sous-classe de Activity
, et qu'elle s'occupe des initialisations de base dans la méthode onCreate
, notamment la création de l'interface graphique (setContentView
).gen
contient des fichiers générés par Eclipse lui-même. Vous n'avez pas à y toucher.
R
, générée automatiquement, permet d'accéder aux ressources du projet (voir ci-dessous) depuis les programmes Java.res
contient la définition des ressources. Une ressource peut être une image, l'interface
graphique d'un écran, ou même simplement une chaîne de caractères. Il
est en effet déconseillé de mettre en dur les chaînes dans le code source,
mais plutôt de les déclarer comme ressources, ce qui facilite la
traduction des applications. Vous pouvez ouvrir les fichiers strings.xml
et activity_plan_trip.xml
, qui contiennent respectivement des chaînes et l'interface associée à l'activité PlanTrip
.
Notez que dans les deux cas, un onglet situé en bas de l'écran vous
permet de basculer entre un éditeur « sympathique » et une
vision « brute » en XML du fichier de ressources. Chacune
de ces ressources donne lieu à une entrée dans la classe R
, par exemple R.layout.activity_plan_trip
pour l'interface graphique définie dans activity_plan_trip.xml
. C'est cette entrée dans la classe R
qui permet de charger des ressources depuis le programme. Regardez dans le code source de PlanTrip.java
, vous voyez une instruction setContentView(R.layout.activity_plan_trip);
qui permet de charger l'interface de l'activité PlanTrip
.AndroidManifest.xml
définit les propriétés essentielles du projet. Notamment, dans l'onglet Application on trouve sous Application Nodes (en bas) la liste des activités définies. Il y a pour le moment une seule activité, fr.supelec.guiderer.PlanTrip
, mais nous en ajouterons d'autres.Afin de terminer la configuration de votre projet, vous allez
finalement intégrer dans le projet la bibliothèque de calcul
d'itinéraires, transport.jar
:
transport.jar
dans la fenêtre d'Eclipse, sur le dossier libs
du « Package Explorer ».
La bibliothèque est alors intégrée à votre projet. Vous pouvez aisément le vérifier : faites un un clic droit sur le nom du projet dans le volet gauche, et choisissez Build Path → Configure Build Path. Dans la fenêtre qui s'ouvre, allez sur l'onglet Libraries.
transport.jar
doit apparaître dans la rubrique Android Private Libraries.
Pour prendre en main le kit de développement, vous allez commencer par ajouter un comportement très simple à votre application. L'idée serait d'avoir un champ de texte en haut de l'écran et un bouton en bas, un appui sur le bouton déclenchant l'affichage d'un message reprenant le contenu du champ texte (type « Hello Untel »).
Ouvrez le fichier res/layout/activity_plan_trip.xml
. Vous devez voir
un écran qui comporte la barre de titre de l'application et une zone blanche comportant juste un texte du type « Hello World! ». À droite vous est présentée la hiérarchie des
widgets : par défaut, un RelativeLayout
(le conteneur principal), dans lequel se trouve un TextView
qui affiche le petit texte.
Cliquez sur le TextView
dans l'interface. Le widget doit se sélectionner. En bas de l'écran, vous devriez avoir un onglet Properties : sélectionnez-le. Si vous ne le voyez pas allez dans les menus Window → Show View → Other et sélectionnez General → Properties pour le faire apparaître.
Regardez par exemple la propriété Text, censée correspondre au texte affiché par le widget. Pourquoi voyez-vous @string/hello_world
, et non pas « Hello World! » ?
Modifiez l'interface comme suit :
TextView
.EditText
, aussi appelé Plain text).Button
).Modifiez quelques propriétés comme suit :
res/values/strings.xml
.ButtonLabel
) et Value (par exemple Go
).activity_plan_trip.xml
, sur l'objet bouton, vous pouvez cliquer sur Browse à côté du champ Text pour choisir @string/ButtonLabel
.Il reste un dernier point important à régler : chaque widget est identifié par un identificateur unique (Id). Eclipse affecte des Id par défaut : editText1
pour la première zone de texte, button1
pour le premier bouton, etc. Or pour pouvoir faire référence facilement
aux widgets depuis le code, il est préférable de choisir des
identificateurs parlants. Pour chacun de vos deux widgets, modifiez dans le panneau Properties la valeur de son Id comme suit :
buttonGo
(ce qui s'écrit @+id/buttonGo
),userInput
(ce qui s'écrit @+id/userInput
).Au terme de ces modifications, l'interface et sa hiérarchie de widgets doivent se présenter ainsi :
Testez votre première application Android !
Une fenêtre d'émulation s'ouvre, dans laquelle le système Android démarre. Patience, ça peut être long ! Après le démarrage, votre application doit s'ouvrir automatiquement.
Nous voudrions maintenant que lorsque l'utilisateur clique sur le bouton, cela affiche un petit message.
Dans le corps d'une activité, il est possible de relier du code à
différents événements de la vie de l'activité, en implémentant des
méthodes du type onNomÉvenement
. Par exemple, la méthode onCreate
est appelé tout au début de la vie de l'activité.
Nous allons donc utiliser la méthode onCreate
pour
associer au bouton un « gestionnaire de clics », c'est-à-dire
une méthode destinée à être appelée à chaque fois que l'utilisateur
appuie sur le bouton. La première chose à faire est de récupérer l'objet
Java de type Button
correspondant au bouton. On utilise pour cela la méthode findViewById
qui associe à un Id l'objet correspondant :
Button button = (Button) findViewById(R.id.buttonGo);
L'ajout d'un « gestionnaire de clics » peut se faire de la façon suivante :
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Écrire ici le code à appeler lorsque survient un clic } });
Notez l'utilisation d'une classe interne anonyme (ce qui se fait fréquemment, mais n'est pas obligatoire). Que doit faire le morceau de programme appelé lorsqu'un clic se produit ? Pour faire simple, nous allons afficher un petit message à l'écran.
Pour cela, nous utilisons une construction très utile, permettant d'afficher de petits messages sur l'écran de l'appareil :
Toast.makeText(PlanTrip.this, "Un message", Toast.LENGTH_LONG).show();
Ne pas oublier l'appel .show()
à la fin de cette instruction, sans quoi rien ne se passera... (Erreur fréquente)
Modifier le code de PlanTrip
de sorte qu'un message soit
affiché lorsque l'utilisateur presse sur le bouton. Ce message devra
comporter le texte contenu dans la zone de texte (Id userInput
, de type EditText
).
Avec ce que nous venons de voir, vous êtes capables d'obtenir une
référence à l'objet Java sous-jacent à la zone de texte. Sur cet objet,
vous pouvez appeler la méthode getText()
pour accéder au texte contenu.
Testez la nouvelle version de votre application.
Comme indiqué en introduction, l'activité PlanTrip
sera dédiée au calcul d'itinéraire et à l'affichage du résultat. Nous allons donc utiliser sa méthode onCreate
pour lancer successivement les deux sous-activités liées au choix de
l'origine et de la destination du trajet. Commençons par le choix de la
station de départ.
Créons une seconde activité, dédiée à la sélection d'une station dans une liste. Nous l'appellerons SelectStation
, et elle héritera de android.app.ListActivity
, une sorte d'activité spécialisée dans les choix parmi une liste d'éléments.
Pour créer cette nouvelle activité (i.e. une classe Java) :
fr.supelec.guiderer
(dans src
).SelectStation
.android.app.ListActivity
(il est possible de ne taper que ListActivity
, puis en tapant Ctrl+Espace le champ se complète automatiquement).Nous allons ajouter la méthode onCreate
de SelectStation
, de façon à configurer cette nouvelle activité, notamment lui spécifier quelle est la liste à afficher lorsqu'elle démarre...
Placez-vous dans la classe SelectStation
et tapez onCreate
puis Ctrl+Espace. Un menu s'ouvre avec différentes possibilités.
Choisissez la première. Eclipse génère un squelette pour la méthode,
dont la première instruction est un appel à la version par défaut de la
méthode (super.onCreate(...)
). Toutes les instructions que vous ajouterez dans onCreate
devront être placés après cet appel.
Il s'agit maintenant d'indiquer à l'activité SelectStation
qu'elle doit afficher la liste de toutes les stations du RER. Il est
facile d'obtenir cette dernière à partir de la bibliothèque fournie, via
un appel Network.PARIS_RER.getAllStations()
qui rend un objet de type List<Station>
.
Sous Android (comme sous Swing), les composants d'interface (widgets) sont reliés aux données métier sous-jacentes via des adaptateurs. Plus spécifiquement, un composant de type « liste » est conçu pour utiliser un ArrayAdapter
, lequel sait aller s'interfacer avec un objet List
de Java.
Voici comment créer l'adaptateur dont vous avez besoin :
ArrayAdapter<Station> adapter = new ArrayAdapter<Station>( this, android.R.layout.simple_list_item_1, Network.PARIS_RER.getAllStations());
Les arguments du constructeur ArrayAdapter
sont les suivants :
android.R
, et non simplement R
, car nous faisons référence à des ressources génériques d'Android, pas à des ressources spécifiques à votre projet.List
, dans lequel les données doivent être puisées.Après avoir créé un tel adaptateur, on peut en faire l'adaptateur utilisé par l'activité liste avec l'instruction :
setListAdapter(adapter);
Modifiez le code de onCreate
de façon à ce qu'elle mette en place un tel adaptateur.
Il reste une chose à faire avant que nous ayions achevé une première version de notre activité : la déclarer dans le manifeste.
Pour ajouter SelectStation
au manifeste :
AndroidManifest.xml
.res/values/strings.xml
.SelectStationLabel
) et Value (par exemple Choisissez votre station
).AndroidManifest.xml
, vous pouvez cliquer sur Browse à côté du champ Label pour choisir @string/SelectStationLabel
.Nous possédons maitenant une activité SelectStation
, qui
devrait être capable d'afficher la liste des stations du réseau.
Cependant, avant de pouvoir l'essayer, il faut faire en sorte que cette
activité soit appelée en tant que sous-activité par notre activité
principale, PlanTrip
. Cet appel va être ajouté à la fin de la méthode onCreate
, de façon à se faire dès que l'application démarre.
Pour démarrer une nouvelle activité, on utilise un objet Intent
.
Les intents sont utilisés aussi bien pour démarrer une nouvelle
activité (et lui transmettre des paramètres) que pour permettre à une
activité de transmettre un résultat à son activité parente.
Créer un nouvel intent pour démarrer l'activité SelectStation
est très simple :
Intent intent = new Intent(PlanTrip.this, SelectStation.class);
Reste alors à effectuer le démarrage proprement dit de l'activité. Il existe plusieurs méthodes du type startActivity...
.
Ici, l'activité « fille » que nous lançons doit nous
fournir un résultat, la station sélectionnée. Nous utilisons donc la
méthode startActivityForResult
, à laquelle nous devons
donner un identifiant numérique, identifiant que nous utiliserons plus
tard pour reconnaître le résultat que la sous-activité nous donnera.
Nous posons donc une constante
private final static int ORIGIN_STATION_SUBACTIVITY = 1;
Et l'appel à startActivityForResult
s'écrit :
startActivityForResult(intent, ORIGIN_STATION_SUBACTIVITY);
Modifiez en conséquence le code de PlanTrip
.
Exécutez votre programme.
Vous devriez alors voir une liste de toutes les stations connues du système. Cependant, rien ne se passe encore lorsque vous sélectionnez une station... Nous allons tout de suite aborder la réaction à ces interactions.
Dans SelectStation
, il est facile de répondre aux « clics » sur des éléments de la liste : il suffit d'ajouter une méthode onListItemClick
. Son contenu sera appelé lorsqu'on appuiera sur l'un des éléments.
Ajoutez la méthode onListItemClick
à la classe SelectStation
. Comme précédemment, tapez le nom de la méthode directement dans la classe, puis Ctrl+Espace pour compléter et générer le squelette.
onListItemClick
reçoit notamment un paramètre position
: il s'agit de l'indice de l'élément sélectionné dans la liste.
Avant de poursuivre la construction de notre application, nous allons commencer par vérifier que les appuis sur des noms de stations sont bien interceptés.
Ajoutez un affichage de message dans la méthode onListItemClick
. Le message doit comporter la valeur du paramètre position
. (Note : souvenez-vous de Toast.makeText
...)
Essayez la nouvelle version de l'application. Lorsqu'on appuie sur un élément de la liste, on doit obtenir un affichage comme ci-contre.
En réalité, plutôt qu'afficher un message, nous souhaitons fournir un résultat à l'activité principale.
Pour attribuer un résultat à l'activité, et terminer l'activité, on utilise le code suivant :
setResult(RESULT_OK, result); // attribue un résultat finish(); // met fin à l'activié (retour)
Dans setResult
, RESULT_OK
est un code conventionnel, défini au niveau d'Android, indiquant le succès de l'activité en cours. En revanche, la variable result
doit contenir le résultat que nous souhaitons transmettre. Cette variable result
doit être de type Intent
, type que nous avons déjà manié. Nous devons donc préalablement déclarer cette variable result
, et créer un nouvel intent :
Intent result = new Intent();
Le mécanisme pour transmettre des paramètres via des intents est celui des extras, des couples clé-valeur que l'on peut associer à un intent via la méthode putExtra
.
Dans un tel couple, la clé doit être une chaîne : il faut donc
convenir entre activités de la signification des clés. Si l'on pose une
constante STATION_ID
:
public static final String STATION_ID = "StationID";
alors on peut utiliser putExtra
ainsi :
result.putExtra(STATION_ID, position);
Modifier la méthode onListItemClick
de façon à ce qu'elle transmette en résultat l'indice de l'élément choisi, et mette fin à l'activité.
Vous pouvez réexécuter votre application à ce stade. Lorsque l'on
sélectionne une station, la sous-activité se termine (c'est ce que fait finish()
) et l'on revient sur l'activité principale. On peut alors voir son interface (champ texte et bouton « Go »).
À ce stade, la sous-activité SelectStation
renvoie bien
un résultat (l'indice de la station choisie dans la liste de toutes les
stations), mais ce résultat n'est pas encore récupéré par l'activité
principale PlanTrip
. Pour récupérer des résultats, cette dernière doit mettre en place une nouvelle méthode de traitement d'événements, appelée onActivityResult
.
Créez une nouvelle méthode onActivityResult
dans PlanTrip
en suivant la procédure « habituelle » (taper le nom ou le
début du nom, puis Ctrl+Espace). Vous devez obtenir une méthode avec la
signature suivante :
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { ... }
Détaillons les arguments de cette méthode :
requestCode
permet de faire le lien entre ce résultat et un lancement de sous-activité par startActivityForResult
. En effet, une activité peut lancer plusieurs sous-activités de façon asynchrone ; elle doit donc pouvoir identifier les résultats de chacune d'entre elles. C'est pourquoi nous avons donné un paramètre ORIGIN_STATION_SUBACTIVITY
à startActivityForResult
: c'est le request code qui permet de faire le lien entre le résultat et le lancement de la sous-activité.resultCode
est le code indiqué par la sous-activité dans setResult
. Dans notre cas, ce devrait être RESULT_OK
.data
est un objet Intent
qui contient les résultats additionnels transmis par setResult
.Voici donc ce que doit faire cette méthode onActivityResult
:
resultCode
est bien RESULT_OK
requestCode
est ORIGIN_STATION_SUBACTIVITY
data
) et y chercher la valeur associée à la clé SelectStation.STATION_ID
Écrivez une première version du code de onActivityResult
,
sans pour le moment lancer la sous-activité de choix de la destination,
mais en vous contentant d'afficher un message à l'écran comportant le
nom de la station choisie. Cela vous permettra de vérifier que le
fonctionnement est correct.
Voici comment récupérer la valeur entière associée à une clé dans l'intent data
:
int valeur = data.getExtras().getInt(clé);
Après sélection d'une station, vous devez obtenir le résultat ci-contre.
Les techniques vues jusqu'ici peuvent être appliquées de même pour le choix de la station de destination. De plus, l'activité SelectStation
peut être réutilisée telle-quelle ; elle sera juste appelée deux fois en tant que sous-activité de PlanTrip
.
Modifiez le code de votre application de sorte qu'après le choix de la station de départ, l'utilisateur se voit directement proposer le choix de la station de destination. Lorsque les deux choix ont été effectués, un message doit apparaître à l'écran avec le nom des deux stations choisies.
À ce stade, vous devez disposer de références vers la station de
départ et la station d'arrivée. Supposons que vous ayez ainsi deux
variables origin
et destination
de type Station
.
La bibliothèque fournie permet de calculer facilement un itinéraire
entre ces deux stations grâce à l'expression suivante :
Network.PARIS_RER.calculateTrip(origin, destination)
Le résultat est de type List<TripSection>
. Un objet TripSection
correspond à une étape élémentaire du trajet, d'une station de départ (getOrigin()
) à une station d'arrivée (getDestination()
), situées toutes deux sur la même ligne (getLine()
).
Modifiez votre application de sorte qu'après le choix de la station
de départ et de la station d'arrivée, le contenu du trajet calculé
apparaisse dans la zone de texte de l'activité PlanTrip
.
On ne cherche pas pour le moment à réaliser un affichage « sympathique », aussi souvenez-vous qu'un objet de type List<TripSection>
dispose d'une méthode toString()
.
Le résultat doit ressembler à l'écran ci-contre.
Nous allons maintenant modifier notre interface de façon à mettre en
place un affichage plus ergonomique du trajet calculé. Afficher une
liste d'étapes est a priori bien adapté, aussi nous utiliserons un widget ListView
.
Modifiez le layout de l'interface de sorte à remplacer la zone de texte (EditText
) par une liste (ListView
).
Pour définir le contenu d'une ListView
, utilisez comme tout à l'heure la méthode setAdapter
, à laquelle vous fournirez un objet de type ArrayAdapter<TripSection>
. Pour le layout des items de la liste, prenez dans un premier temps simple_list_item_1
comme précédemment ; nous allons améliorer l'interface dans un instant.
Le résultat doit ressembler à la copie d'écran ci-contre.
La vue liste améliore la présentation, mais il faudrait afficher
chaque widget de façon plus élégante que ce qui est fait actuellement.
Pour cela, au lieu d'utiliser la présentation standard simple_list_item_1
, nous allons créer notre propre présentation.
Pour commencer, il s'agit de mettre en forme cette présentation, c'est-à-dire créer un layout adéquat.
Créez un nouveau layout avec File > New > Android XML File. Sélectionnez le type Layout et appelez le fichier resultitem.xml
. Gardez la disposition par défaut LinearLayout. Vous obtenez un layout vide, que vous devez compléter.
Chaque item de la liste doit afficher les caractéristiques d'une étape, donc la ligne, la gare de départ et la gare d'arrivée.
Il vous est donc suggéré de créer trois zones de texte (widgets TextView
) d'identifiants @id+/TextLine
, @id+/TextOrigin
, @id+/TextDestination
. Voici une proposition de présentation (que vous êtes libres d'adapter à votre idée) :
Il reste maintenant à connecter cette nouvelle disposition et les
données à présenter. Pour ce faire, vous allez créer un adaptateur
personnalisé pour remplacer le ArrayAdapter
standard.
Dans votre package fr.supelec.guiderer
, faites New > Class :
TripAdapter
,android.widget.ArrayAdapter<transport.TripSection>
.Créez un constructeur public TripAdapter(Context context, int resource, List<TripSection> items)
:
super(context, resource, items)
resource
dans un attribut : vous en aurez besoin plus tard.Surchargez la méthode public View getView(int position, View convertView, ViewGroup parent) {
Cette méthode est chargée de deux choses :
convertView
contient la vue si elle a déjà été créée, sinon convertView
vaut null
).position
dans la vue.Vous pouvez utiliser le code suivant pour la création de la vue, ou la récupération d'une instance déjà existante :
LinearLayout tripView; if(convertView == null) { tripView = new LinearLayout(getContext()); LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); li.inflate(resource, tripView, true); } else { tripView = (LinearLayout) convertView; }
Ensuite, vous n'avez plus qu'à mettre le texte voulu dans vos trois zones de texte. Avec ce que vous avez déjà acquis, vous devez facilement vous en sortir.
Dans PlanTrip
, pensez à modifier la création du ArrayAdapter
en TripAdapter
.
Afin de tester la synthèse vocale, nous souhaitons qu'en cliquant sur une étape d'un itinéraire, un texte soit prononcé. Pour pouvoir utiliser la synthèse vocale, il faut commencer par vérifier qu'elle est bien disponible sur notre appareil. On va donc modifier l'initialisation de PlanTrip
de sorte à vérifier la présence de la synthèse en premier lieu.
À la fin de la méthode onCreate
, remplacez
Intent intent = new Intent(PlanTrip.this, SelectStation.class); startActivityForResult(intent, ORIGIN_STATION_SUBACTIVITY);
par :
Intent checkTTSIntent = new Intent(); checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); startActivityForResult(checkTTSIntent, CHECK_TTS_SUBACTIVITY);
sans oublier d'ajouter l'attribut suivant à la class :
private final static int CHECK_TTS_SUBACTIVITY = 3;
Lorsque l'activité de vérification sera terminée, nous pourrons créer un objet TextToSpeech
. Modifiez le début de la méthode onActivityResult
comme suit :
if(requestCode == CHECK_TTS_SUBACTIVITY) { if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) { tts = new TextToSpeech(this, this); } else { Intent installTTSIntent = new Intent(); installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); startActivity(installTTSIntent); } }
Il faut que vous déclariez un attribut tts
dans votre classe :
private TextToSpeech tts;
Il faut aussi que votre classe implémente l'interface OnInitListener
. Cela vous force à ajouter une méthode onInit
:
public void onInit(int initStatus) { if (initStatus == TextToSpeech.SUCCESS) { if(tts.isLanguageAvailable(Locale.FRENCH)==TextToSpeech.LANG_AVAILABLE) { tts.setLanguage(Locale.FRENCH); } } else if (initStatus == TextToSpeech.ERROR) { Toast.makeText(this, "Synthèse vocale indisponible", Toast.LENGTH_LONG).show(); tts = null; } // démarrage de la sélection des stations de RER... Intent intent = new Intent(PlanTrip.this, SelectStation.class); startActivityForResult(intent, ORIGIN_STATION_SUBACTIVITY); }
Cette méthode sera appelée après initialisation du moteur de synthèse. Nous essayons de basculer en français, et nous lançons (enfin !) l'activité de sélection des stations de RER.
À ce stade, vous pouvez faire prononcer un texte donné en écrivant :
tts.speak("Un texte", TextToSpeech.QUEUE_FLUSH, null);
Par exemple, pour prononcer un texte quand on clique sur l'une des étapes d'un trajet calculé :
listStations.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { tts.speak("Un texte", TextToSpeech.QUEUE_FLUSH, null); } });
À ce stade, vous devez alors avoir obtenu une application conforme au cahier des charges de départ ! Félicitations !