Android动态壁纸详解
16lz
2021-01-23
动态壁纸
Livewallpaper(动态壁纸): 首先动态壁纸并不是GIF图片,而是一个独立的应用程序,本质是一个Service,甚至可以没有桌面图标。
直接看AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android" package="com.qwq.clocklivewallpaper"> "true" android:icon="@drawable/clock" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> ".MainActivity"> "android.intent.action.MAIN" /> "android.intent.category.LAUNCHER" /> ".SettingsActivity" android:exported="true" android:label="@string/app_name"> ".ClockWallpaperService" android:enabled="true" android:label="@string/wallpaper_name" //下面第一张图片中label android:permission="android.permission.BIND_WALLPAPER">//动态壁纸必须加此权限 //系统就是通过APK的这个action把其当做一个动态墙纸。 "android.service.wallpaper.WallpaperService"> "android.service.wallpaper" android:resource="@xml/clock_wallpaper">
clock_wallpaper.xml
<?xml version="1.0" encoding="UTF-8"?>"http://schemas.android.com/apk/res/android" android:description="@string/wallpaper_description" android:settingsActivity="com.qwq.clocklivewallpaper.SettingsActivity" android:thumbnail="@drawable/ic_preview" />
android:thumbnail 动态壁纸列表中的图标,
android:description 动态壁纸的简单介绍文字,有的手机可能不显示
android:settingsActivity 指定一个Activity。系统会检测动态壁纸应用有没有此属性。如果有则和下方的图片显示效果一样(两个Button),点击下图中设定Button可跳转至指定的Activity(设置界面)。如果没有此属性,就只有一个设置壁纸Button。可以去掉此属性看一下效果。
WallpaperService(核心)
实现动态壁纸必须继承WallpaperService,且重载onCreateEngine方法。onCreateEngine()方法只需返回一个Engine的子类对象就可以了,所以动态壁纸应用的主要工作就是实现Engine的子类。其原理是使用surfaceview不断更新,实现动态壁纸效果。不多说了,直接贴代码,重要的方法都会有所说明。
package com.qwq.clocklivewallpaper;import android.content.SharedPreferences;import android.content.SharedPreferences.OnSharedPreferenceChangeListener;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.Handler;import android.preference.PreferenceManager;import android.service.wallpaper.WallpaperService;import android.util.Log;import android.view.MotionEvent;import android.view.SurfaceHolder;import java.util.Date;public class ClockWallpaperService extends WallpaperService { static final String TAG = ClockWallpaperService.class.getSimpleName(); static final int POINTER_WIDTH = 9; // onCreateEngine()方法需返回一个Engine的子类对象就可以了, // 所以动态壁纸应用的主要工作就是实现Engine的子类 @Override public Engine onCreateEngine() { Log.d(TAG, "onCreateEngine"); return new ClockWallpaperEngine(); } //实现Engine的子类,本文的重中之重,动态壁纸就是在此具体实现的 private class ClockWallpaperEngine extends Engine implements OnSharedPreferenceChangeListener { private final Handler handler = new Handler(); // surfacewive使用线程更新UI,所以我们可使用Runnable接口创建一个线程,把具体绘制方法放进去, // 下文会使用handler调用 private final Runnable drawRunner = new Runnable() { @Override public void run() { draw(); } }; private Paint paint; private int width; private int height; private boolean isVisible = true; private boolean isShowSecond; private ClockView clockView; private SharedPreferences sp; //构造函数,初始化动态壁纸 public ClockWallpaperEngine() { Log.d(TAG, "ClockWallpaperEngine"); initSp(); initPaint(); startDrawClock(); } public void initSp() { sp = PreferenceManager.getDefaultSharedPreferences(ClockWallpaperService.this); sp.registerOnSharedPreferenceChangeListener(this); isShowSecond = sp.getBoolean(SettingsActivity.IS_SHOW_SECOND, true); } public void initPaint() { paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(POINTER_WIDTH); } public void startDrawClock() { clockView = new ClockView(getApplicationContext()); handler.post(drawRunner); } @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); Log.d(TAG, "onCreate"); } //监听是否可见变化,可见时开始更新UI,不可见时停止刷新 @Override public void onVisibilityChanged(boolean visible) { this.isVisible = visible; if (visible) { handler.post(drawRunner); } else { handler.removeCallbacks(drawRunner); } } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed(holder); isVisible = false; handler.removeCallbacks(drawRunner); sp.unregisterOnSharedPreferenceChangeListener(this); } @Override public void onSurfaceChanged(SurfaceHolder holder,int format,int width,int height){ this.width = width; this.height = height; super.onSurfaceChanged(holder, format, width, height); } //绘制,每个200毫秒刷新界面 private void draw() { SurfaceHolder holder = getSurfaceHolder(); Canvas canvas = null; try { canvas = holder.lockCanvas(); if (canvas != null) { drawClock(canvas); } } finally { if (canvas != null) { holder.unlockCanvasAndPost(canvas); } } handler.removeCallbacks(drawRunner); if (isVisible) { handler.postDelayed(drawRunner, 200); } } //具体绘制钟表 private void drawClock(Canvas canvas) { canvas.drawColor(Color.WHITE); //绘制整个动态壁纸的背景颜色 clockView.config(width / 2, height / 2, (int) (width * 0.8f), new Date(), paint, isShowSecond); clockView.draw(canvas); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){ //监听SharedPreference变化,改变动态壁纸样式 if (SettingsActivity.IS_SHOW_SECOND.equals(key)) { isShowSecond = sharedPreferences.getBoolean(SettingsActivity.IS_SHOW_SECOND, true); } } @Override public void onTouchEvent(MotionEvent event) { super.onTouchEvent(event); // 可以在这里做一些与用户交互的操作,例如动态星星壁纸,用户触摸屏幕时增加星星数量。 Log.d(TAG, "onTouchEvent"); } }}
ClockView(钟表的具体绘制)
package com.qwq.clocklivewallpaper;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.Log;import android.view.View;import java.util.Calendar;import java.util.Date;public class ClockView extends View { static final String TAG = ClockView.class.getSimpleName(); private float x; private float y; private int radius; private Calendar cal; private Paint paint; private Bitmap clockDial = BitmapFactory.decodeResource(getResources(),R.drawable.clock_bg); private int sizeScaled = -1; private Bitmap clockDialScaled; private boolean isShowSecond; public ClockView(Context context) { super(context); cal = Calendar.getInstance(); }//设置钟表宽高等属性public void config(float x, float y, int size, Date date, Paint paint, boolean isShowSecond) { this.x = x; this.y = y; this.paint = paint; this.isShowSecond = isShowSecond; cal.setTime(date); if (size != sizeScaled) { clockDialScaled = Bitmap.createScaledBitmap(clockDial, size, size, false); radius = size / 2; } }//具体绘制钟表的指针protected void onDraw(Canvas canvas) { Log.d(TAG,"onDraw:"+canvas); super.onDraw(canvas); if (paint != null) { canvas.drawBitmap(clockDialScaled, x - radius, y - radius, null); float sec = cal.get(Calendar.SECOND); float min = cal.get(Calendar.MINUTE); float hour = cal.get(Calendar.HOUR_OF_DAY); paint.setColor(Color.RED); canvas.drawLine(x, y, (float)(x + (radius * 0.5f)*Math.cos(Math.toRadians((hour / 12.0f * 360.0f) - 90f))), (float)(y + (radius * 0.5f)*Math.sin(Math.toRadians((hour / 12.0f * 360.0f) -90f))), paint); canvas.save(); paint.setColor(Color.BLUE); canvas.drawLine(x, y, (float)(x + (radius * 0.6f)*Math.cos(Math.toRadians((min / 60.0f * 360.0f) - 90f))), (float)(y + (radius * 0.6f)*Math.sin(Math.toRadians((min / 60.0f * 360.0f) - 90f))), paint); canvas.save(); if (isShowSecond) { paint.setColor(Color.GREEN); canvas.drawLine(x, y, (float)(x + (radius * 0.7f)*Math.cos(Math.toRadians((sec/60.0f * 360.0f) - 90f))), (float)(y + (radius * 0.7f)*Math.sin(Math.toRadians((sec/60.0f * 360.0f) - 90f))), paint); } } }}
更多相关文章
- android实现动态更换应用图标
- Android ViewPager实现Tabhost选项卡底部滑块动态滑动过渡
- Android 进阶——Material Design新控件之AppBarLayout+Toolbar+
- Android动态分析工具Inspeckage
- Android 10.0 Andorid.bp 动态编译模块
- 【Android高级】DexClassloader和PathClassloader动态加载插件的
- Android 中关于PathEffect子类的效果(中级)
- Android下动态链接库.so调用的简单例子