Android自定義動(dòng)態(tài)壁紙開發(fā)(時(shí)鐘)
看到有些手機(jī)酷炫的動(dòng)態(tài)壁紙,有沒有好奇過他們是如何實(shí)現(xiàn)的,其實(shí)我們自己也可以實(shí)現(xiàn)。
先看效果

上圖是動(dòng)態(tài)壁紙鐘的一個(gè)時(shí)鐘。
我們先來看看 Livewallpaper(即動(dòng)態(tài)墻紙)的實(shí)現(xiàn),Android的動(dòng)態(tài)墻紙并不是GIF圖片,而是一個(gè)標(biāo)準(zhǔn)的Android應(yīng)用程序,也就是APK。既然是應(yīng)用程序,當(dāng)然意味著天生具有GIF圖片不具備的功能——能與用戶發(fā)生交互,而且動(dòng)態(tài)的背景變化絕不僅僅局限于GIF圖片那般只能是固定的幾張圖片的循環(huán)播放。但是我們?cè)谶@里沒有加入與用戶交互的動(dòng)作,只是加入一個(gè)時(shí)鐘(當(dāng)然時(shí)間取的是系統(tǒng)時(shí)間);
新建android工程;
在res下面新建一個(gè)xml文件夾 然后新建一個(gè)wallpaper.xml 內(nèi)容如下
<?xml version="1.0" encoding="utf-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:thumbnail="@mipmap/heart" />
其中android:thumbnail="@mipmap/heart"是我們?cè)谶@是壁紙時(shí)可以看到的圖標(biāo)
3. 創(chuàng)建一個(gè)MainWallpaper繼承自WallpaperService,并實(shí)現(xiàn)onCreateEngine方法,在此方法中我們要實(shí)現(xiàn)Engine類并返回。那我們這里就要?jiǎng)?chuàng)建一個(gè)MyEgine類繼承Engine,實(shí)現(xiàn)他的OnCreate,onVisibilityChanged方法。到此我們就可以在OnCreate 方法中初始化我們的畫筆等,onVisibilityChanged方法中畫出我們想要的效果;
public class MainWallpaper extends WallpaperService {
private static final String TAG = "MainWallpaper";
@Override
public Engine onCreateEngine() {
return new MyEngine();
}
class MyEngine extends Engine implements Runnable{
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
//這里初始化畫筆等
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
//這里開始繪制
}
}
當(dāng)然到此動(dòng)態(tài)壁紙還沒有完成,還有最重要的一步就是配置AndroidManifest.xml,Android動(dòng)態(tài)壁紙的本質(zhì)是一個(gè)“Service”,這個(gè)Service能夠被主界面Launcher加載,所以我們應(yīng)該注冊(cè)一個(gè)service 如下:
<service android:label="@string/app_name"
android:name="com.demo.mylove.MainWallpaper"
android:permission="android.permission.BIND_WALLPAPER">
<!-- 為實(shí)時(shí)壁紙配置intent-filter -->
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<!-- 為實(shí)時(shí)壁紙配置meta-data 沒錯(cuò)這里wallpaper是我們第一步創(chuàng)建的xml文件-->
<meta-data android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>
動(dòng)態(tài)壁紙的創(chuàng)建已完成,那么接下來就要完成我們的時(shí)鐘的繪制咯!
在上文中的初始化畫筆位置還有重要步驟就是獲取我們所需要的SurfaceHolder及獲取屏幕的寬高
WindowManager wm= (WindowManager) getSystemService(WINDOW_SERVICE);
Display display=wm.getDefaultDisplay();
DisplayMetrics displayMetrics=new DisplayMetrics();
display.getMetrics(displayMetrics);
mWidth =displayMetrics.widthPixels;
mHeight =displayMetrics.heightPixels;
//獲取SurfaceHolder
mHolder=getSurfaceHolder();
那么接下來就是繪制時(shí)鐘的(需要放入線程中繪制)
// 繪制方法
private void drawFrame()
{
// 對(duì)畫布加鎖
mCanvas = mHolder.lockCanvas();
if (mCanvas != null)
{
mCanvas.save();
// 繪制背景色
mCanvas.drawColor(0xff000000);
//mCanvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.wall_background),null,mRectF,mPaint);
drawClock();
if (mCanvas != null)
mHolder.unlockCanvasAndPost(mCanvas);
}
}
private void drawClock(Canvas canvas){
mCalendar = Calendar.getInstance();
int second=mCalendar.get(Calendar.SECOND);
int minute=mCalendar.get(Calendar.MINUTE);
int hour=mCalendar.get(Calendar.HOUR);
String time=hour+":"+minute+":"+second;
mCanvas.save();
//畫秒針及間隔
mCanvas.translate(mWidth/2,mHeight/2);//平移畫布的中心位置
float item;//外圈的分割線的長度
for (int i = 0; i< CLOCK_COUNT; i++){//畫間隔
mPaint.setColor(Color.RED);
if(second==i){
mPaint.setColor(Color.BLUE);
}
if (i%15==0){//整點(diǎn)的位置
item=-radius+27;
}else if (i%5==0){//整點(diǎn)中間位置
item=-radius+37;
}else {//其他時(shí)間
item=-radius+17;
}
mCanvas.drawLine(0,-radius,0,item,mPaint);
mCanvas.rotate(CLOCK_ROTATE);
}
mCanvas.save();
//畫分針
mCanvas.rotate(minute* CLOCK_ROTATE +(CLOCK_ROTATE /60)*second);
mCanvas.drawLine(0,0,0,-radius/2,mPaint);
mCanvas.restore();
mCanvas.save();
//畫時(shí)針
mCanvas.rotate(hour*(360/12)+minute/2);
mCanvas.drawLine(0,0,0,-radius/3,mPaint);
mCanvas.restore();
mCanvas.save();
//畫中心點(diǎn)
mCanvas.drawCircle(0,0,20,mPaint);
//畫時(shí)鐘顯示的時(shí)間
mCanvas.getTextBounds(time,0,time.length(), mRect);
mCanvas.drawText(time,-mRect.width()/2,radius+40,mPaint);
mCanvas.getTextBounds(TWELVE,0,TWELVE.length(), mRect);
//畫數(shù)字12
mCanvas.drawText(TWELVE,-mRect.width()/2,-radius+60,mPaint);
mCanvas.getTextBounds(NINE,0,NINE.length(), mRect);
//畫數(shù)字3
mCanvas.drawText(THREE,radius-60,mRect.height()/2,mPaint);
//畫數(shù)字6
mCanvas.drawText(SIX,-mRect.width()/2,radius-40,mPaint);
//畫數(shù)字9
mCanvas.drawText(NINE,-radius+40,mRect.height()/2,mPaint);
}
到這里只是實(shí)現(xiàn)效果,還有一些細(xì)節(jié)問題,希望有興趣的朋友可以將其完善共同交流;如有更好的實(shí)現(xiàn)方法忘不吝賜教
一、動(dòng)態(tài)壁紙?jiān)?/strong>
如果你了解使用過SurfaceView的話,那么開發(fā)一款動(dòng)態(tài)壁紙對(duì)你來說其實(shí)非常簡單。
動(dòng)態(tài)壁紙的本質(zhì)其實(shí)就是一個(gè)服務(wù)在維護(hù)一個(gè)動(dòng)態(tài)壁紙引擎Engine,所以我們看到的動(dòng)態(tài)效果其實(shí)是通過這個(gè)引擎畫出來的。而維護(hù)這個(gè)引擎的服務(wù),就是WallpaperService。本篇文章并不討論內(nèi)部實(shí)現(xiàn)原理,只是讓大家知道如何去實(shí)現(xiàn)動(dòng)態(tài)壁紙,所以就不詳細(xì)說了。
二、實(shí)現(xiàn)動(dòng)態(tài)壁紙
大體上可分為三個(gè)步驟:
- 創(chuàng)建自定義WallpaperService繼承WallpaperService
- 在Manifest中注冊(cè)該Service并添加相關(guān)屬性
- 創(chuàng)建所需要的xml文件
1.創(chuàng)建自定義WallpaperService
public class MyLwp extends WallpaperService {
@Override
public Engine onCreateEngine() {
return new MyEngine();
}
class MyEngine extends Engine{
@Override
public SurfaceHolder getSurfaceHolder() {
return super.getSurfaceHolder();
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
}
}
}
2.Manifest注冊(cè)
<service
android:name=".MyLwp"
android:enabled="true"
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/livewallpapervideo" />
</service>
一定要添加的幾個(gè)地方:permission、intent-filter、meta-data。
3.創(chuàng)建需要的xml文件
這個(gè)xml文件就是Manifest中meta-data中的resource需要的文件:
<?xml version="1.0" encoding="utf-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/app_name" android:settingsActivity="ASD" android:thumbnail="@mipmap/ic_launcher"/>
需要注意第二個(gè)屬性:settingsActivity,這個(gè)屬性可以設(shè)置也可以不設(shè)置,他是啟動(dòng)一個(gè)設(shè)置動(dòng)態(tài)壁紙的界面,一般情況下其實(shí)用不到這個(gè)界面,我們一般會(huì)使用PreferenceActivity去實(shí)現(xiàn)。下面是添加該屬性和不添加該屬性的區(qū)別:

