因为在工作中遇到了些问题,在网上简单查询了下没有类似的博客。于是,就决定自己试着写一篇文章来分享一下我是如何利用Canvas来给View绘制动画,希望大家能共同进步。
主要利用自定义View和Canvas两个方面知识,简要原理就是通过重写View中的onDraw函数通过对应的Canvas变换来实现自己想要的动画效果。
通过重写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去实现上述动画。具体作用下一章告诉大家。