Erstellen Sie Android Live Wallpapers

Ich muss auf dem Telefonbildschirm technische Informationen über seinen Zustand anzeigen, genauer gesagt über seinen Zustand im Testpool. Ich möchte diese Informationen immer auf dem Startbildschirm und ohne zusätzliche Körperbewegungen sehen.


Es gibt nur zwei Möglichkeiten, die sich nicht auf die Ausführung anderer Anwendungen auswirken: Widget oder Live Wallpaper. Ich habe Live Wallpaper gewählt, sie sind auch "Live Wallpaper", da sie automatisch zu allen Seiten des Startbildschirms und sogar zum Sperrbildschirm gelangen. Dieser Artikel enthält praktische Tipps zum Erstellen von Live-Hintergründen.


Auf der Suche nach der Wahrheit


Dokumentation über die "Live Wallpaper" Katze weinte. Seit der ersten (und einzigen) Ankündigung im Blog vor mehr als 9 Jahren hat Google kein einziges verständliches Beispiel oder Codelab zu diesem Thema erstellt. Ich musste verstehen


Erste Grundlagen. Die interne Mechanik von Android ist so, dass wir die Anwendung nur auf dem Gerät installieren können und das Gerät aller Anwendungen das gleiche ist. Da "Live Wallpaper" auch eine Anwendung ist, ist die Auswahl einer Steuerungskomponente nicht großartig, und wir sollten erwarten, dass es sich um Service handelt. Es ist einfach zu finden: Es ist WallpaperService .


Es kann mehrere Fälle von "Live Wallpaper" geben, und ihr Lebenszyklus unterscheidet sich von dem von Aktivität oder Ansicht. Dementsprechend sollte es eine weitere Basisklasse geben. Dies ist WallpaperService.Engine (und es ist notwendigerweise inner für WallpaperService !). Wenn Sie genau hinschauen, wird sich herausstellen, dass es sich um denselben Anbieter von Lebenszyklusereignissen handelt wie Activity, Service und andere ähnliche.


Der Lebenszyklus von "Live Wallpaper" sieht folgendermaßen aus:


onCreate (SurfaceHolder surfaceHolder)
onSurfaceCreated (SurfaceHolder-Halter)
onSurfaceChanged (SurfaceHolder-Halter, int-Format, int-Breite, int-Höhe)
onVisibilityChanged (Boolescher Wert sichtbar)
onSurfaceRedrawNeeded (SurfaceHolder-Halter)
onSurfaceDestroyed (SurfaceHolder-Halter)
onDestroy ()


Aus dieser Liste wird deutlich, wann Sie das Bild neu zeichnen können / müssen (oder mit dem Neuzeichnen beginnen müssen, wenn Sie eine Animation haben) und wann es Zeit ist, alle Aktivitäten zu stoppen und keine Batterie zu verschwenden.


Die onSurfaceRedrawNeeded () -Methode hebt sich von den anderen ab (siehe unten). Es gibt auch eine isVisible () -Methode (die in Kotlin zur isVisible Eigenschaft wird).


Jetzt können Sie diesen Konstruktor erstellen. Ich werde am Ende beginnen.


Zeichnen


Wir müssen uns auf Leinwand zeichnen, wir werden kein Layout und keinen Inflater haben. Wie Sie Canvas von SurfaceHolder erhalten und wie Sie darauf zeichnen, geht über den Rahmen dieses Artikels hinaus. Im Folgenden finden Sie ein einfaches Beispiel.


 fun dummyDraw(c: Canvas) { c.save() c.drawColor(Color.CYAN) c.restore() } // surfaceHolder property is actually a call to the getSurfaceHolder() method fun drawSynchronously() = drawSynchronously(surfaceHolder) fun drawSynchronously(holder: SurfaceHolder) { if (!isVisible) return var c: Canvas? = null try { c = holder.lockCanvas() c?.let { dummyDraw(it) } } finally { c?.let { holder.unlockCanvasAndPost(it) } } } 

Methoden des Motorlebenszyklus


Alle Lebenszyklusmethoden außer onSurfaceRedrawNeeded erfordern kein sofortiges Neuzeichnen. Ein guter Ton wäre daher, die Warteschlange neu zu zeichnen.


 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) // do it immediately, don't plan } 

Achten Sie auf onSurfaceRedrawNeeded , mit dem wir den gleichnamigen SurfaceHolder-Rückruf aufrufen, der beim Ändern der Größe und ähnlichen Ereignissen auftritt. Mit diesem Rückruf können Sie sofort neu zeichnen, ohne dass der Benutzer das alte (und bereits falsche) Bild anzeigen kann. Das System garantiert, dass die Ausgabe auf dem Bildschirm angehalten wird, bis eine Rückkehr von dieser Methode erfolgt ist.


