Android view利用canvas绘制动画(一)

写在前面

因为在工作中遇到了些问题,在网上简单查询了下没有类似的博客。于是,就决定自己试着写一篇文章来分享一下我是如何利用Canvas来给View绘制动画,希望大家能共同进步。

概念相关

主要利用自定义View和Canvas两个方面知识,简要原理就是通过重写View中的onDraw函数通过对应的Canvas变换来实现自己想要的动画效果。

利用自定义View绘制一个图形

新建一个继承View的AnimView用于绘制

通过重写onDraw函数绘制出一个图形

private BitmapDrawable mDrawable;

    private int mViewWidth;
    private int mViewHeight;

    public AnimView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mViewWidth = w;
        mViewHeight = h;
        init();
    }

    private void init() {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        mDrawable = new BitmapDrawable(bitmap);
        mDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {
            canvas.translate(mViewWidth / 2, mViewHeight / 2);
            mDrawable.draw(canvas);
    }

移动该图形

如果需要将图形向右移动100px, 我们可以对Canvas直接做变换,大家可以自行了解Canvas的简单用法

    canvas.translate(mViewWidth / 2 + 100, mViewHeight / 2);
    mDrawable.draw(canvas);

将其变成完整的移动动画

小伙伴们有意见了,说道这不是坑爹吗,这样移动哪里是动画啊!
其实,Android动画的本质就是通过对Canvas的各种变换实现的, 例如移动动画(TranslateAnimation)本质就是在onDraw中通过Canvas的translate方法,不断对其移动的。那么我们只需要在一段连续时间内对Canvas的translate值做修改,就能实现一个连贯的动画效果。
1.那么现在创建一个float类型的值,使其从0匀速增大至1,用于模拟整个动画的进度。

// 初始化时间值
        if (mStartTime == -1) {
            mStartTime = SystemClock.uptimeMillis();
        }
        long curTime = SystemClock.uptimeMillis();
        // t为一个0到1均匀变化的值
        float t = (curTime - mStartTime - mStartOffset) / (float) mDuration;
        t = Math.max(0, Math.min(t, 1));

2.如果需要对t加入插值器(Interpolator)可以通过Interpolator#getInterpolation(input) 返回一个对应的t值。

3.通过t值和动画初始位置,计算出当前t值下,该图的位置,计算方法如下

// 数制差
    float lerp(float start, float end, float t) {
        return start + (end - start) * t;
    }

当t小于1时说明,通过自行调用invalidate()继续绘制。否则停止绘制
最后附上完整的代码,

private BitmapDrawable mDrawable;

    private int mViewWidth;
    private int mViewHeight;
    private long mStartX;
    private long mEndX;
    private long mStartY;
    private long mEndY;

    public AnimView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mViewWidth = w;
        mViewHeight = h;
        init();
    }

    private void init() {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        mDrawable = new BitmapDrawable(bitmap);
        mDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());

        mStartX = mViewWidth / 2;
        // 右移200px 
        mEndX = mStartX + 200;
        mStartY = mViewHeight / 2;
        mEndY = mStartY;
    }

    private long mStartTime = -1;
    private long mStartOffset = 1000;
    private long mDuration = 2000;

    @Override
    protected void onDraw(Canvas canvas) {
        // 初始化时间值
        if (mStartTime == -1) {
            mStartTime = SystemClock.uptimeMillis();
        }
        long curTime = SystemClock.uptimeMillis();
        boolean done = true;

        // t为一个0到1均匀变化的值
        float t = (curTime - mStartTime - mStartOffset) / (float) mDuration;
        t = Math.max(0, Math.min(t, 1));
        int translateX = (int) lerp(mStartX, mEndX, t);
        int translateY = (int) lerp(mStartY, mEndY, t);
        if (t < 1) {
            done = false;
        }
        if (0 < t && t <= 1) {
            done = false;
            // 保存画布,方便下次绘制
            canvas.save();
            canvas.translate(translateX, translateY);
            mDrawable.draw(canvas);
            canvas.restore();
        }

        if (!done) {
            invalidate();
        }
    }

    // 数制差
    float lerp(float start, float end, float t) {
        return start + (end - start) * t;
    }

通过上述代码即可绘制出简单的位移动画

写在后面

对应的,可以通过mDrawable.setAlpha()来实现渐变,canvas.rotate()实现旋转,canvas.scale()实现缩放动画等等。
当然,小伙伴们会问这有什么用,不如用现成的Api去实现上述动画。具体作用下一章告诉大家。

你可能感兴趣的:(Android)