使用 SurfaceView 进行动画
本文介绍一种使用 Canvas、Paint 和 SurfaceView 开发动画的解决方案。
引言
实际上,您可能遇到一些类似的需求:**使列表自动滚动,中心图像会放大...** 遇到这类需求,Android 中似乎没有现成的 View 可以直接满足,您必须自定义。
通常,我认为几乎所有开发者都必须解决的最重要的一点是 View 动画。
今天,我将指导您完成一个在 Android 开发中自定义 View 的解决方案,即 SurfaceView 中的 **动画**。 我也写过一些关于它的文章,例如 半圆形列表视图 或 自动滑动列表视图,等等。
本文只是我的一些想法,可能不是最佳解决方案。但我希望我能为您提供一种继续进行 Android 自定义 View 开发的途径。
背景
许多开发者认为,SurfaceView 在 Android 中非常特殊。不,它只是一个普通的 View。不同之处在于它可以在后台线程中绘制(View 的 onDraw() 必须在 UI 线程中执行)。总结来说,执行动画的方式将执行以下两个函数:
- 更新视图的属性坐标、alpha、缩放。
- 使用更新后的属性绘制视图。
为此,请使用 Canvas 和 Paint。 我将通过一个 Bitmap 的示例来帮助您理解下面的代码:
canvas.drawBitmap(mBitmap, x, y, paint);
平移 mBitmap,更新 **x, y**
淡出 或 淡入 mBitmap,更新 alpha 值(在 paint 中)
paint.setAlpha((int) alpha);
缩放 mBitmap,更新 canvas 的 scale 属性。
canvas.scale(scaleValue, 1, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
旋转 mBitmap,您可以更新 matrix 值。请参考此处获取更多详细信息。
动画呢?
基于上述想法,如果我们能给出一个公式来更新 mBitmap 的属性,我们就可以执行动画。请考虑一个简单的示例:
假设从时间 **t1** 到时间 **t2**,视图从 **x1** 平移到 **x2**。在时间 **t** 时视图的坐标是多少?公式将是:x = x1 + t * (x2-x1) / (t2-t1)
使用代码
Animation.java
基于以上想法,我创建了一个简单的 Animation 类,用于计算当前值,具有以下属性:
mStartValue
:动画开始时的初始值。mEndValue
:动画结束时的最终值。mDuration
:从 mStartValue 更改到 mEndValue 的持续时间。mStartTime
:动画开始的时间。动画开始时,通过System.currentTimeMillis()
获取。mEndTime
:动画结束的时间。mEndTime
=mStartTime
+mDuration
。
public float getCurrentValue(long currentTime) {
float currentValue = mStartValue + (currentTime)
* (mEndValue - mStartValue) / mDuration;
if (currentTime > mEndTime) {
currentValue = mEndValue;
}
return currentValue;
}
扩展 Animation.java,我们有 AlphaAnimation、ScaleXAnimation、ScaleYAnimation、TranslateXAnimation、TranslateYAnimation。
要在 3 秒内将位图从 [x = 0] 平移到 [x = 屏幕宽度],您可以创建 TranslateXAnimation:
animation = new TranslateXAnimation();
animation.mDuration = 3000;
animation.mStartTime = System.currentTimeMillis();
animation.mEndTime = System.currentTimeMillis() + 3000;
animation.mStartValue = 0;
animation.mEndValue = SCREEN_WIDTH;
要在 3 秒内将位图从 [y = 0] 平移到 [y = 屏幕高度],您可以创建 TranslateYAnimation:
animation = new TranslateYAnimation();
animation.mDuration = 3000;
animation.mStartTime = System.currentTimeMillis();
animation.mEndTime = System.currentTimeMillis() + 3000;
animation.mStartValue = 0;
animation.mEndValue = SCREEN_HEIGHT;
要将位图从 alpha = 0 淡入到 alpha = 255,请创建 AlphaAnimation
animation = new AlphaAnimation();
animation.mDuration = 3000;
animation.mStartTime = System.currentTimeMillis();
animation.mEndTime = System.currentTimeMillis() + 3000;
animation.mStartValue = 0;
animation.mEndValue = 255;
要在 SurfaceView 中绘制位图,请根据动画类型在后台应用相应的方法。
if (animation.mType == Type.TranslateX) {
float currentX = animation.getCurrentValue(System
.currentTimeMillis() - animation.mStartTime);
canvas.drawBitmap(mBitmap, currentX, Y_DEFAULT_COORDINATE,
paint);
} else if (animation.mType == Type.TranslateY) {
float currentY = animation.getCurrentValue(System
.currentTimeMillis() - animation.mStartTime);
canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE, currentY,
paint);
} else if (animation.mType == Type.ScaleX) {
float currentY = animation.getCurrentValue(System
.currentTimeMillis() - animation.mStartTime);
// canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE, currentY,
// paint);
canvas.scale(currentY, 1, mBitmap.getWidth() / 2,
mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE,
Y_DEFAULT_COORDINATE, paint);
} else if (animation.mType == Type.ScaleY) {
float currentY = animation.getCurrentValue(System
.currentTimeMillis() - animation.mStartTime);
// canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE, currentY,
// paint);
canvas.scale(1, currentY, mBitmap.getWidth() / 2,
mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE,
Y_DEFAULT_COORDINATE, paint);
} else if (animation.mType == Type.Alpha) {
float alpha = animation.getCurrentValue(System
.currentTimeMillis() - animation.mStartTime);
// canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE, currentY,
// paint);
paint.setAlpha((int) alpha);
canvas.drawBitmap(mBitmap, X_DEFAULT_COORDINATE,
Y_DEFAULT_COORDINATE, paint);
}
历史
20130905:首次创建。