完成這些之后,就是我們?cè)O(shè)計(jì)動(dòng)態(tài)壁紙的時(shí)候了?;氐阶远x的Wallpaper類中:
我們當(dāng)時(shí)在類中自定義了一個(gè)內(nèi)部類MyEngine繼承自Engine。這個(gè)Engine就是用來繪制的。關(guān)于Engine的幾個(gè)主要方法如下:
class MyEngine extends Engine{
//獲取SurfaceHolder時(shí)調(diào)用
@Override
public SurfaceHolder getSurfaceHolder() {
return super.getSurfaceHolder();
}
//手勢(shì)移動(dòng)時(shí)回調(diào)
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset, yPixelOffset);
}
//Surface創(chuàng)建時(shí)回調(diào)
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
//Surface銷毀時(shí)回調(diào)
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
}
}
onOffsetsChanged要注意一下,還記得有的手機(jī)滑動(dòng)桌面時(shí)候背景圖片會(huì)跟著左右移動(dòng)嗎,這個(gè)方法就可以實(shí)現(xiàn)這個(gè)效果,在手勢(shì)滑動(dòng)的每一幀都會(huì)回調(diào)依次。一下是個(gè)人理解的參數(shù)的含義:
- xOffset:x方向滑動(dòng)的百分比(與桌面分頁數(shù)有關(guān))
- yOffset:y方向滑動(dòng)百分比(一般用不到)
- xOffsetStep:x方向每個(gè)分頁所占的百分比(1 / xOffsetStep = 桌面的分頁數(shù))
- yOffsetStep:同
- xPixelOffset:x放下像素偏移量
- y。。。
4.繪制。
實(shí)現(xiàn)繪制的方式,就跟SurfaceView的繪制一樣了:
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.RED);
holder.unlockCanvasAndPost(canvas);
}
我只是簡單的話了一個(gè)紅色背景,效果如下:

