2025-05-25

動態壁紙原理 及 例子

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

  一:原理

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

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

  <wallpaper  xmlns:android="http://schemas.android.com/apk/res/android" />最簡單的就是寫這一句,這樣的話打開動態壁紙就會出現隻出現一個按鈕(左圖),一般我們不這樣做,要像右圖這樣子。

 

  

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

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

  

 

如上截圖是手機上的動態壁紙列表,你也可以通過android:description=“XXX”來設置描述,通過anroid:thumbnail="XX"來設置該動態壁紙的圖片。

  2.接下來要在manifest中註冊一個service。

  <service>

    XXX

  </service>

  在這個servier中要指定你繼承WallpaperService類的路徑,指定1中設置的xml,設置廣播,設置允許權限等。比如:

  通過android:name="com.bn.ex12f.Sample12_6_WallPaper"指定繼承WallpaperService的類 ,

  通過android:permission="android.permission.BIND_WALLPAPER">允許動態壁紙權限。

  這一種還必須設置一個<intent-filter>,用來監聽Android系統發出的動態壁紙的廣播。

  還要通過<meta-data android:name="android.service.wallpaper" android:resource="@xml/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 )

 

 

這個是繼承瞭WallpaerService的類的代碼。
  View Code
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);//一定時間後繪制球
         }
     }        
  }
}

  定義常量的類:
  View Code
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;//綠球尺寸
}

  定義單個球控制的類:
  View Code
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);
    }
}

  管理所有球運動的類:
  View Code
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();//使單個球運動
        }
    }
}

  最後是啟動球運動的線程類:
  View Code
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();//打印異常
            }
        }       
    }
}

 

 

摘自  Carman

Carman
 Carman
 Carman
 Carman
  

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *