Je dois afficher sur l'écran du téléphone des informations techniques sur son état, plus précisément sur son état dans le pool de test. Je veux toujours voir ces informations, c'est-à-dire sur l'écran d'accueil, et sans mouvements corporels supplémentaires.
Il n'y a que deux façons qui n'affecteront pas l'exécution d'autres applications: Widget ou Live wallpaper. J'ai choisi Live wallpaper, ce sont aussi des "live wallpapers", car ils accèdent automatiquement à toutes les pages de l'écran d'accueil, et même à l'écran de verrouillage. Cet article fournit des conseils pratiques sur la création de fonds d'écran animés.
À la recherche de la vérité
La documentation sur le chat "live wallpaper" a pleuré. Depuis la première (et la seule) annonce sur le blog qui a eu lieu il y a plus de 9 ans, Google n'a pas fait un seul exemple intelligible ou codelab sur ce sujet. Je devais comprendre.
Premières bases. La mécanique interne d'Android est telle que nous ne pouvons installer l'application que sur l'appareil, et l'appareil de toutes les applications est le même. Étant donné que le «fond d'écran en direct» est également une application, le choix d'un composant de contrôle n'est pas grand, et nous devrions nous attendre à ce que ce soit le service. Le trouver est facile: c'est WallpaperService .
Il peut y avoir plusieurs instances de "fond d'écran en direct", et leur cycle de vie sera différent de celui de l'activité ou de la vue. Par conséquent, il devrait y avoir une autre classe de base. Il s'agit de WallpaperService.Engine (et il est nécessairement interne à WallpaperService !). Si vous regardez attentivement, il se révélera être le même fournisseur d'événements de cycle de vie que l'activité, le service et d'autres comme eux.
Le cycle de vie du "fond d'écran en direct" ressemble à ceci:
onCreate (SurfaceHolder surfaceHolder)
onSurfaceCreated (support SurfaceHolder)
onSurfaceChanged (support SurfaceHolder, format int, largeur int, hauteur int)
onVisibilityChanged (booléen visible)
onSurfaceRedrawNeeded (support SurfaceHolder)
onSurfaceDestroyed (support SurfaceHolder)
onDestroy ()
À partir de cette liste, il devient clair quand vous pouvez / devez redessiner l'image (ou commencer à redessiner si vous avez une animation), et quand il est temps d'arrêter toute activité et de ne pas gaspiller la batterie.
La méthode onSurfaceRedrawNeeded () se démarque des autres, lire ci-dessous. Il existe également une méthode isVisible () pour aider (qui, dans Kotlin, se transforme en propriété isVisible
).
Vous pouvez maintenant construire ce constructeur. Je vais commencer par la fin.
Dessiner
Nous devrons nous dessiner sur Canvas , nous n'aurons aucune mise en page et gonfleur. Comment obtenir Canvas de SurfaceHolder et comment dessiner dessus dépasse le cadre de cet article, voici un exemple simple ci-dessous.
fun dummyDraw(c: Canvas) { c.save() c.drawColor(Color.CYAN) c.restore() }
Méthodes de cycle de vie du moteur
Toutes les méthodes de cycle de vie sauf onSurfaceRedrawNeeded
ne nécessitent pas de redessin immédiat. Par conséquent, un bon ton serait de redessiner la file d'attente.
override fun onSurfaceCreated(holder: SurfaceHolder?) { super.onSurfaceCreated(holder) redrawHandler.planRedraw() } override fun onSurfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { super.onSurfaceChanged(holder, format, width, height) redrawHandler.planRedraw() } override fun onVisibilityChanged(visible: Boolean) { super.onVisibilityChanged(visible) redrawHandler.planRedraw() } override fun onSurfaceDestroyed(holder: SurfaceHolder?) { super.onSurfaceDestroyed(holder) redrawHandler.omitRedraw() } override fun onDestroy() { super.onDestroy() redrawHandler.omitRedraw() } override fun onSurfaceRedrawNeeded(holder: SurfaceHolder) { super.onSurfaceRedrawNeeded(holder) redrawHandler.omitRedraw() drawSynchronously(holder)
Faites attention à onSurfaceRedrawNeeded , qui nous donne un appel au rappel SurfaceHolder du même nom , qui se produit lors du redimensionnement et d'événements similaires. Ce rappel vous permet de redessiner immédiatement, sans autoriser l'utilisateur à afficher l'image ancienne (et déjà incorrecte). Le système garantit que jusqu'à ce qu'un retour de cette méthode se produise, la sortie à l'écran sera suspendue.
Planificateur
J'aime redéfinir Handler et ne pas y exécuter Runnable. À mon avis, si élégant.
Si vous avez une animation ou des mises à jour régulières, vous devrez faire une mise en file d'attente régulière du message ( postAtTime () et postDelayed () pour vous aider). Si les données sont mises à jour sporadiquement, appelez simplement planRedraw()
pour les mettre à jour.
val redrawHandler = RedrawHandler() inner class RedrawHandler : Handler(Looper.getMainLooper()) { private val redraw = 1 fun omitRedraw() { removeMessages(redraw) } fun planRedraw() { omitRedraw() sendEmptyMessage(redraw) } override fun handleMessage(msg: Message) { when (msg.what) { redraw -> drawSynchronously() else -> super.handleMessage(msg) } } }
Service & moteur
Cette mareshka de Service and Engine se présente comme suit:
class FooBarWallpaperService : WallpaperService() { override fun onCreateEngine() = FooBarEngine() inner class FooBarEngine : Engine() { .... } }
AndroidManifest et autres sorts
J'appelle des sorts dans le développement logiciel qu'il est impossible de comprendre, mais il faut répéter exactement.
Dans .../app/src/main/res/xml
doit .../app/src/main/res/xml
avoir un fichier XML avec une description de "live wallpaper". Le nom de ce fichier doit être spécifié dans AndroidManifest (recherchez le mot foobarwallpaper
dans l'exemple ci-dessous)
<?xml version="1.0" encoding="UTF-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:thumbnail="@drawable/some_drawable_preview" android:description="@string/wallpaper_description" />
Ne perdez pas l' permission
, les meta-data
et le intent-filter
dans la description du service:
<service android:name=".FooBarWallpaperService" android:enabled="true" android:label="Wallpaper Example" android:permission="android.permission.BIND_WALLPAPER"> <meta-data android:name="android.service.wallpaper" android:resource="@xml/foobarwallpaper" > </meta-data> <intent-filter> <action android:name="android.service.wallpaper.WallpaperService"> </action> </intent-filter> </service>
Comment ajouter
Les "fonds d'écran animés" se cachent, donc un indice. Je décris à quoi cela ressemble sur mon Samsung.
Pour commencer, appuyez longuement quelque part sur l'écran d'accueil, le téléphone passera en mode paramètres de bureau, l'icône Fonds d'écran apparaîtra.
Nous cliquons sur l'icône Fonds d'écran , plusieurs sections, nous avons besoin de Mes fonds d'écran , cliquez sur l'inscription Voir tout dans le coin supérieur droit de la section, la liste s'ouvre en plein écran.
Nous appuyons sur les "trois points" de l'appel de menu, dans celui-ci l'élément LIve wallpapers (j'en ai un), une liste de "live wallpapers" disponibles apparaît.
Sélectionnez notre fond d'écran, puis sélectionnez "Accueil et écran de verrouillage".
Un "aperçu" apparaîtra, qui est déjà dessiné par notre application (pour reconnaître ce moment, il y a la méthode isPreview () ), cliquez sur Définir comme fond d'écran ... Et nous ne voyons rien, car nous revenons à la liste des fonds d'écran disponibles.
Cliquez sur "Accueil" et profitez-en.
Et puis Android Watch?!
Une observation intéressante en cours de route est que les visages dans Android Watch sont créés exactement de la même manière (avec la précision qu'ils ont leurs propres classes de base avec leur propre implémentation): le même Service + Engine , presque les mêmes métadonnées et filtre d'intention pour Service dans le manifeste (dans où le mot fond d'écran apparaît quatre fois :), vous devez également écrire votre propre sheduler basé sur Handler .
Dans les classes de base de Watch Faces, il y a un onDraw()
prêt à l' onDraw()
, où Canvas est passé à, et il y a invalidate()
pour l'appeler. Mais ce n'est pas une différence fondamentale, mais la partie implémentée du passe-partout.
Contrairement au Live Wallpaper, il existe des exemples pour Watch Faces, vous pouvez les creuser (liens ici , au tout début).
Qu'est-il arrivé?
Les captures d'écran d'une application qui peint un écran vert n'ont pas de sens. Mais quelques photos qui, sur la base de cela, se sont avérées être faites pour le stand de combat, sous le spoiler.
Quelques photos

Les autocollants sont le système de détection de problème restant de la génération précédente.
Remerciements
Sans ces deux articles, j'aurais erré dans l'obscurité beaucoup plus longtemps. Je ne peux pas imaginer comment ils auraient pu être écrits déjà en 2010 avec une telle documentation de qualité?!
Kirill Grouchnikov, Fonds d'écran animés
Vogella, Android Live Wallpaper - Tutoriel
Code source
→ GitHub