當(dāng)然可以實(shí)現(xiàn)很多不同的效果,這個(gè)就根據(jù)不同的需求去實(shí)現(xiàn)了。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
- Android獲取設(shè)備CPU核數(shù)、時(shí)鐘頻率以及內(nèi)存大小的方法
- Android多功能時(shí)鐘開發(fā)案例(實(shí)戰(zhàn)篇)
- android實(shí)現(xiàn)widget時(shí)鐘示例分享
- Android 仿日歷翻頁、仿htc時(shí)鐘翻頁、數(shù)字翻頁切換效果
- Android多功能時(shí)鐘開發(fā)案例(基礎(chǔ)篇)
- android高仿小米時(shí)鐘(使用Camera和Matrix實(shí)現(xiàn)3D效果)
- Android實(shí)現(xiàn)簡單時(shí)鐘View的方法
- Android編程基于自定義控件實(shí)現(xiàn)時(shí)鐘功能的方法
- Android仿小米時(shí)鐘效果
- Android自定義View實(shí)現(xiàn)時(shí)鐘功能
相關(guān)文章
Android啟動(dòng)頁優(yōu)化之實(shí)現(xiàn)應(yīng)用秒開
現(xiàn)在很多應(yīng)用都會(huì)在進(jìn)入主界面之前,添加一個(gè)啟動(dòng)頁,然后加入幾秒鐘的廣告,我覺得這個(gè)不能算是 “真正意義上的 “ 啟動(dòng)頁,應(yīng)該叫廣告頁。2021-05-05
Android中EditText+Button組合導(dǎo)致輸入板無法收起的原因分析及解決辦法
這篇文章主要介紹了Android中EditText+Button組合導(dǎo)致輸入板無法收起的原因分析及解決辦法的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android 驅(qū)動(dòng)編寫LED-NDK程序
這篇文章主要介紹了Android 驅(qū)動(dòng)編寫LED-NDK程序的相關(guān)資料,需要的朋友可以參考下2016-09-09
Android自定義ViewGroup實(shí)現(xiàn)朋友圈九宮格控件
在我們的實(shí)際應(yīng)用中,經(jīng)常需要用到自定義控件,比如自定義圓形頭像,自定義計(jì)步器等等,這篇文章主要給大家介紹了關(guān)于Android自定義ViewGroup實(shí)現(xiàn)朋友圈九宮格控件的相關(guān)資料,需要的朋友可以參考下2021-07-07
Android App仿QQ制作Material Design風(fēng)格沉浸式狀態(tài)欄
這篇文章主要介紹了Android App仿QQ制作Material Design風(fēng)格沉浸式狀態(tài)欄的實(shí)例,同時(shí)也給出了4.4版本下實(shí)現(xiàn)效果與5.0的對(duì)比,需要的朋友可以參考下2016-04-04
Android的權(quán)限設(shè)置及自啟動(dòng)設(shè)置方法
今天小編就為大家分享一篇Android的權(quán)限設(shè)置及自啟動(dòng)設(shè)置方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-07-07
android Launcher3設(shè)置默認(rèn)桌面應(yīng)用
這篇文章主要為大家詳細(xì)介紹了android Launcher3設(shè)置默認(rèn)桌面應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android開發(fā)筆記之:深入理解多線程AsyncTask
本篇文章是對(duì)Android中多線程AsyncTask進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

