個人覺得一篇很好的,初學動態牆紙的文章【轉】

最近做動態壁紙的項目,原來覺得動態壁紙是個很小的項目,但是看到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();//打印異常
            }
	        }       
	    }
	}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章