Android 動態壁紙原理 及 例子

jopen 12年前發布 | 460K 次閱讀 Android Android開發 移動開發

         最近做動態壁紙的項目,原來覺得動態壁紙是個很小的項目,但是看到Android Market上有個專門的動態壁紙分類(現在升級為Google Play了), 而且自己做的這個項目可連接上服務器,供用戶購買下載圖片,終于有了自信,認識到這個不算是個小項目了。接下來我主要談談動態壁紙的原理,然后會解釋一個 “小球的例子”,供大家能深入的理解該原理。

        一:原理

        動態壁紙為:在手機上點擊 Menu→Wallpapers→Live wallpapers→然后打開自己的程序。建個最簡單的動態壁紙的步驟如下:

        1.在rex/xml中新建一個.xml.其中注冊一個wallpaper.假設這個名字為ab.xml(下文要用到,可隨意設置,沒要求)

        最簡單的就是寫 這一句,這樣的話打開動態壁紙就會出現只出現一個按鈕(左圖),一般我們不這樣做,要像右圖這樣子。

                Android 動態壁紙原理 及 例子         Android 動態壁紙原理 及 例子

若動態壁紙"設置..."(Setting...)你想連接Activity,也在這里指定,比如:

android:settingsActivity="com.birbeck.wallpaperslideshow.SettingsActivity" (這個一般是繼承了PreferenceActivity類的Activity。就是首選項模式的類),要設置了這個屬性,就會如有圖所示了。

 

                Android 動態壁紙原理 及 例子

        如上截圖是手機上的動態壁紙列表,你也可以通過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調用之后會立即調用該方法。

        這些就是動態壁紙原理的介紹。接下來是一個小例,希望大家能夠喜歡。這個例子很簡單。效果圖如下:

        功能說明:黃 藍 綠三個小球(截圖不好,球顯示不對)。碰到屏幕邊的話會像談到地面上一樣,會返回。

        效果不錯吧,你們會了嗎? (代碼雖然都附上了,但是資源,布局都沒法附。博客園又不支持附件,還用原來的方法,想要代碼,郵箱聯系我:carman_loneliness@163.com )

                Android 動態壁紙原理 及 例子

        這個是繼承了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, int height) //重寫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,int ySpan)//構造器
    {
        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//創建單個球對象,并加入列表
            (
                    new SingleBall(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();//打印異常
            }
        }        
    }
}
轉自:http://www.cnblogs.com/carmanloneliness/archive/2012/03/10/2388500.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!