美文网首页Android 精华程序员Android技术知识
Hacks动画篇-Hack4 在Canvas上显示动画

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

作者: diygreen | 来源:发表于2016-05-11 15:18 被阅读483次
    在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

    相关文章

      网友评论

      • hackware:无语,用属性动画不就得了
      • 谭冉冉:invalidate 如何和插值器关联起来呢,毕竟invalidate实现的动画很生硬
      • 飞奔的胖纸:谢谢楼主……楼主给一个初学者,有什么好的建议……

      本文标题:Hacks动画篇-Hack4 在Canvas上显示动画

      本文链接:https://www.haomeiwen.com/subject/lkzyrttx.html