如何使用自訂的View來畫泡泡

如何使用自訂的View來畫泡泡

情境

如果想要使用一個自訂的View做如下圖的動畫,應該怎麼作?

程式碼說明

首先我們先宣告一個類別叫做MyView

public class MyView extends View {
    public MyView(Context context,int screenWidth,int screenHeight){
    }
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
    }
}

讓建構子傳入Context物件,以及螢幕的長寬,
並且覆寫onDraw方法,onDraw方法只要你宣告MyView物件,
它就會執行onDraw方法把這個View重新畫一遍。

接著我們初始化一些資料

public final static int DOWN = 0;
public final static int UP = 1;
public final static int LEFT = 2;
public final static int RIGHT = 3;
private final int RANGE = 20;
private int screenWidth;//手機螢幕的寬
private int screenHeight;//手機螢幕的高
private final static int RANDOM_SPEED = 7;
private final static int BUBBLE_COUNT = 70;

private int randomSpeed;//隨機移動的pixel
private int bubbleCount;//泡泡的數量
private int direction;

public MyView(Context context,int screenWidth,int screenHeight){
        super(context);
    this.screenWidth = screenWidth;
    this.screenHeight = screenHeight;

    mBitmap = ((BitmapDrawable) this.getResources().getDrawable(
            R.drawable.bubble)).getBitmap();

    bubbleCount = BUBBLE_COUNT;
    randomSpeed = RANDOM_SPEED;
    direction = RIGHT;            
    bitmapX = new int[bubbleCount];
    bitmapY = new int[bubbleCount];

    for(int i=0;i<bubbleCount;i++){
        bitmapX[i] = (int)(Math.random()*this.screenWidth+RANGE);
        bitmapY[i] = (int)(Math.random()*this.screenHeight+RANGE);
    }
}

首先我們定義四個方向,分別為上下左右,
然後還有隨機移動的像數,以及泡泡的數量,
隨機移動是指你想要讓畫面每畫一次,就移動幾個pixel,
泡泡的數量是指一個畫面有幾個泡泡,
以上這兩個是我隨便設定的一個值,你也可以自己定義你覺得ok的值。

mBitmap就是我們的主角泡泡了,我們從Drawable把它讀出來,
然後包裝成為Bitmap的物件。

最後我宣告兩個陣列,分別放置每個泡泡在螢幕的xy值,而且不能超過螢幕的大小。

private Bitmap bubbleSize(float size){
    Matrix matrix = new Matrix();
    matrix.postScale(size, size);
    Bitmap resizedBitmap = Bitmap.createBitmap(mBitmap,
            0, 0, mBitmap.getWidth(), mBitmap.getHeight(),matrix,true);
    return resizedBitmap;
}

接著我們宣告一個方法,用來改變泡泡的大小。

private Bitmap bubbleRotation(int angle){
    Matrix matrix = new Matrix();
    matrix.setRotate(angle);
    Bitmap RotateBitmap = Bitmap.createBitmap(mBitmap,
            0, 0, mBitmap.getWidth(), mBitmap.getHeight(),matrix,true);
    return RotateBitmap;
}

再來我們宣告一個方法,來改變泡泡的旋轉角度。

這裡兩個方法有一個共通點,就是利用Matrix物件來讓bitmap做大小或角度的改變。

在來自定一個方法,讓使用者設定泡泡的方向。

public void setDirection(int direction){
    this.direction = direction;
}


public void decidedDirection(){

    for(int i=0;i<bubbleCount;i++){

        if(bitmapX[i]>=-1*RANGE && bitmapX[i] <=screenWidth+RANGE &&
                bitmapY[i]>=-1*RANGE && bitmapY[i]<=screenHeight+RANGE){

            switch(direction){
                case DOWN:
                    bitmapX[i] += Math.random()*randomSpeed;
                    bitmapY[i] += Math.random()*randomSpeed;
                    break;
                case UP:
                    bitmapX[i] += Math.random()*randomSpeed;
                    bitmapY[i] -= Math.random()*randomSpeed;
                    break;
                case LEFT:
                    bitmapX[i] -= Math.random()*randomSpeed;
                    bitmapY[i] += Math.random()*randomSpeed;
                    break;
                case RIGHT:
                    bitmapX[i] -= Math.random()*randomSpeed;
                    bitmapY[i] -= Math.random()*randomSpeed;
                    break;
            }
        }
        else{
            bitmapX[i] = (int)(Math.random()*screenWidth);
            bitmapY[i] = (int)(Math.random()*screenHeight);
        }

    }
}

