我是Android平台的新手。我希望开发一个动态壁纸应用程序。当我在搜索引擎中对此进行搜索时,他们中的许多人创建了一个动态壁纸作为代码(使用SurfaceView
和Canvas
),我对此并不太了解。我怀疑的是,任何可以将.gif图像设置为动态壁纸。
答案 0 :(得分:47)
这是基本的壁纸服务(在动态壁纸教程中提供)被黑客攻击以显示GIF动画。
首先 - 创建一个项目&将您的清单设置为动态壁纸 然后 - 下载一个gif,就像这个一样
将gif保存在项目的res/raw/nyan.gif
中
创建一个动态壁纸服务,如本例所示。
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();
}
}
}
这将基本上缩放nyan-nyan猫以适应屏幕并永久制作它。
动态壁纸清单看起来很像(此示例不包含配置活动):
<?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>
AndroidManifest.xml引用res/xml
中的文件,在本例中名为“nyan.xml”:
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />
答案 1 :(得分:0)
查看本文:How to set gif as android live wallpaper - techprpr - Medium
Jens答案使用“电影”类,但在android API 28中不推荐使用电影类
所以当api> = 28时,我改用AnimatedImageDrawable
设置动态壁纸(如Jens Answer),然后更改壁纸服务代码:
墙纸服务: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);
}
}
}