Criar papéis de parede animados para Android

Preciso exibir na tela do telefone informações técnicas sobre sua condição, mais precisamente, sobre sua condição no pool de testes. Eu sempre quero ver essas informações, ou seja, na tela inicial e sem movimentos adicionais do corpo.


Existem apenas duas maneiras que não afetarão a execução de outros aplicativos: Widget ou Live wallpaper. Eu escolhi o papel de parede ao vivo, eles também são "papéis de parede ao vivo", porque acessam automaticamente todas as páginas da tela inicial e até a tela de bloqueio. Este artigo fornece dicas práticas sobre como criar papéis de parede ao vivo.


Em busca da verdade


Documentação sobre o gato "live wallpaper" chorou. Desde o primeiro (e único) anúncio no blog que aconteceu há mais de 9 anos, o Google não fez um único exemplo inteligível ou codelab sobre esse tópico. Eu tive que entender.


Primeiros princípios. A mecânica interna do Android é tal que só podemos instalar o aplicativo no dispositivo, e o dispositivo de todos os aplicativos é o mesmo. Como o "papel de parede ao vivo" também é um aplicativo, a escolha de um componente de controle não é ótima, e devemos esperar que seja Serviço. Achar fácil: é o WallpaperService .


Pode haver várias instâncias de "papel de parede ao vivo" e seu ciclo de vida será diferente do de Atividade ou Exibição. Portanto, deve haver mais uma classe base. Este é WallpaperService.Engine (e é necessariamente interno para WallpaperService !). Se você observar de perto, será o mesmo provedor de eventos do ciclo de vida que Atividade, Serviço e outros como eles.


O ciclo de vida do "papel de parede ao vivo" é assim:


onCreate (SurfaceHolder surfaceHolder)
onSurfaceCreated (suporte do SurfaceHolder)
onSurfaceChanged (suporte SurfaceHolder, formato int, largura int, largura int)
onVisibilityChanged (booleano visível)
onSurfaceRedrawNeeded (suporte do SurfaceHolder)
onSurfaceDestroyed (suporte do SurfaceHolder)
onDestroy ()


A partir desta lista, fica claro quando você pode / precisa redesenhar a imagem (ou começar a redesenhar se tiver animação) e quando é hora de interromper todas as atividades e não desperdiçar bateria.


O método onSurfaceRedrawNeeded () se destaca dos demais, leia abaixo. Há também um método isVisible () para ajudar (que no Kotlin se transforma na propriedade isVisible ).


Agora você pode criar esse construtor. Eu vou começar do final.


Draw


Teremos que nos desenhar no Canvas , não teremos nenhum layout e inflador. Como obter o Canvas do SurfaceHolder e como utilizá -lo está além do escopo deste artigo, há um exemplo simples abaixo.


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

Métodos de ciclo de vida do motor


Todos os métodos de ciclo de vida, exceto onSurfaceRedrawNeeded , não requerem redesenho imediato. Portanto, um bom tom seria redesenhar a fila.


 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 } 

Preste atenção ao onSurfaceRedrawNeeded , que nos fornece uma chamada para o retorno de chamada SurfaceHolder com o mesmo nome , que ocorre ao redimensionar e eventos semelhantes. Esse retorno de chamada permite redesenhar imediatamente, sem permitir que o usuário exiba a imagem antiga (e já incorreta). O sistema garante que, até que ocorra um retorno desse método, a saída da tela seja pausada.


Programador


Eu gosto de redefinir Handler, e não executar Runnable nele. Na minha opinião, tão elegante.


Caso você tenha animação ou atualizações regulares, será necessário fazer um enfileiramento regular da mensagem ( postAtTime () e postDelayed () para ajudá-lo). Se os dados forem atualizados esporadicamente, basta chamar planRedraw() para atualizá-los.


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

Serviço e mecanismo


Esse mareshka do Service and Engine é montado assim:


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

AndroidManifest e outros feitiços


Eu chamo feitiços no desenvolvimento de software que é impossível entender, mas é necessário repetir exatamente.


Em .../app/src/main/res/xml deve haver um arquivo XML com uma descrição de "papel de parede ao vivo". O nome desse arquivo deve ser especificado no AndroidManifest (procure a palavra foobarwallpaper no exemplo abaixo)


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

Não perca permission , meta-data e intent-filter na descrição do Serviço:


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

Como adicionar


"Papéis de parede ao vivo" estão escondidos, portanto, uma dica. Descrevo como fica no meu Samsung.


Para começar, pressione e segure em algum lugar na tela inicial, o telefone entrará no modo de configurações da área de trabalho e o ícone Wallpapers aparecerá.


Clicamos no ícone Papéis de parede , em várias seções, precisamos de Meus papéis de parede , clique na inscrição Ver tudo no canto superior direito da seção, a lista é aberta em tela cheia.


Pressionamos os "três pontos" da chamada de menu, nele o item Papéis de parede (eu tenho um), uma lista de "papéis de parede ao vivo" disponíveis aparece.


Selecione nosso papel de parede e selecione "Tela inicial e de bloqueio".


Aparecerá uma "visualização", que já está sendo desenhada pelo nosso aplicativo (para reconhecer esse momento, existe o método isPreview () ), clique em Definir como papel de parede ... E não vemos nada, pois retornamos à lista de papéis de parede disponíveis.


Clique em "Home" e aproveite.


E então Android Watch ?!


Uma observação interessante ao longo do caminho é que as Faces no Android Watch são feitas exatamente da mesma maneira (com a precisão de que eles têm suas próprias classes base com sua própria implementação): o mesmo Service + Engine , quase os mesmos metadados e filtro de intenção do Service no manifesto (em qual a palavra papel de parede ocorre quatro vezes :), você também precisa escrever seu próprio sheduler com base no manipulador .


Nas classes base do Watch Faces, existe um onDraw() pronto para o qual o Canvas é passado e há invalidate() para chamá-lo. Mas essa não é uma diferença fundamental, mas a parte implementada do clichê.


Ao contrário do Live Wallpaper, existem exemplos para Watch Faces, você pode cavar neles (links aqui , no início).


O que aconteceu


As capturas de tela de um aplicativo que pinta uma tela verde fazem pouco sentido. Mas algumas fotos com base nisso foram feitas para o posto de combate, sob o spoiler.


Algumas fotos



Os adesivos são o restante sistema de detecção de problemas da geração anterior.


Agradecimentos


Se não fosse por esses dois artigos, eu teria vagado na escuridão por muito mais tempo. Não consigo imaginar como eles poderiam ter sido escritos já em 2010 com essa documentação de qualidade ?!


Kirill Grouchnikov, Papel de parede animado
Vogella, Android Papel de parede animado - Tutorial


Código fonte


GitHub

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


All Articles