這個方法用來改變泡泡的方法,我們採取隨機的方式,
假設設定的方向是上的話,我們的xy就會每畫一次,就減少隨機的xy值。
同理,上下左右。
那假設超出螢幕的畫面,我們就讓它們從螢幕當中,隨機在跑出來。

@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);
    mPaint = new Paint();
    mPaint.setAntiAlias( true );
    for(int i=0;i<bubbleCount;i++){
        if(i%4 == 0){
            int angle = (int)(Math.random()*2);
            canvas.drawBitmap(bubbleRotation(angle*180), 
                          bitmapX[i], bitmapY[i], mPaint);
        }
        else if(i%4 == 1){
            canvas.drawBitmap(bubbleSize(SMALL), 
                          bitmapX[i], bitmapY[i], mPaint);
        }
        else if(i%4 == 2){
            canvas.drawBitmap(bubbleSize(BIG), 
                          bitmapX[i], bitmapY[i], mPaint);
        }
        else{
            canvas.drawBitmap(mBitmap, bitmapX[i], bitmapY[i], mPaint);
        }
    }
    decidedDirection();
    invalidate();
}

當所有的方法都設計好了,最重要的主角就登場了。
invalidate()方法會讓這個View一直重新呼叫onDraw(),這樣一來,
我們一直重畫一個畫面,累積起來就很像泡泡在移動,
這個方法裡面,我們設定隨機的四種可能,
而設定的條件是mod 4的值當成條件,

如果剛好是4的倍數,我就讓泡泡旋轉180度,
如果是4+1的倍數,我就讓泡泡變小,
如果是4+2的倍數,我就讓泡泡變大,
如果是4+3的倍數,我就讓泡泡呈現原始的狀態,

是不是很簡單? 你也可以設置自己的條件。

然後我們回到ViewDemoActivity的畫面來作設定,
一開始進入onCreate的時候,我們就呼叫剛剛自訂的View類別,
將其設定為我們當前的View,

private DisplayMetrics dm;
private MyView bubbleView;
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    bubbleView = new MyView(this,dm.widthPixels,dm.heightPixels);
    setContentView(bubbleView);
}

而且我們讓目前的View跟螢幕大小是相同的。
這樣一來你就可以看到滿滿的泡泡在移動。

再來我們想要改變方向,於是我們在畫面的長按這邊加入了四個方向的選項,

@Override
public void onCreateContextMenu(ContextMenu menu, View v, 
                                   ContextMenuInfo menuInfo) {
    menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, "UP");
    menu.add(Menu.NONE, Menu.FIRST+1, Menu.NONE, "DOWN");
    menu.add(Menu.NONE, Menu.FIRST+2, Menu.NONE, "LEFT");
    menu.add(Menu.NONE, Menu.FIRST+3, Menu.NONE, "RIGHT");
    super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
    switch(item.getItemId()){
        case Menu.FIRST:
            bubbleView.setDirection(MyView.UP);
        break;
        case Menu.FIRST+1:
            bubbleView.setDirection(MyView.DOWN);
        break;
        case Menu.FIRST+2:
            bubbleView.setDirection(MyView.LEFT);
        break;
        case Menu.FIRST+3:
            bubbleView.setDirection(MyView.RIGHT);
        break;
    }

    return super.onContextItemSelected(item);
}

記得要在onCreate裡面加入這一行喔!

registerForContextMenu(bubbleView);

如此一來, 你就可以利用長按畫面,選擇上下左右了。

github