Scheduler


Ich mag es, Handler neu zu definieren und Runnable nicht darin auszuführen. Meiner Meinung nach so elegant.


Wenn Sie Animationen oder regelmäßige Updates haben, müssen Sie die Nachricht regelmäßig in die Warteschlange stellen ( postAtTime () und postDelayed () , um Ihnen zu helfen). Wenn die Daten sporadisch aktualisiert werden, rufen planRedraw() einfach planRedraw() auf, um sie zu aktualisieren.


 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 & Motor


Diese Mareshka von Service and Engine ist wie folgt zusammengebaut:


 class FooBarWallpaperService : WallpaperService() { override fun onCreateEngine() = FooBarEngine() inner class FooBarEngine : Engine() { .... } } 

AndroidManifest und andere Zauber


Ich nenne Zaubersprüche in der Softwareentwicklung, die nicht zu verstehen sind, aber genau wiederholt werden müssen.


In .../app/src/main/res/xml muss eine XML-Datei mit der Beschreibung "Live Wallpaper" vorhanden sein. Der Name dieser Datei muss in AndroidManifest angegeben werden (suchen foobarwallpaper im folgenden Beispiel nach dem Wort foobarwallpaper ).


 <?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" /> 

Verlieren Sie nicht die permission , meta-data und intent-filter in der Beschreibung des Dienstes:


 <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> 

Wie man hinzufügt


"Live Wallpaper" verstecken sich, daher ein Hinweis. Ich beschreibe, wie es auf meinem Samsung aussieht.


Wenn Sie zu Beginn lange auf den Startbildschirm drücken, wechselt das Telefon in den Desktop-Einstellungsmodus. Das Hintergrundbild- Symbol wird angezeigt.


Wir klicken auf das Hintergrundbild- Symbol, mehrere Abschnitte, wir benötigen Meine Hintergrundbilder , klicken auf die Inschrift Alle in der oberen rechten Ecke des Abschnitts anzeigen, die Liste wird im Vollbildmodus geöffnet.


Wir drücken die "drei Punkte" des Menüaufrufs , darin wird der Eintrag "LIve Wallpaper" (ich habe einen) angezeigt , eine Liste der verfügbaren "Live Wallpaper" wird angezeigt.


Wählen Sie unser Hintergrundbild und dann "Startbildschirm und Sperrbildschirm".


Es wird eine "Vorschau" angezeigt , die bereits von unserer Anwendung gezeichnet wird (um diesen Punkt zu erkennen, gibt es die isPreview () -Methode). Klicken Sie auf Als Hintergrundbild festlegen ... Und wir sehen nichts, da wir zur Liste der verfügbaren Hintergrundbilder zurückkehren.


Klicken Sie auf "Home" und genießen Sie.


Und dann Android Watch ?!


Eine interessante Beobachtung auf dem Weg ist, dass Gesichter in Android Watch genauso erstellt werden (mit der Genauigkeit, dass sie ihre eigenen Basisklassen mit ihrer eigenen Implementierung haben): dieselbe Service + Engine , fast dieselben Metadaten und Absichtsfilter für Service im Manifest (in Was das Wort Wallpaper viermal vorkommt :), müssen Sie auch Ihren eigenen Sheduler basierend auf Handler schreiben .


In den Basisklassen von Watch Faces gibt es ein fertiges onDraw() , an das Canvas übergeben wird, und es gibt invalidate() zum Aufrufen. Dies ist jedoch kein grundlegender Unterschied, sondern der implementierte Teil der Kesselplatte.


Im Gegensatz zu Live Wallpaper gibt es Beispiele für Watch Faces, in die Sie sich vertiefen können (Links hier ganz am Anfang).


Was ist passiert?


Screenshots für eine Anwendung, die einen grünen Bildschirm malt, sind wenig sinnvoll. Aber ein paar Bilder, die darauf basierten, wurden für den Kampfstand unter dem Spoiler gemacht.


Ein paar Bilder



Aufkleber sind das verbleibende Problemerkennungssystem der vorherigen Generation.


Danksagung


Ohne diese beiden Artikel wäre ich viel länger in der Dunkelheit gewandert. Ich kann mir nicht vorstellen, wie sie bereits 2010 mit einer solchen Qualitätsdokumentation hätten geschrieben werden können ?!


Kirill Grouchnikov, Live Wallpaper
Vogella, Android Live Wallpaper - Tutorial


Quellcode


GitHub

Source: https://habr.com/ru/post/de460951/


All Articles