Comme vous le savez probablement, une des forces d'Android est son côté personnalisable. Un des exemples les plus probants est qu'il est tout à fait possible de choisir les éléments qui se trouvent sur l'écran d'accueil. On y trouve principalement des icônes, mais les utilisateurs d'Android sont aussi friands de ce qu'on appelle les « AppWidgets », applications miniatures destinées à être utilisées dans d'autres applications. Ce AppWidgets permettent d'améliorer une application à peu de frais en lui ajoutant un compagnon permanent. De plus, mettre un AppWidget sur son écran d'accueil permet à l'utilisateur de se rappeler l'existence de votre produit et par conséquent d'y accéder plus régulièrement. Par ailleurs, les AppWidgets peuvent accorder un accès direct à certaines fonctionnalités de l’application sans avoir à l'ouvrir, ou même ouvrir l'application ou des portions de l'application.
Un AppWidget est divisé en plusieurs unités, toutes nécessaires pour fonctionner. On retrouve tout d'abord une interface graphique qui détermine quelles sont les vues qui le composent et leurs dispositions. Ensuite, un élément gère le cycle de vie de l'AppWidget et fait le lien entre l'AppWidget et le système. Enfin, un dernier élément est utilisé pour indiquer les différentes informations de configuration qui déterminent certains aspects du comportement de l'AppWidget. Nous allons voir tous ces éléments, comment les créer et les manipuler.
- L'interface graphique
- Définir les propriétés
- Le code
- Déclarer l'AppWidget dans le Manifest
- Application : un AppWidget pour accéder aux tutoriels du Site du Zéro
L'interface graphique
La première chose à faire est de penser à l'interface graphique qui représentera la mise en page de l'AppWidget. Avant de vous y mettre, n'oubliez pas de réfléchir un peu. Si votre AppWidget est l'extension d'une application, faites en sorte de respecter la même charte graphique de manière à assurer une véritable continuité dans l'utilisation des deux programmes. Le pire serait qu'un utilisateur ne reconnaisse pas votre application en voyant un AppWidget et n'arrive pas à associer les deux dans sa tête.
Vous allez comme d'habitude devoir créer un layout dans le répertoire res/layout/
. Cependant, il ne peut pas contenir toutes les vues qui existent. Voici les layouts acceptés :
FrameLayout
LinearLayout
RelativeLayout
… ainsi que les widgets acceptés :
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
Ne confondez pas les widgets, ces vues qui ne peuvent pas contenir d'autres vues, et les AppWidgets. Pour être franc, vous trouverez le terme « widget » utilisé pour désigner des AppWidgets, ce qui est tout à fait correct, mais pour des raisons pédagogiques je vais utiliser AppWidget.
Pourquoi uniquement ces vues-là ?
Toutes les vues ne sont pas égales. Ces vues-là sont des RemoteViews
, c'est-à-dire qu'on peut y avoir accès quand elles se trouvent dans un autre processus que celui dans lequel on travaille. Au lieu de désérialiser une hiérarchie de vues comme on le fait d'habitude, on désérialisera un layout dans un objet de type RemoteViews
. Il est ainsi possible de configurer les vues dans notre receiver pour les rendre accessibles à une autre activité, celle de l'écran d'accueil par exemple.
L'une des contreparties de cette technique est que vous ne pouvez pas implémenter facilement la gestion des évènements avec un OnClickListener
par exemple. À la place, on va attribuer un PendingIntent
à notre RemoteViews
de façon à ce qu'il sache ce qu'il doit faire en cas de clic, mais nous le verrons plus en détail bientôt.
Enfin, sachez qu'on ne retient pas de référence à des RemoteViews
, tout comme on essaie de ne jamais faire de référence à des context
.
Voici un exemple standard :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:gravity="center" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/background" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/title" android:textColor="#ff00ff00" android:text="@string/title" android:background="@drawable/black" android:gravity="center" /> <Button android:id="@+id/bouton" android:layout_width="146dip" android:layout_height="72dip" android:text="@string/bouton" android:background="@drawable/black" android:gravity="center" /> </LinearLayout> |
Vous remarquerez que j'ai utilisé des valeurs bien précises pour le bouton. En effet, il faut savoir que l'écran d'accueil est divisé en cellules. Une cellule est l'unité de base de longueur dans cette application, par exemple une icône fait une cellule de hauteur et une cellule de largeur. La plupart des écrans possèdent quatre cellules en hauteur et quatre cellules en largeur, ce qui donne $4 \times 4 = 16$ cellules en tout.
Pour déterminer la mesure que vous désirez en cellules, il suffit de faire le calcul $(74 \times N) - 2$ avec N
le nombre de cellules voulues. Ainsi, j'ai voulu que mon bouton fasse une cellule de hauteur, ce qui donne $(74 \times 1) - 2 = 72$ dp.
Vous vous rappelez encore les dp
? C'est une unité qui est proportionnelle à la résolution de l'écran, contrairement à d'autres unités comme le pixel par exemple. Imaginez, sur un écran qui a 300 pixels en longueur, une ligne qui fait 150 pixels prendra la moitié de l'écran, mais sur un écran qui fait 1500 pixels de longueur elle n'en fera qu'un dixième ! En revanche, avec les dp (ou dip, c'est pareil), Android calculera automatiquement la valeur en pixels pour adapter la taille de la ligne à la résolution de l'écran.
Définir les propriétés
Maintenant, il faut préciser différents paramètres de l'AppWidget dans un fichier XML. Ce fichier XML représente un objet de type AppWidgetProviderInfo
.
Tout d'abord, la racine est de type <appwidget-provider>
et doit définir l'espace de nommage android
, comme ceci :
1 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" /> |
Vous pouvez définir la hauteur minimale de l'AppWidget avec android:minHeight
et sa largeur minimale avec android:minWidth
. Les valeurs à indiquer sont en dp comme pour le layout.
Ensuite, on utilise android:updatePeriodMillis
pour définir la fréquence de mise à jour voulue, en millisecondes. Ainsi, android:updatePeriodMillis="60000"
fait une minute, android:updatePeriodMillis="3600000"
fait une heure, etc. Puis on utilise android:initialLayout
pour indiquer la référence au fichier XML qui indique le layout de l'AppWidget. Enfin, vous pouvez associer une activité qui permettra de configurer l'AppWidget avec android:configure
.
Voici par exemple ce qu'on peut trouver dans un fichier du genre res/xml/appwidget_info.xml
:
1 2 3 4 5 6 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minHeight="220dp" android:minWidth="146dp" android:updatePeriodMillis="3600000" android:initialLayout="@layout/widget" android:configure="sdz.chapitreQuatre.WidgetExample.AppWidgetConfigure" /> |
Le code
Le receiver
Le composant de base qui permettra l’interaction avec l'AppWidget est AppWidgetProvider
. Il permet de gérer tous les évènements autour de la vie de l'AppWidget. AppWidgetProvider
est une classe qui dérive de BroadcastReceiver
, elle va donc recevoir les divers broadcast intents qui sont émis et qui sont destinés à l'AppWidget. On retrouve quatre évènements pris en compte : l'activation, la mise à jour, la désactivation et la suppression. Comme d'habitude, chaque période de la vie d'un AppWidget est représentée par une méthode de callback.
La mise à jour
La méthode la plus importante est celle relative à la mise à jour, vous devrez l'implémenter chaque fois. Il s'agit de public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
avec comme paramètres :
- Le
context
dans lequel le receiver s'exécute. appWidgetManager
représente le gestionnaire des AppWidgets, il permet d'avoir des informations sur tous les AppWidgets disponibles sur le périphérique et de les mettre à jour.- Les identifiants des AppWidgets à mettre à jour sont contenus dans
appWidgetIds
.
Cette méthode sera appelée à chaque expiration du délai updatePeriodMillis
.
Ainsi, dans cette méthode, on va récupérer l'arbre de RemoteViews
qui constitue l'interface graphique et on mettra à jour les vues qui ont besoin d'être mises à jour. Pour récupérer un RemoteViews
, on utilisera le constructeur RemoteViews(String packageName, int layoutId)
qui a besoin du nom du package du context
dans packageName
(on le récupère facilement avec la méthode String getPackageName()
de Context
) et l'identifiant du layout dans layoutId
.
Vous pouvez ensuite manipuler n'importe quelle vue qui se trouve dans cette hiérarchie à l'aide de diverses méthodes de manipulation. Par exemple, pour changer le texte d'un TextView
, on fera void setTextViewText(int viewId, CharSequence text)
avec viewId
l'identifiant du TextView
et le nouveau text
. Il n'existe bien entendu pas de méthodes pour toutes les méthodes que peuvent exécuter les différentes vues, c'est pourquoi RemoteViews
propose des méthodes plus génériques qui permettent d'appeler des méthodes sur les vues et de leur passer des paramètres. Par exemple, un équivalent à :
1 | remote.setTextViewText(R.id.textView, "Machin") |
… pourrait être :
1 | remote.setString(R.id.textView, "setText", "Machin") |
On a en fait fait appel à la méthode setText
de TextView
en lui passant un String
.
Maintenant que les modifications ont été faites, il faut les appliquer. En effet, elles ne sont pas effectives toutes seules, il vous faudra utiliser la méthode void updateAppWidget(int appWidgetId, RemoteViews views)
avec appWidgetId
l'identifiant du widget qui contient les vues et views
la racine de type RemoteViews
.
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // Pour chaque instance de notre AppWidget for (int i = 0 ; i < appWidgetIds.length ; i++) { // On crée la hiérarchie sous la forme d'un RemotViews RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget_layout); // On récupère l'identifiant du widget actuel int id = appWidgetIds[i]; // On met à jour toutes les vues du widget appWidgetManager.updateAppWidget(id, views); } } |
Les autres méthodes
Tout d'abord, comme AppWidgetProvider
dérive de BroadcastReceiver
, on pourra retrouver les méthodes de BroadcastReceiver
, dont public void onReceive(Context context, Intent intent)
qui est activé dès qu'on reçoit un broadcast intent.
La méthode public void onEnabled(Context context)
n'est appelée que la première fois qu'un AppWidget est créé. Si l'utilisateur place deux fois un AppWidget sur l'écran d'accueil, alors cette méthode ne sera appelée que la première fois. Le broadcast intent associé est APP_WIDGET_ENABLED
.
Ensuite, la méthode public void onDeleted(Context context, int[] appWidgetIds)
est appelée à chaque fois qu'un AppWidget est supprimé. Il répond au broadcast intent APP_WIDGET_DELETED
.
Et pour finir, quand la toute dernière instance de votre AppWidget est supprimée, le broadcast intent APP_WIDGET_DISABLED est envoyé afin de déclencher la méthode public void onDisabled(Context context)
.
L'activité de configuration
C'est très simple, il suffit de créer une classe qui dérive de PreferenceActivity
comme vous savez déjà le faire.
Déclarer l'AppWidget dans le Manifest
Le composant de base qui représente votre application est le AppWidgetProvider
, c'est donc lui qu'il faut déclarer dans le Manifest. Comme AppWidgetProvider
dérive de BroadcastReceiver
, il faut déclarer un nœud de type <receiver>
. Cependant, contrairement à un BroadcastReceiver
classique où l'on pouvait ignorer les attributs android:icon
et android:label
, ici il vaut mieux les déclarer. En effet, ils sont utilisés pour donner des informations sur l'écran de sélection des widgets :
1 2 3 4 5 6 | <receiver android:name=".AppWidgetProviderExample" android:label="@string/nom_de_l_application" android:icon="@drawable/icone"> … </receiver> |
Il faut bien entendu rajouter des filtres à intents dans ce receiver, sinon il ne se lancera jamais. Le seul broadcast intent qui nous intéressera toujours est android.appwidget.action.APPWIDGET_UPDATE
qui est envoyé à chaque fois qu'il faut mettre à jour l'AppWidget :
1 2 3 | <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> |
Ensuite, pour définir l'AppWidgetProviderInfo
, il faut utiliser un élément de type <meta-data>
avec les attributs android:name
qui vaut android.appwidget.provider
et android:resource
qui est une référence au fichier XML qui contient l'AppWidgetProviderInfo
:
1 2 | <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" /> |
Ce qui donne au complet :
1 2 3 4 5 6 7 8 9 10 | <receiver android:name=".AppWidgetProviderExample" android:label="@string/nom_de_l_application" android:icon="@drawable/icone"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_info" /> </receiver> |
Application : un AppWidget pour accéder aux tutoriels du Site du Zéro
On va créer un AppWidget qui ne sera pas lié à une application. Il permettra de choisir quel tutoriel du Site du Zéro l'utilisateur souhaite visualiser.
Résultat attendu
Mon AppWidget ressemble à la figure suivante. Évidemment, vous pouvez modifier le design pour obtenir quelque chose de plus… esthétique. Là, c'est juste pour l'exemple.
On peut cliquer sur le titre du tutoriel pour lancer le tutoriel dans un navigateur. Les deux boutons permettent de naviguer dans la liste des tutoriels disponibles.
Aspect technique
Pour permettre aux trois boutons (celui qui affiche le titre est aussi un bouton) de réagir aux clics, on va utiliser la méthode void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
de RemoteViews
avec viewId
l'identifiant du bouton et pendingIntent
le PendingIntent
qui contient l'Intent
qui sera exécuté en cas de clic.
Détail important : pour ajouter plusieurs évènements de ce type, il faut différencier chaque Intent
en leur ajoutant un champ Données
différent. Par exemple, j'ai rajouté des données de cette manière à mes intents : intent.setData(Uri.withAppendedPath(Uri.parse("WIDGET://widget/id/"), String.valueOf(Identifiant_de_cette_vue)))
. Ainsi, j'obtiens des données différentes pour chaque intent, même si ces données ne veulent rien dire.
Afin de faire en sorte qu'un intent lance la mise à jour de l'AppWidget, on lui mettra comme action AppWidgetManager.ACTION_APPWIDGET_UPDATE
et comme extra les identifiants des widgets à mettre à jour ; l'identifiant de cet extra sera AppWidgetManager.EXTRA_APPWIDGET_ID
:
1 2 | intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); |
Comme AppWidgetProvider
dérive de BroadcastReceiver
, vous pouvez implémenter void onReceive(Context context, Intent intent)
pour gérer chaque intent qui lance ce receiver.
Ma solution
Tout d'abord je déclare mon layout :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?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="@drawable/background" > <Button android:id="@+id/link" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="30" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="10dp" android:background="@android:color/transparent" android:textColor="#FFFFFF" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="70" android:orientation="horizontal" android:layout_marginBottom="10dp" > <Button android:id="@+id/previous" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_weight="50" android:text="Prec." /> <Button android:id="@+id/next" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:layout_weight="50" android:text="Suivant" /> </LinearLayout> </LinearLayout> |
La seule chose réellement remarquable est que le fond du premier bouton est transparent grâce à l'attribut android:background="@android:color/transparent"
.
Une fois mon interface graphique créée, je déclare mon AppWidgetProviderInfo
:
1 2 3 4 5 | <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="144dip" android:minHeight="144dip" android:updatePeriodMillis="3600000" android:initialLayout="@layout/widget" /> |
Je désire qu'il fasse au moins 2 cases en hauteur et 2 cases en largeur, et qu'il se rafraîchisse toutes les heures.
J'ai ensuite créé une classe très simple pour représenter les tutoriels :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package sdz.chapitrequatre.tutowidget; import android.net.Uri; public class Tuto { private String intitule = null; private Uri adresse = null; public Tuto(String intitule, String adresse) { this.intitule = intitule; this.adresse = Uri.parse(adresse); } public String getIntitule() { return intitule; } public void setIntitulé(String intitule) { this.intitule = intitule; } public Uri getAdresse() { return adresse; } public void setAdresse(Uri adresse) { this.adresse = adresse; } } |
Puis, le receiver associé à mon AppWidget :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | package sdz.chapitrequatre.tutowidget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.widget.RemoteViews; public class TutoWidget extends AppWidgetProvider { // Les tutos que propose notre widget private final static Tuto TUTO_ARRAY[] = { new Tuto("Apprenez à créer votre site web avec HTML5 et CSS3", "http://www.siteduzero.com/tutoriel-3-13666-apprenez-a-creer-votre-site-web-avec-html5-et-css3.html"), new Tuto("Apprenez à programmer en C !", "http://www.siteduzero.com/tutoriel-3-14189-apprenez-a-programmer-en-c.html"), new Tuto("Créez des applications pour Android", "http://www.siteduzero.com/tutoriel-3-554364-creez-des-applications-pour-android.html") }; // Intitulé de l'extra qui contient la direction du défilé private final static String EXTRA_DIRECTION = "extraDirection"; // La valeur pour défiler vers la gauche private final static String EXTRA_PREVIOUS = "previous"; // La valeur pour défiler vers la droite private final static String EXTRA_NEXT = "next"; // Intitulé de l'extra qui contient l'indice actuel dans le tableau des tutos private final static String EXTRA_INDICE = "extraIndice"; // Action qui indique qu'on essaie d'ouvrir un tuto sur internet private final static String ACTION_OPEN_TUTO = "sdz.chapitreQuatre.tutowidget.action.OPEN_TUTO"; // Indice actuel dans le tableau des tutos private int indice = 0; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); // Petite astuce : permet de garder la longueur du tableau sans accéder plusieurs fois à l'objet, d'où optimisation final int length = appWidgetIds.length; for (int i = 0 ; i < length ; i++) { // On récupère le RemoteViews qui correspond à l'AppWidget RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); // On met le bon texte dans le bouton views.setTextViewText(R.id.link, TUTO_ARRAY[indice].getIntitule()); // La prochaine section est destinée au bouton qui permet de passer au tuto suivant //******************************************************** //*******************NEXT********************************* //******************************************************** Intent nextIntent = new Intent(context, TutoWidget.class); // On veut que l'intent lance la mise à jour nextIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); // On n'oublie pas les identifiants nextIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); // On rajoute la direction nextIntent.putExtra(EXTRA_DIRECTION, EXTRA_NEXT); // Ainsi que l'indice nextIntent.putExtra(EXTRA_INDICE, indice); // Les données inutiles mais qu'il faut rajouter Uri data = Uri.withAppendedPath(Uri.parse("WIDGET://widget/id/"), String.valueOf(R.id.next)); nextIntent.setData(data); // On insère l'intent dans un PendingIntent PendingIntent nextPending = PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Et on l'associe à l'activation du bouton views.setOnClickPendingIntent(R.id.next, nextPending); // La prochaine section est destinée au bouton qui permet de passer au tuto précédent //******************************************************** //*******************PREVIOUS***************************** //******************************************************** Intent previousIntent = new Intent(context, TutoWidget.class); previousIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); previousIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); previousIntent.putExtra(EXTRA_DIRECTION, EXTRA_PREVIOUS); previousIntent.putExtra(EXTRA_INDICE, indice); data = Uri.withAppendedPath(Uri.parse("WIDGET://widget/id/"), String.valueOf(R.id.previous)); previousIntent.setData(data); PendingIntent previousPending = PendingIntent.getBroadcast(context, 1, previousIntent, PendingIntent.FLAG_UPDATE_CURRENT); views.setOnClickPendingIntent(R.id.previous, previousPending); // La section suivante est destinée à l'ouverture d'un tuto dans le navigateur //******************************************************** //*******************LINK********************************* //******************************************************** // L'intent ouvre cette classe même… Intent linkIntent = new Intent(context, TutoWidget.class); // Action l'action ACTION_OPEN_TUTO linkIntent.setAction(ACTION_OPEN_TUTO); // Et l'adresse du site à visiter linkIntent.setData(TUTO_ARRAY[indice].getAdresse()); // On ajoute l'intent dans un PendingIntent PendingIntent linkPending = PendingIntent.getBroadcast(context, 2, linkIntent, PendingIntent.FLAG_UPDATE_CURRENT); views.setOnClickPendingIntent(R.id.link, linkPending); // Et il faut mettre à jour toutes les vues appWidgetManager.updateAppWidget(appWidgetIds[i], views); } } @Override public void onReceive(Context context, Intent intent) { // Si l'action est celle d'ouverture du tutoriel if(intent.getAction().equals(ACTION_OPEN_TUTO)) { Intent link = new Intent(Intent.ACTION_VIEW); link.setData(intent.getData()); link.addCategory(Intent.CATEGORY_DEFAULT); // Comme on ne se trouve pas dans une activité, on demande à créer une nouvelle tâche link.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(link); } else { // Sinon, s'il s'agit d'une demande de mise à jour // On récupère l'indice passé en extra, ou -1 s'il n'y a pas d'indice int tmp = intent.getIntExtra(EXTRA_INDICE, -1); // S'il y avait bien un indice passé if(tmp != -1) { // On récupère la direction String extra = intent.getStringExtra(EXTRA_DIRECTION); // Et on calcule l'indice voulu par l'utilisateur if (extra.equals(EXTRA_PREVIOUS)) { indice = (tmp - 1) % TUTO_ARRAY.length; if(indice < 0) indice += TUTO_ARRAY.length; } else if(extra.equals(EXTRA_NEXT)) indice = (tmp + 1) % TUTO_ARRAY.length; } } // On revient au traitement naturel du Receiver, qui va lancer onUpdate s'il y a demande de mise à jour super.onReceive(context, intent); } } |
Enfin, on déclare le tout dans le Manifest :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sdz.chapitrequatre.tutowidget" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <receiver android:name=".TutoWidget" android:icon="@drawable/ic_launcher" android:label="@string/app_name"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="sdz.chapitreQuatre.tutowidget.action.OPEN_TUTO" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider_info" /> </receiver> </application> </manifest> |
- Un AppWidget est une extension de votre application. Afin que l'utilisateur ne soit pas désorienté, adoptez la même charte graphique que votre application.
- Les seules vues utilisables pour un widget sont les vues
RemoteViews
. - La déclaration d'un AppWidget se fait dans un élément
appwidget-provider
à partir d'un fichier XMLAppWidgetProviderInfo
. - La super classe de notre AppWidget sera un
AppWidgetProvider
. Il s'occupera de gérer tous les évènements sur le cycle de vie de notre AppWidget. Cette classe dérive deBroadcastReceiver
, elle va donc recevoir les divers broadcast intents qui sont émis et qui sont destinés à l'AppWidget. - Pour déclarer notre AppWidget dans le manifest, nous allons créer un élément
receiver
auquel nous ajoutons un élémentintent-filter
pour lancer notre AppWidget et un élémentmeta-data
pour définir l'AppWidgetProviderInfo
.