Hacks動畫篇-Hack4 在Canvas上顯示動畫

在Canvas上顯示動畫

作者:李旺成

時間:2016年5月11日


這個 Hack 將介紹如何使用 Canvas 類在屏幕上繪制圖形,并為其添加動畫效果。

理解 Canvas

Canvas 類就是表示一塊畫布,你可以在上面畫你想畫的東西。

Canvas 是 Android 2D 繪圖中的一個關(guān)鍵類,先看一下官方對 Canvas 的簡介:

Canvas類

好吧!確實很簡潔,看了之后基本上不知道何為 Canvas(反正我是這個感覺),里面就一句話有用:”For more information about how to use Canvas, read the Canvas and Drawables developer guide.“(就是查看詳情)

英文閱讀無障礙的同學(xué)請自行點擊上面的鏈接跳轉(zhuǎn)過去閱讀,不喜歡看英文的,這里提供一段《50 Android Hacks》上對 Canvas 的介紹(從以前文檔翻譯來的):
”可以把 Canvas 視為 Surface 的替身或者接口,圖形便是繪制在 Surface 上的。Canvas 封裝了所有的繪圖調(diào)用。通過 Canvas,繪制到 Surface 上的內(nèi)容首先存儲到與之關(guān)聯(lián)的 Bitmap 中,該 Bitmap 最終會呈現(xiàn)到窗口上。“

簡而言之,Canvas 就是畫布,可以在上面畫各種圖形或圖像。下面來看看如何使用 Canvas。

Canvas 簡單使用

這里以畫一個紅色方塊為例介紹一下 Canvas 的使用。效果如下:


Canvas 簡單使用

獲取 Canvas

獲取 Canvas 一般有兩種方式:
1、從 View 的 onDraw() 方法中獲取
看一下 View onDraw() 方法的方法簽名:

protected void onDraw(Canvas canvas);

自定義 View 一般會重寫 onDraw() 方法,這時候 View 中的 Canvas 對象會被當(dāng)做參數(shù)傳遞過來,可以直接操作這個 Canvas (Google 也建議使用 onDraw() 傳入的 Canvas),對該 Canvas 的效果會直接反應(yīng)在 View 中。

2、自行創(chuàng)建 Canvas 對象
Canvas 類提供了兩個 public 的構(gòu)造方法。可以直接調(diào)用構(gòu)造方法創(chuàng)建對象:

Bitmap b = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
// 創(chuàng)建 Canvas 方式一
Canvas c1 = new Canvas();
c1.setBitmap(b);

// 創(chuàng)建 Canvas 方式二
Canvas c2 = new Canvas(b);

Canvas 需要以 Bitmap 為操作對象,無論哪種方式創(chuàng)建的 Canvas 都是需要傳入自定義的 Bitmap。

