最近做動態壁紙的項目,原來覺得動態壁紙是個很小的項目,但是看到Android Market上有個專門的動態壁紙分類(現在升級爲Google Play了), 而且自己做的這個項目可連接上服務器,供用戶購買下載圖片,終於有了自信,認識到這個不算是個小項目了。接下來我主要談談動態壁紙的原理,然後會解釋一個 “小球的例子”,供大家能深入的理解該原理。
一:原理
動態壁紙爲:在手機上點擊 Menu→Wallpapers→Live wallpapers→然後打開自己的程序。建個最簡單的動態壁紙的步驟如下:
1.在rex/xml中新建一個.xml.其中註冊一個wallpaper.假設這個名字爲ab.xml(下文要用到,可隨意設置,沒要求)
最簡單的就是寫 這一句,這樣的話打開動態壁紙就會出現只出現一個按鈕(左圖),一般我們不這樣做,要像右圖這樣子。
若動態壁紙"設置..."(Setting...)你想連接Activity,也在這裏指定,比如:
android:settingsActivity="com.birbeck.wallpaperslideshow.SettingsActivity" (這個一般是繼承了PreferenceActivity類的Activity。就是首選項模式的類),要設置了這個屬性,就會如有圖所示了。
如上截圖是手機上的動態壁紙列表,你也可以通過android:description=“XXX”來設置描述,通過anroid:thumbnail="XX"來設置該動態壁紙的圖片。
2.接下來要在manifest中註冊一個service。
XXX
在這個servier中要指定你繼承WallpaperService類的路徑,指定1中設置的xml,設置廣播,設置允許權限等。比如:
通過android:name="com.bn.ex12f.Sample12_6_WallPaper"指定繼承WallpaperService的類 ,
通過android:permission="android.permission.BIND_WALLPAPER">允許動態壁紙權限。
這一種還必須設置一個,用來監聽Android系統發出的動態壁紙的廣播。
還要通過ab" />.這篇文章中主要講原理和重要的點,源碼我會附上的。
3.就是實現繼承了WallpaerService的類了。只需要重寫WallpaperServiced的onCreateEngine方法。
@Override
public Engine onCreateEngine()
{
ce=new BallEngine();(class BallEngine extends Engine{...})
return ce;
}
在這個方法裏只需返回一個Engine的子類對象就可以了。所以重頭戲,寫動態壁紙程序的主要工作量就是實現Engine的子類。
4.實現Engine的子類
簡而言之,該類的作用就是讓你去實現動態壁紙的具體代碼。以上三點可認爲是格式化的一些東西。這個類不需要強制繼承任何方法,現在簡述一下一般要重寫的方法的功能。
public void onCreate(SurfaceHolder surfaceHolder){...}
public void onDestroy(){...}這倆方法就不說明了
public void onVisibilityChanged(boolean visible)
{
if(visible)//如果可見
{
...
}
else//如果不可見
{
...
}
}
該方法作用是當前動態壁紙可見時要畫圖。重寫這個方法一般如以上格式所示。
public void onSurfaceCreated(SurfaceHolder holder) //重寫onSurfaceCreated方法
{
super.onSurfaceCreated(holder);//調用父類對應方法
}
該方法是應用程序第一次創建時要調用。可在這個方法裏調用父類對應方法。該方法執行完畢後系統會立即調用onSurfaceChanged方法(如下)。若在這裏調用父類對應方法,那麼就在onSurfaceChanged中實現主要功能。
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
...
}
該方法有兩個用處。1.若動態壁紙要隨着橫屏豎屏而切換可在這裏寫。2.想和用戶交互的話,比如用戶滑動屏幕時,點擊屏幕時等。3.注意:onSurfaceCreated調用之後會立即調用該方法。
這些就是動態壁紙原理的介紹。接下來是一個小例,希望大家能夠喜歡。這個例子很簡單。效果圖如下:
功能說明:黃 藍 綠三個小球(截圖不好,球顯示不對)。碰到屏幕邊的話會像談到地面上一樣,會返回。
這個是繼承了WallpaerService的類的代碼。
package com.bn.ex12f;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.view.SurfaceHolder;
public class Sample12_6_WallPaper extends WallpaperService
{
BallEngine ce; //BallEngine的引用
Handler hd = new Handler();//創建Handler對象
Bitmap yellowBallBitmap;//黃球位圖
Bitmap blueBallBitmap;//藍球位圖
Bitmap greenBallBitmap;//綠球位圖
@Override
public Engine onCreateEngine() //重寫onCreateEngine方法
{
ce=new BallEngine(); //創建 BallEngine對象
return ce;//返回創建的對象
}
//初始化圖片資源的方法
public void initBitmap(){
yellowBallBitmap=BitmapFactory.decodeResource(this.getResources(), R.drawable.yellowball);//初始化黃球
blueBallBitmap=BitmapFactory.decodeResource(this.getResources(), R.drawable.blueball);//初始化藍球
greenBallBitmap=BitmapFactory.decodeResource(this.getResources(), R.drawable.greenball);//初始化綠球
}
class BallEngine extends Engine //創建內部類
{
Sample12_6_WallPaper father;//MovingBallWallPaper的引用
private final Paint paint = new Paint(); //創建畫筆
boolean ifDraw;//是否可見的標誌位
BallGoThread bgThread; //BallGoThread引用
AllBalls allBalls;// AllBalls對象的引用
private final Runnable mDrawCube = new Runnable() {//匿名內部類
public void run() {//重寫run方法
drawBalls();//調用drawBalls方法
}
};
@Override
public void onCreate(SurfaceHolder surfaceHolder) //重寫onCreate方法
{
super.onCreate(surfaceHolder); //調用父類對應方法
paint.setAntiAlias(true);//打開抗鋸齒
initBitmap();//初始化位圖資源
}
@Override
public void onDestroy() //重寫onDestroy方法
{
super.onDestroy();//調用父類對應方法
}
@Override
public void onVisibilityChanged(boolean visible) //重寫onVisibilityChanged方法
{
ifDraw=visible;//獲得是否可見標誌位
if(ifDraw)//如果可見
{
bgThread=new BallGoThread(allBalls);//創建BallGoThread線程
bgThread.start();//啓動該線程
hd.postDelayed(mDrawCube, Constant.MOVE_TIME);//一定時間後繪製球
}
else//如果不可見
{
bgThread.ballGoFlag=false;//停止BallGoThread線程
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, intheight) //重寫onSurfaceChanged方法
{
super.onSurfaceChanged(holder, format, width, height); //調用父類對應方法
Constant.SCREEN_HEIGHT=height; //初始化寬和高
Constant.SCREEN_WIDTH=width;
int[] ballsSize={Constant.YELLOW_BALL_SIZE,Constant.BLUE_BALL_SIZE,Constant.GREEN_BALL_SIZE};//所有球尺寸數組
Bitmap[] ballsBitmap={yellowBallBitmap,blueBallBitmap,greenBallBitmap};//所有球位圖數組
int[] ballsXspan={Constant.YELLOW_XSPAN,Constant.BLUE_XSPAN,Constant.GREEN_XSPAN};//所有球的xSpan數組
int[] ballsYspan={Constant.YELLOW_YSPAN,Constant.BLUE_YSPAN,Constant.GREEN_YSPAN};//所有球的ySpan數組
allBalls=new AllBalls(ballsSize,ballsBitmap,ballsXspan,ballsYspan);//創建AllBalls對象
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) //重寫onSurfaceCreated方法
{
super.onSurfaceCreated(holder);//調用父類對應方法
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) //重寫onSurfaceDestroyed方法
{
super.onSurfaceDestroyed(holder);//調用父類對應方法
}
void drawBalls() //繪製所有球的方法
{
final SurfaceHolder holder = getSurfaceHolder();//獲得SurfaceHolder對象
Canvas canvas = null;//聲明畫布引用
try
{
canvas = holder.lockCanvas();//鎖定並獲得畫布對象
if (canvas != null) //如果已得到畫布對象
{
canvas.drawColor(Color.argb(255, 0, 0, 0));//擦空界面
allBalls.drawSelf(canvas, paint);//繪製所有的球
}
}
finally
{
if (canvas != null) holder.unlockCanvasAndPost(canvas);//繪製完釋放畫布
}
if(ifDraw)//如果桌面可見
{
hd.postDelayed(mDrawCube,Constant.MOVE_TIME);//一定時間後繪製球
}
}
}
}
定義常量的類:
package com.bn.ex12f;
/*
* 橫豎屏公共常量類
*/
public class Constant {
public static int SCREEN_WIDTH;//屏幕寬度
public static int SCREEN_HEIGHT;//屏幕高度
public static final int MOVE_TIME=10;//繪製所有球的時間間隔
public static final int YELLOW_XSPAN=1;//黃球x方向步進
public static final int YELLOW_YSPAN=1;//黃球y方向步進
public static final int BLUE_XSPAN=1;//藍球x方向步進
public static final int BLUE_YSPAN=1;//藍球y方向步進
public static final int GREEN_XSPAN=1;//綠球x方向步進
public static final int GREEN_YSPAN=1;//綠球y方向步進
public static final int YELLOW_BALL_SIZE=42;//黃球尺寸
public static final int BLUE_BALL_SIZE=39;//藍球尺寸
public static final int GREEN_BALL_SIZE=35;//綠球尺寸
}
定義單個球控制的類:
package com.bn.ex12f;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
/*
* 表示單個球的類
*/
public class SingleBall {
public static final int DIRECTION_YS=0;//右上
public static final int DIRECTION_ZS=1;//左上
public static final int DIRECTION_ZX=2;//左下
public static final int DIRECTION_YX=3;//右下
int x;//球位置座標
int y;
int size;//球的尺寸
int xSpan=2;//球x方向的步進
int ySpan=2;//球y方向的步進
int direction;//球的運動方向
Bitmap bitmap;//球的位圖
public SingleBall(int x,int y,int size,int direction,Bitmap bitmap,int xSpan,intySpan)//構造器
{
this.x=x;//初始化座標位置
this.y=y;
this.size=size;//初始化球的尺寸
this.bitmap=bitmap;//初始化球的位圖
this.direction=direction;//球的運動方向
this.xSpan=xSpan;//初始化x方向的步進
this.ySpan=ySpan;//初始化y方向的步進
}
void drawSelf(Canvas canvas, Paint paint) //繪製單個球的方法
{
canvas.drawBitmap(bitmap, x,y, paint);
}
void go()//單個球運動的方法
{
int tempX,tempY;//球的目標位置
switch(direction)
{
case DIRECTION_YS://如果在向右上方運動
tempX=x+xSpan;//計算目標位置座標
tempY=y-ySpan;
if(isCollideWithRight(tempX,tempY))//到達屏幕右側
{
direction=DIRECTION_ZS;//改變運動方向爲左上
}
else if(isCollideWithUp(tempX,tempY))//到達屏幕上側
{
direction=DIRECTION_YX;//改變運動方向爲右下
}
else//如果沒有碰撞
{
x=tempX;//更新座標位置
y=tempY;
}
break;
case DIRECTION_ZS://如果在向左上方運動
tempX=x-xSpan;//計算目標位置座標
tempY=y-ySpan;
if(isCollideWithLeft(tempX,tempY))//到達屏幕左側
{
direction=DIRECTION_YS;//改變運動方向爲右上
}
else if(isCollideWithUp(tempX,tempY))//到達屏幕上側
{
direction=DIRECTION_ZX;//改變運動方向爲左下
}
else//如果沒有碰撞
{
x=tempX;//更新座標位置
y=tempY;
}
break;
case DIRECTION_ZX://如果在向左下方運動
tempX=x-xSpan;//計算目標位置座標
tempY=y+ySpan;
if(isCollideWithLeft(tempX,tempY))//到達屏幕左側
{
direction=DIRECTION_YX;//改變運動方向爲右下
}
else if(isCollideWithDown(tempX,tempY))//到達屏幕下側
{
direction=DIRECTION_ZS;//改變運動方向爲左上
}
else//如果沒有碰撞
{
x=tempX;//更新座標位置
y=tempY;
}
break;
case DIRECTION_YX://如果在向右下方運動
tempX=x+xSpan;//計算目標位置座標
tempY=y+ySpan;
if(isCollideWithRight(tempX,tempY))//到達屏幕右側
{
direction=DIRECTION_ZX;//改變運動方向爲左下
}
else if(isCollideWithDown(tempX,tempY))//到達屏幕下側
{
direction=DIRECTION_YS;//改變運動方向爲右上
}
else//如果沒有碰撞
{
x=tempX;//更新座標位置
y=tempY;
}
break;
}
}
boolean isCollideWithRight(int tempX,int tempY)//判斷是否與屏右側碰撞的方法
{
return !(tempX>0&&tempX<Constant.SCREEN_WIDTH - this.size * 1.5);
}
boolean isCollideWithUp(int tempX,int tempY)//判斷是否與屏上側碰撞的方法
{
return !(tempY>0);
}
boolean isCollideWithLeft(int tempX,int tempY)//判斷是否與屏左側碰撞的方法
{
return !(tempX>0);
}
boolean isCollideWithDown(int tempX,int tempY)//判斷是否與屏下側碰撞的方法
{
return !(tempY>0&&tempY<Constant.SCREEN_HEIGHT - this.size * 1.5);
}
}
管理所有球運動的類:
package com.bn.ex12f;
/*
* 控制所有球的類
*/
import java.util.ArrayList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public class AllBalls {
ArrayList<SingleBall> alSingleBall=new ArrayList<SingleBall>();//單個球列表
Bitmap[] ballsBitmap;//位圖數組
int[] ballsSize;//球尺寸數組
int[] ballsXSpan;//球x方向步進數組
int[] ballsYSpan;//球y方向步進數組
public AllBalls(int[] ballsSize,Bitmap[] ballsBitmap,int[] ballsXSpan,int[] ballsYSpan)//構造器
{
this.ballsSize=ballsSize;//成員變量賦值
this.ballsBitmap=ballsBitmap;//成員變量賦值
this.ballsXSpan=ballsXSpan;//成員變量賦值
this.ballsYSpan=ballsYSpan;//成員變量賦值
for(int i=0;i<ballsSize.length;i++)//循環球尺寸數組
{
int x=(int) (Math.random()*(Constant.SCREEN_WIDTH-ballsSize[i]));//隨機生成單個球的初始位置
int y=(int) (Math.random()*(Constant.SCREEN_HEIGHT-ballsSize[i]));
int direction=(int) Math.random()*4;//隨機生成單個球的運動方向
alSingleBall.add//創建單個球對象,並加入列表
(
newSingleBall(x,y,ballsSize[i],direction,ballsBitmap[i],ballsXSpan[i],ballsYSpan[i])
);
}
}
public void drawSelf(Canvas canvas, Paint paint)//繪製所有球的方法
{
for(SingleBall sb:alSingleBall)//循環單個球列表
{
sb.drawSelf(canvas, paint);//繪製單個球
}
}
public void go()//使所有球運動的方法
{
for(SingleBall sb:alSingleBall)//循環單個球列表
{
sb.go();//使單個球運動
}
}
}
最後是啓動球運動的線程類:
package com.bn.ex12f;
/*
* 控制所有球運動的線程
*/
public class BallGoThread extends Thread {
AllBalls allBalls;//聲明AllBalls的引用
public BallGoThread(AllBalls allBalls)//構造器
{
this.allBalls=allBalls;//成員變量賦值
}
boolean ballGoFlag=true;//循環標誌位
@Override
public void run()//重寫run方法
{
while(ballGoFlag)//while循環
{
allBalls.go();//調用使所有球運動的方法
try
{
Thread.sleep(Constant.MOVE_TIME);//一段時間後再運動
}
catch(Exception e)
{
e.printStackTrace();//打印異常
}
}
}
}