Live Wallpaper
Autor: Gustavo Carvalho | Publicado: 2012-06-05T09:41:00-03:00




Live Wallpaper, ou Papel de Parede Animado conforme a tradução, foram introduzidos na versão 2.1 (API 7) do Android. Como o nome sugere, ele nada mais é do que um fundo que se mexe. Mas ao contrário do que se imagina ele pode ser muito mais poderoso do que se imagina. É possível fazer praticamente qualquer coisa que um aplicativo comum faz, inclusive interagir com toques do usuário.



Mas, como dizia Tio Ben, com grandes poderes vêm grandes responsabilidades. Lembre-se que o Live Wallpaper irá rodar boa parte do tempo, então este não deve fazer muitas operações pesadas para não consumir muitos dados ou energia do aparelho.




O exemplo mostrado aqui pode ser baixado clicando aqui.




O Live Wallpaper não passa de um Service que rodará infinitamente enquanto você manter o Live Wallpaper. Então vamos começar definindo isso no AndroidManifest:


&lt;uses-feature android:name="android.software.live_wallpaper" /&gt;

    &lt;application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" &gt;
        
        &lt;service
            android:label="@string/app_name"
            android:name=".ExempleWallpaper"
            android:permission="android.permission.BIND_WALLPAPER"&gt;
            &lt;intent-filter&gt;
                &lt;action android:name="android.service.wallpaper.WallpaperService" /&gt;
            &lt;/intent-filter&gt;
            &lt;meta-data android:name="android.service.wallpaper" android:resource="@xml/wall" /&gt;
        &lt;/service&gt;
        
    &lt;/application&gt;



Note o uses-feature&nbsp;que é necessário e a definição do Service. Assim como na definição de um app widget aqui também definimos um meta-data&nbsp;que está em xml/wall. Vamos cria-lo:


&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;wallpaper xmlns:android="http://schemas.android.com/apk/res/android"/&gt;




Só isso. Dentro dessa tag&nbsp;poderíamos&nbsp;definir&nbsp;android:settingsActivity que seria a Activity de configuração, semelhante a Activity de configuração do app widget, mas por enquanto não iremos fazer isso. A seguir vamos criar nosso service ExempleWallpaper&nbsp;propriamente dito, como definido no Manifest:




package com.gdacarv.tutoriandroid.livewallpaper;


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class ExempleWallpaper extends WallpaperService {

    // Handler utilizado para redesenhar a tela
    private final Handler mHandler = new Handler();

    @Override
    public Engine onCreateEngine() {
        return new ExempleEngine();
    }

    class ExempleEngine extends Engine {

        // Frames Por Segundo
        private static final int FPS = 30;

        // Velocidade da animação
        private static final float SPEED = 3;
        
        // Utilizado quando a tela é movida
        private float mOffset;
        
        // Registra toques na tela
        private float mTouchX = -1;
        private float mTouchY = -1;
        
        
        private int height, width;
        
        private Bitmap mBitmap;
        private float x,y;
        private int dirHorizontal = 1, dirVertical = 1;

        private final Runnable mDrawCube = new Runnable() {
            public void run() {
                drawFrame();
            }
        };
        
        private boolean mVisible;

        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);

            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
            //setTouchEventsEnabled(true); // Ativa eventos de toque (Atualmente desativados por default)
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacks(mDrawCube);
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            mVisible = visible;
            if (visible) {
                drawFrame();
            } else {
                mHandler.removeCallbacks(mDrawCube);
            }
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            super.onSurfaceChanged(holder, format, width, height);
            // Salva largura e altura da tela
            this.width = width;
            this.height = height;
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder) {
            super.onSurfaceDestroyed(holder);
            mVisible = false;
            mHandler.removeCallbacks(mDrawCube);
        }

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xStep, float yStep, int xPixels, int yPixels) {
            mOffset = xOffset;
        }

       
        @Override
        public void onTouchEvent(MotionEvent event) {
            // Guarda toques na tela. Só é executado se comando setTouchEventsEnabled(true) for executado previamente.
            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                mTouchX = event.getX();
                mTouchY = event.getY();
            } else {
                mTouchX = -1;
                mTouchY = -1;
            }
            super.onTouchEvent(event);
        }

        void drawFrame() {
            final SurfaceHolder holder = getSurfaceHolder();

            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    mover();
                    onDraw(c);
                }
            } finally {
                if (c != null) holder.unlockCanvasAndPost(c);
            }

            // Agenda proximo re-desenho
            mHandler.removeCallbacks(mDrawCube);
            if (mVisible) {
                mHandler.postDelayed(mDrawCube, 1000 / FPS);
            }
        }

        private void mover() {
            // Move a imagem
            x += SPEED*dirHorizontal;
            y += SPEED*dirVertical;
            if(y &lt; 0 || y &gt; height-mBitmap.getHeight())
                dirVertical *= -1;
            if(x &lt; 0 || x &gt; width-mBitmap.getWidth())
                dirHorizontal *= -1;
        }

        private void onDraw(Canvas c) {
            // Limpa tela e desenha a imagem
            c.drawARGB(255, 0, 0, 0);
            c.drawBitmap(mBitmap, x, y, null);
        }

    }
}





O que importa nesse Service é a Engine. Estendemos a classe Engine para que possamos definir nossos próprios&nbsp;métodos. Note que Engine fornece métodos de criação, de alteração da Surface, de mudança (deslizamento) de tela, de toque na tela, entre outros. Você deve sempre fazer algo no&nbsp;onVisibilityChanged para que quando o wallpaper não estiver visível ele pause o seu funcionamento, assim economizando recursos.



Acesse a documentação oficial e esse exemplo para mais informações.



Qualquer dúvida é só perguntar.