當(dāng)使用自己創(chuàng)建的 Canvas 在 bitmap 上執(zhí)行完繪制操作后,可以將繪制的結(jié)果轉(zhuǎn)交給另外一個 Canvas,這樣就可以達(dá)到兩個 Canvas 協(xié)作完成的效果,簡化邏輯。(參考自:Android Canvas繪圖詳解(圖文)

使用 Canvas 繪制方塊

直接看代碼:

// 創(chuàng)建畫筆
Paint paint = new Paint();
// 設(shè)置畫筆顏色
paint.setARGB(255, 255, 0, 0);
// 設(shè)置抗鋸齒
paint.setAntiAlias(true);
// 創(chuàng)建矩形
RectF rect = new RectF(50, 50, 50, 50);
// 繪制矩形
canvas.drawRoundRect(rect,   
        100, //x軸的半徑   
        100, //y軸的半徑   
        paint); 

好了,準(zhǔn)備工作完成了,下面來看看如何在 Canvas 上顯示動畫。

在 Canvas 上顯示動畫

我們想實現(xiàn)這樣一個效果:一個紅色的方塊在屏幕上彈跳,當(dāng)觸碰到屏幕邊緣的時候就會彈開。具體效果如下動圖所示:

在 Canvas 上顯示動畫

思路分析

這里的關(guān)鍵是如何讓”紅色方塊“動起來,一般可能可以想到這么個思路:啟動一個線程,每隔一段時間改變方塊的位置,然后刷新視圖顯示。

是的,用這種方式可以實現(xiàn),但是有沒有想過是否有更簡單的方式?如果有很多需要持續(xù)運行的元素,這樣做是不是會很麻煩,而且會影響性能(開啟子線程挺消耗性能的)。

不知道是否聽過這樣一種最佳實踐的提示:
不要在 onDraw() 方法中創(chuàng)建對象,或者使用臨時對象,應(yīng)該該方法會頻繁調(diào)用。

好了,我的思路是:
利用 View 調(diào)用 invalidate() 方法請求重新繪制視圖時會調(diào)用該視圖的 onDraw() 方法,以到達(dá)循環(huán)調(diào)用的目的。在每次 onDraw() 的時候都改變”紅色方塊“的坐標(biāo),這樣就可以實現(xiàn)不停的運動了。

具體實現(xiàn)

上面介紹了實現(xiàn)的思路,我們這里的具體實現(xiàn)和上面說的稍有不同,但是,實際上是一樣的。

創(chuàng)建紅色方塊視圖

這個紅色方塊需要提供繪制”紅色方塊“的實現(xiàn),以及坐標(biāo)改變的方法,實現(xiàn)代碼如下(Rectangle.java):

public class Rectangle extends View {

    ...
    
    public void move() {
        moveTo(mSpeedX, mSpeedY);
    }

    // 真正的移動方法
    private void moveTo(int goX, int goY) {
        // check the borders, and set the direction if a border has reached
        if (mCoordX > (mDrawView.width - MAX_SIZE)) {
            goRight = false;
        }

        if (mCoordX < 0) {
            goRight = true;
        }

        if (mCoordY > (mDrawView.height - MAX_SIZE)) {
            goDown = false;
        }
        if (mCoordY < 0) {
            goDown = true;
        }

        // move the x and y
        if (goRight) {
            mCoordX += goX;
        } else {
            mCoordX -= goX;
        }
        if (goDown) {
            mCoordY += goY;
        } else {
            mCoordY -= goY;
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mDrawRect.set(mCoordX, mCoordY, mCoordX + mRealSize, mCoordY
                + mRealSize);
        canvas.drawRoundRect(mDrawRect, 0, 0, mInnerPaint);
    }

    ...
    
}

這里截取了關(guān)鍵代碼,具體代碼請自行下載項目代碼。

繪制方塊

專門提供一個類 DrawView.java 用于負(fù)責(zé)在屏幕上繪制上面創(chuàng)建的”方塊視圖“??创a:

public class DrawView extends View {
    private Rectangle mRectangle;
    public int width;
    public int height;

    public DrawView(Context context) {
        super(context);

        mRectangle = new Rectangle(context, this);
        mRectangle.setARGB(255, 255, 0, 0);
        mRectangle.setSpeedX(3);
        mRectangle.setSpeedY(3);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        invalidate();

        mRectangle.move();
        mRectangle.onDraw(canvas);
    }

}

Rectangle 將在 DrawView 的 width 和 height 范圍內(nèi)運動,真正導(dǎo)致運動的原因是 onDraw() 方法中調(diào)用了 invalidate() 方法,該方法會請求重繪,這時 Rectangle 的坐標(biāo)變化了,所以就出現(xiàn)了移動的效果。

顯示 DrawView

你把它當(dāng)作普通的自定義 View 使用即可,這里將 DrawView 做為 Activity 的內(nèi)容視圖:

public class CanvasAnimActivity extends AppCompatActivity {

    private DrawView mDrawView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 獲取屏幕尺寸
        Display display = getWindowManager().getDefaultDisplay();
        mDrawView = new DrawView(this);
        // 這里簡單簡單起見為狀態(tài)欄和ActionBar的高度取了個固定值 200
        mDrawView.height = display.getHeight() - 200;
        mDrawView.width = display.getWidth();

        setContentView(mDrawView);
    }
}

運行起來就可以看到紅色方塊運動的效果了。

小結(jié)

這個 Hack 的代碼其實挺簡單的,關(guān)鍵是理解”方塊“是如何動起來的。

在 onDraw() 方法中通過調(diào)用 invalidate() 方法變換視圖的位置實現(xiàn)自定義動畫的簡單方法。當(dāng)然,使用這個小技巧來處理游戲的主循環(huán)也是一個不錯的方案。

項目地址

AndroidHacks合集
動畫篇

項目示例代碼:
CanvasAnimActivity.java
Rectangle.java
DrawView.java

參考

Android Canvas繪圖詳解(圖文)
Android——Canvas類的使用
Canvas and Drawables

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容