Hacks动画篇-Hack4 在Canvas上显示动画

在Canvas上显示动画

作者:李旺成

时间:2016年5月11日


这个 Hack 将介绍如何使用 Canvas 类在屏幕上绘制图形,并为其添加动画效果。

理解 Canvas

Canvas 类就是表示一块画布,你可以在上面画你想画的东西。

Canvas 是 Android 2D 绘图中的一个关键类,先看一下官方对 Canvas 的简介:

Canvas类

好吧!确实很简洁,看了之后基本上不知道何为 Canvas(反正我是这个感觉),里面就一句话有用:”For more information about how to use Canvas, read the Canvas and Drawables developer guide.“(就是查看详情)

英文阅读无障碍的同学请自行点击上面的链接跳转过去阅读,不喜欢看英文的,这里提供一段《50 Android Hacks》上对 Canvas 的介绍(从以前文档翻译来的):
”可以把 Canvas 视为 Surface 的替身或者接口,图形便是绘制在 Surface 上的。Canvas 封装了所有的绘图调用。通过 Canvas,绘制到 Surface 上的内容首先存储到与之关联的 Bitmap 中,该 Bitmap 最终会呈现到窗口上。“

简而言之,Canvas 就是画布,可以在上面画各种图形或图像。下面来看看如何使用 Canvas。

Canvas 简单使用

这里以画一个红色方块为例介绍一下 Canvas 的使用。效果如下:


Canvas 简单使用

获取 Canvas

获取 Canvas 一般有两种方式:
1、从 View 的 onDraw() 方法中获取
看一下 View onDraw() 方法的方法签名:

protected void onDraw(Canvas canvas);

自定义 View 一般会重写 onDraw() 方法,这时候 View 中的 Canvas 对象会被当做参数传递过来,可以直接操作这个 Canvas (Google 也建议使用 onDraw() 传入的 Canvas),对该 Canvas 的效果会直接反应在 View 中。

2、自行创建 Canvas 对象
Canvas 类提供了两个 public 的构造方法。可以直接调用构造方法创建对象:

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

// 创建 Canvas 方式二
Canvas c2 = new Canvas(b);

Canvas 需要以 Bitmap 为操作对象,无论哪种方式创建的 Canvas 都是需要传入自定义的 Bitmap。

当使用自己创建的 Canvas 在 bitmap 上执行完绘制操作后,可以将绘制的结果转交给另外一个 Canvas,这样就可以达到两个 Canvas 协作完成的效果,简化逻辑。(参考自:Android Canvas绘图详解(图文)

使用 Canvas 绘制方块

直接看代码:

// 创建画笔
Paint paint = new Paint();
// 设置画笔颜色
paint.setARGB(255, 255, 0, 0);
// 设置抗锯齿
paint.setAntiAlias(true);
// 创建矩形
RectF rect = new RectF(50, 50, 50, 50);
// 绘制矩形
canvas.drawRoundRect(rect,   
        100, //x轴的半径   
        100, //y轴的半径   
        paint); 

好了,准备工作完成了,下面来看看如何在 Canvas 上显示动画。

在 Canvas 上显示动画

我们想实现这样一个效果:一个红色的方块在屏幕上弹跳,当触碰到屏幕边缘的时候就会弹开。具体效果如下动图所示:

在 Canvas 上显示动画

思路分析

这里的关键是如何让”红色方块“动起来,一般可能可以想到这么个思路:启动一个线程,每隔一段时间改变方块的位置,然后刷新视图显示。

是的,用这种方式可以实现,但是有没有想过是否有更简单的方式?如果有很多需要持续运行的元素,这样做是不是会很麻烦,而且会影响性能(开启子线程挺消耗性能的)。

不知道是否听过这样一种最佳实践的提示:
不要在 onDraw() 方法中创建对象,或者使用临时对象,应该该方法会频繁调用。

好了,我的思路是:
利用 View 调用 invalidate() 方法请求重新绘制视图时会调用该视图的 onDraw() 方法,以到达循环调用的目的。在每次 onDraw() 的时候都改变”红色方块“的坐标,这样就可以实现不停的运动了。

具体实现

上面介绍了实现的思路,我们这里的具体实现和上面说的稍有不同,但是,实际上是一样的。

创建红色方块视图

这个红色方块需要提供绘制”红色方块“的实现,以及坐标改变的方法,实现代码如下(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);
    }

    ...
    
}

这里截取了关键代码,具体代码请自行下载项目代码。

绘制方块

专门提供一个类 DrawView.java 用于负责在屏幕上绘制上面创建的”方块视图“。看代码:

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 范围内运动,真正导致运动的原因是 onDraw() 方法中调用了 invalidate() 方法,该方法会请求重绘,这时 Rectangle 的坐标变化了,所以就出现了移动的效果。

显示 DrawView

你把它当作普通的自定义 View 使用即可,这里将 DrawView 做为 Activity 的内容视图:

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);
        // 这里简单简单起见为状态栏和ActionBar的高度取了个固定值 200
        mDrawView.height = display.getHeight() - 200;
        mDrawView.width = display.getWidth();

        setContentView(mDrawView);
    }
}

运行起来就可以看到红色方块运动的效果了。

小结

这个 Hack 的代码其实挺简单的,关键是理解”方块“是如何动起来的。

在 onDraw() 方法中通过调用 invalidate() 方法变换视图的位置实现自定义动画的简单方法。当然,使用这个小技巧来处理游戏的主循环也是一个不错的方案。

项目地址

AndroidHacks合集
动画篇

项目示例代码:
CanvasAnimActivity.java
Rectangle.java
DrawView.java

参考

Android Canvas绘图详解(图文)
Android——Canvas类的使用
Canvas and Drawables

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容