I need to display on the phone screen technical information about his condition, more precisely, about his condition in the test pool. I always want to see this information, that is, on the Home screen, and without additional body movements.
There are only two ways that will not affect the execution of other applications: Widget or Live wallpaper. I chose Live wallpaper, they are also “live wallpapers” because they automatically get to all pages of the Home screen, and even to the Lock screen. This article provides practical guidance on how to create live wallpapers.
Documentation about the "live wallpaper" cat cried. Since the first (and only) announcement on the blog that happened more than 9 years ago, Google has not made a single intelligible example or codelab on this topic. I had to understand.
First basics. The internal mechanics of Android is such that we can only install the application on the device, and the device of all applications is the same. Since "live wallpaper" is also an application, the choice of a control component is not great, and we should expect that it will be Service. Finding it is easy: it's WallpaperService .
There may be several instances of “live wallpaper”, and their life cycle will be different from that of Activity or View. Accordingly, there should be one more base class. This is WallpaperService.Engine (and it is necessarily inner for WallpaperService !). If you look closely, it will turn out to be the same provider of life cycle events as Activity, Service and others like them.
The life cycle of "live wallpaper" looks like this:
onCreate (SurfaceHolder surfaceHolder)
onSurfaceCreated (SurfaceHolder holder)
onSurfaceChanged (SurfaceHolder holder, int format, int width, int height)
onVisibilityChanged (boolean visible)
onSurfaceRedrawNeeded (SurfaceHolder holder)
onSurfaceDestroyed (SurfaceHolder holder)
onDestroy ()
From this list it becomes clear when you can / need to redraw the picture (or start redrawing if you have animation), and when it's time to stop all activity and not waste battery.
The onSurfaceRedrawNeeded () method stands out from the rest, read below. Also, there is an isVisible () method to help (which in Kotlin turns into an isVisible
property).
Now you can build this constructor. I'll start from the end.
We will have to draw ourselves on Canvas , we will not have any layout and inflater. How to get Canvas from SurfaceHolder and how to draw on it is beyond the scope of this article, there is a simple example below.
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) } } }
All lifecycle methods except onSurfaceRedrawNeeded
do not require immediate redrawing. Therefore, a good tone would be to redraw the queue.
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 }
Pay attention to onSurfaceRedrawNeeded , which gives us a call to the SurfaceHolder callback of the same name , which occurs when resizing and similar events. This callback allows you to redraw immediately, without allowing the user to display the old (and already incorrect) picture. The system ensures that until a return from this method has occurred, the output to the screen will be paused.
I like to redefine Handler, and not to run Runnable in it. In my opinion, so elegant.
In case you have animation or regular updates, you will need to make a regular queuing of the message ( postAtTime () and postDelayed () to help you). If the data is updated occasionally, just call planRedraw()
to update it.
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) } } }
This mareshka from Service and Engine is assembled like this:
class FooBarWallpaperService : WallpaperService() { override fun onCreateEngine() = FooBarEngine() inner class FooBarEngine : Engine() { .... } }
I call spells in software development that it is impossible to understand, but it is necessary to repeat exactly.
In .../app/src/main/res/xml
must be an XML file with a description of "live wallpaper". The name of this file must be specified in AndroidManifest (look for the word foobarwallpaper
in the example below)
<?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" />
Do not lose permission
, meta-data
and intent-filter
in the description of the 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>
"Live wallpapers" are hiding, therefore a hint. I describe how it looks on my Samsung.
To start, long press somewhere on the Home screen, the phone will go into the desktop settings mode, the Wallpapers icon will appear.
We click on the Wallpapers icon, several sections, we need My wallpapers , click on the inscription View all in the upper right corner of the section, the list opens in full screen.
We press the "three points" of the menu call, in it the LIve wallpapers item (I have one), a list of available "live wallpapers" appears.
Select our wallpaper, and select "Home and lock screen".
A “preview” will appear, which is already being drawn by our application (to recognize this moment, there is the isPreview () method), click Set as wallpaper ... And we don’t see anything, because we return to the list of available wallpapers.
Click "Home" and enjoy.
An interesting observation along the way is that Faces in Android Watch are made in exactly the same way (with the accuracy that they have their own base classes with their own implementation): the same Service + Engine , almost the same metadata and intent filter for Service in the manifest (in which the word wallpaper occurs four times :), you also need to write your own sheduler based on Handler .
In the base classes of Watch Faces there is a ready-made onDraw()
, where Canvas is passed to, and there is invalidate()
for calling it. But this is not a fundamental difference, but the implemented part of the boilerplate.
Unlike Live Wallpaper, there are examples for Watch Faces, you can dig into them (links here , at the very beginning).
Screenshots for an application that paints a green screen make little sense. But a couple of pictures that based on this turned out to be done for the combat stand, under the spoiler.
Stickers are the remaining problem detection system of the previous generation.
If not for these two articles, I would have wandered in the darkness much longer. I can’t imagine how they could have been written already in 2010 with such quality documentation ?!
Kirill Grouchnikov, Live wallpapers
Vogella, Android Live Wallpaper - Tutorial
→ GitHub
Source: https://habr.com/ru/post/460951/
All Articles