I'm new to Android platform. I wish to develop a live wallpaper application. When i was searched regarding this in search Engine's, many of them created a live wallpaper as their code (using SurfaceView
and Canvas
), I'm not much aware in this. Here my doubt is, any possible to set a .gif images as a live wallpaper.

- 12,086
- 10
- 56
- 90
-
Refer [to this answer](http://stackoverflow.com/a/7772170/967142) – Jens Feb 06 '12 at 10:05
-
@Jens: thanks for your replay, if you have any sample code how to do this, – Aerrow Feb 06 '12 at 10:40
-
Well, this shows you [how](http://droid-blog.net/2011/10/14/tutorial-how-to-use-animated-gifs-in-android-part-1/) – Jens Feb 06 '12 at 11:03
-
i used like this, but i set the .gif image as my wall paper it looks like normal jpeg image. its not animated – Aerrow Feb 06 '12 at 11:33
2 Answers
This is the basic wallpaper service (as supplied in the Live Wallpaper Tutorial) hacked to display an animated gif.
First - create a project & set up your manifest as a Live wallpaper.
Then - download a gif, like this one
Save that gif in res/raw/nyan.gif
in your project.
Create a live wallpaper service, like shown in this example.
public class NyanNyanService extends WallpaperService {
static final String TAG = "NYAN";
static final Handler mNyanHandler = new Handler();
/**
* @see android.service.wallpaper.WallpaperService#onCreate()
*/
@Override
public void onCreate() {
super.onCreate();
}
/**
* @see android.service.wallpaper.WallpaperService#onCreateEngine()
*/
@Override
public Engine onCreateEngine() {
try {
return new NyanEngine();
} catch (IOException e) {
Log.w(TAG, "Error creating NyanEngine", e);
stopSelf();
return null;
}
}
class NyanEngine extends Engine {
private final Movie mNyan;
private final int mNyanDuration;
private final Runnable mNyanNyan;
float mScaleX;
float mScaleY;
int mWhen;
long mStart;
NyanEngine() throws IOException {
InputStream is = getResources().openRawResource(R.raw.nyan);
if (is != null) {
try {
mNyan = Movie.decodeStream(is);
mNyanDuration = mNyan.duration();
} finally {
is.close();
}
} else {
throw new IOException("Unable to open R.raw.nyan");
}
mWhen = -1;
mNyanNyan = new Runnable() {
public void run() {
nyan();
}
};
}
@Override
public void onDestroy() {
super.onDestroy();
mNyanHandler.removeCallbacks(mNyanNyan);
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
nyan();
} else {
mNyanHandler.removeCallbacks(mNyanNyan);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
mScaleX = width / (1f * mNyan.width());
mScaleY = height / (1f * mNyan.height());
nyan();
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);
nyan();
}
void nyan() {
tick();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
nyanNyan(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
mNyanHandler.removeCallbacks(mNyanNyan);
if (isVisible()) {
mNyanHandler.postDelayed(mNyanNyan, 1000L/25L);
}
}
void tick() {
if (mWhen == -1L) {
mWhen = 0;
mStart = SystemClock.uptimeMillis();
} else {
long mDiff = SystemClock.uptimeMillis() - mStart;
mWhen = (int) (mDiff % mNyanDuration);
}
}
void nyanNyan(Canvas canvas) {
canvas.save();
canvas.scale(mScaleX, mScaleY);
mNyan.setTime(mWhen);
mNyan.draw(canvas, 0, 0);
canvas.restore();
}
}
}
This will basically scale the nyan-nyan cat to fit the screen and animate it perpetually.
A Live wallpaper manifest looks sort-of-like this (this example does not contain a configuration activity):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your.nyan.nyan.package"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/application_nyan" >
<service
android:label="@string/wallpaper_nyan"
android:name=".NyanNyanService"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data android:name="android.service.wallpaper" android:resource="@xml/nyan" />
</service>
</application>
</manifest>
The AndroidManifest.xml has a reference to a file in res/xml
, in this case named "nyan.xml":
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />

- 16,853
- 4
- 55
- 52
-
it executed but there is no output on screen, in my console shows, cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=test.anim/.NyanNyanService } from null (pid=7019, uid=2000) requires android.permission.BIND_WALLPAPER – Aerrow Feb 06 '12 at 13:22
-
I think i make mistake in manifest file, could you please post the manifest file. – Aerrow Feb 06 '12 at 13:24
-
hm. yes, you cannot start a live wallpaper from anything that does not hold the BIND_WALLPAPER permission (which is reserved for system applications) - i.e. to test your wallpaper you select it in the live wallpaper picker in your home screen. – Jens Feb 06 '12 at 14:03
-
1hi... this nice bat some time more then 1 gif file and used only one service that time the code not sported so many problem fase try – Android Aug 17 '12 at 11:17
-
1+1 for great explanation. Can i play video instead of GIF image ? if Yes how ? – Hardik Joshi Nov 03 '12 at 11:42
-
If you can render it to a Canvas you should, depending on the performance of your device. – Jens Nov 03 '12 at 19:07
-
-
see this if you can answer http://stackoverflow.com/questions/14372878/unable-to-change-animated-gif-in-live-wallpaper-in-android – moDev Jan 18 '13 at 07:29
-
This was an excellent answer that I used to make my own Nyan Cat live wallpaper (no, I'm not kidding.) I would like to make it available for free on the Android Market, and would be willing to host the code on Github, but of course I won't do either without your blessing or without crediting you. May I do these things? – peejaybee Feb 26 '13 at 18:59
-
4
-
1If you notice tearing on lower end devices or older versions of Android (I haven't done the testing that would tell me which was at fault) check to see that (1) The animated gif is not optimized and (2) all the frames are the same size. The live wallpaper looked terrible on a G2 running Gingerbread. Step 1 was a huge improvement, but it took both steps to make it look right on the older phone. – peejaybee Mar 05 '13 at 14:03
-
What is the "Movie" class? Is it really about all kinds of videos files, and not just GIF animation? All I've found is a rather empty documentation of it: https://developer.android.com/reference/android/graphics/Movie.html . Does it mean I can use it to view other video files on the live wallpaper, using the same code? – android developer Apr 19 '18 at 18:50
See this article: How to set gif as android live wallpaper - techprpr - Medium
Jens answer use "Movie" class, but Movie class is deprecated in android api 28
so when api >= 28, I use AnimatedImageDrawable instead
Setup live wallpaper like Jens answer, and I change wallpaper service code:
wallpaper service: AnimWallpaper
public class AnimWallpaper extends WallpaperService {
@Override
public Engine onCreateEngine() {
return new CustomEngine();
}
class CustomEngine extends Engine {
UseAnim useAnim;
public CustomEngine() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
useAnim = new UseAnim(getApplicationContext(), getSurfaceHolder(), R.raw.gif2);
}
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
useAnim.restart();
} else {
useAnim.stop();
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
useAnim.updateScaleAndPadding2(width, height);
useAnim.restart();
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);
useAnim.restart();
}
@Override
public void onDestroy() {
super.onDestroy();
useAnim.stop();
}
}
}
UseAnim
class UseAnim {
SurfaceHolder holder;
Runnable startRunnable;
AnimatedImageDrawable gif;
float fps = 60;
Handler handler = new Handler();
@RequiresApi(api = Build.VERSION_CODES.P)
public UseAnim(Context ctx, SurfaceHolder holder, int gifResId) {
this.holder = holder;
final ImageDecoder.Source src = ImageDecoder.createSource(ctx.getResources(), gifResId);
startRunnable = new Runnable() {
@Override
public void run() {
start();
}
};
new Handler().post(new Runnable() {
@Override
public void run() {
try {
UseAnim.this.gif = (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
} catch (IOException e) {
throw new RuntimeException(e);
}
UseAnim.this.gif.start();
}
});
}
public void restart() {
stop();
start();
}
float scale = -1;
public void start() {
// since get gif with AnimatedImageDrawable must be in handler.post, so gif maybe null
if (gif != null) {
Canvas canvas = null;
try {
if (scale == -1) {
updateScaleAndPadding();
}
canvas = holder.lockCanvas();
if (canvas != null) {
canvas.translate(horiPadding, vertiPadding);
canvas.scale(scale, scale);
gif.draw(canvas);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
handler.removeCallbacks(startRunnable);
handler.postDelayed(startRunnable, (long) (1000L / fps));
}
public void stop() {
handler.removeCallbacks(startRunnable);
}
int horiPadding;
int vertiPadding;
private void updateScaleAndPadding() {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
int cw = canvas.getWidth();
int ch = canvas.getHeight();
updateScaleAndPadding2(cw, ch);
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
public void updateScaleAndPadding2(int cw, int ch) {
if (gif != null) {
int gifW = gif.getIntrinsicWidth();
int gifH = gif.getIntrinsicHeight();
if (gifW * 1f / gifH > cw * 1f / ch) {
scale = ch * 1f / gifH;
} else {
scale = cw * 1f / gifW;
}
horiPadding = (int) ((cw - gifW * scale) / 2);
vertiPadding = (int) ((ch - gifH * scale) / 2);
}
}
}

- 3,591
- 4
- 41
- 73
-
This code has errors. When I try to access this command I got the crash NullPointerException! useAnim.updateScaleAndPadding2(width, height); – Dyno Cris Nov 14 '20 at 22:15
-
@DynoCris in my example, R.raw.gif2 is my gif in my project "raw" dir, doesn't exist in your project, you can find android docs with AnimatedImageDrawable, to understand how to make AnimatedImageDrawable show gif – chikadance Nov 15 '20 at 09:34
-
of course I added this gif file. What to do else to lauch your snippet? To fix NPE with updateScaleAndPadding2() method – Dyno Cris Nov 15 '20 at 14:36