前言
Android自带的补间动画只有alpha、rotate、translate和scale这四种效果,在真正开发过程中根本无法满足一些特殊需求。这里来使用补间动画实现一下简单的抛物线效果和3D旋转动画。
实现效果
抛物线实现
抛物线的原理很简单,x轴表示横向移动的偏移量,y轴表示竖向的偏移量,如果dy=dx*dx也就是y轴方向的移动偏移量是x轴方向偏移量的平方值,这个时候对象在轨迹上就会表现出一个抛物线。
public class ParabolaAnimation extends Animation {
private static final String TAG = "ParabolaAnimation";
// 解析得到需要移动到的位置
private float mToPosX;
private float mToPosY;
// 用户赋值的位置
private float mToPosXValue;
private float mToPosYValue;
// 用户赋值位置的类型,可以使绝对、相对自己、相对父布局
private int mPosType;
// y = a * x * x,抛物线的常量系数
private float mParamA;
public ParabolaAnimation(float toPosX, float toPosY, int posType) {
mToPosXValue = toPosX;
mToPosYValue = toPosY;
mPosType = posType;
}
// 当开始播放补间动画的时候第一次会做初始化操作,
// 主要是把当前动画View和它的父布局宽高设置进来
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
// 如果是相对与父控件mToPosXValue是小数,需要乘以父控件高度
if (mPosType == Animation.RELATIVE_TO_PARENT) {
mToPosX = mToPosXValue * parentWidth;
mToPosY = mToPosYValue * parentHeight;
}
// 计算出抛物线的常数系数
mParamA = (mToPosY) / (mToPosX * mToPosX);
Log.d(TAG, "initialize: mToPosX = " + mToPosX + ", mToPosY = " + mToPosY + ", mParamA = " + mParamA);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// 根据动画流逝的时间计算X轴方向偏移量
// 同时根据x轴偏移量计算y轴方向偏移量
float posX = mToPosX * interpolatedTime;
float posY = mParamA * posX * posX;
Log.d(TAG, "posX = " + posX + ", posY = " + posY);
// 将偏移量设置到转换矩阵中,这样onDraw方法里
// Canvas绘制当前View会做偏移动画
t.getMatrix().setTranslate(posX, posY);
}
}
实际上了解了补间动画实现的原理上面的自定义补间动画就很容易理解了,不熟悉补间动画实现源码的可以参考补间动画的源码解析。调用方法和内置的动画非常相似:
parabolaAnimation = new ParabolaAnimation(0.5f, 0.8f, Animation.RELATIVE_TO_PARENT);
parabolaAnimation.setDuration(2000);
ball.startAnimation(parabolaAnimation);
3D旋转实现
在图形变换实现里只要使用一个3*3的矩阵就能够实现图像的各种旋转缩放和移动操作,但是矩阵值的计算非常的麻烦,对于非专业的图像处理开发者来说还是有一定的难度的。好在Android系统为开发者提供了一个Camera工具对象,它能够轻松的计算各种旋转操作需要的矩阵,主要这个是android.graphics.Camera工具类而不是拍照的那个相机工具类。
Camera的主要API有:
接口 | 说明 |
---|---|
rotateX(float degree) | 围绕X轴旋转 |
rotateY(float degree) | 围绕Y轴旋转 |
rotateZ(float degree) | 围绕Z轴旋转 |
translate(x, y, z) | 平移 |
save(),restore() | 与Canvas相同,都是保存原先状态和恢复原先状态 |
getMatix(matrix) | 将内部的Matrix的值复制到matrix(注意必须在restore之前) |
现在来简单使用rotateX和rotateY实现3D横向旋转和竖向旋转效果。
public class RotateXAnimation extends Animation {
// Camera工具对象
private Camera mCamera;
// 开始的角度
private float mFromDegrees;
// 结束的角度
private float mToDegrees;
// 定义旋转的中心轴位置
private int mPivotXType;
private int mPivotYType;
private float mPivotXValue;
private float mPivotYValue;
private float mPivotX;
private float mPivotY;
public RotateXAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mPivotXValue = pivotXValue;
mPivotXType = pivotXType;
mPivotYValue = pivotYValue;
mPivotYType = pivotYType;
mCamera = new Camera();
}
// 初始化操作,计算各种情况下的旋转轴位置
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
switch (mPivotXType) {
case Animation.ABSOLUTE:
mPivotX = mPivotXValue;
break;
case Animation.RELATIVE_TO_PARENT:
mPivotX = mPivotXValue * parentWidth;
break;
case Animation.RELATIVE_TO_SELF:
mPivotX = mPivotXValue * width;
break;
}
switch (mPivotYType) {
case Animation.ABSOLUTE:
mPivotY = mPivotYValue;
break;
case Animation.RELATIVE_TO_PARENT:
mPivotY = mPivotYValue * parentHeight;
break;
case Animation.RELATIVE_TO_SELF:
mPivotY = mPivotYValue * height;
break;
}
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// 根据动画播放时间确认目前旋转到的角度
float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
float scale = getScaleFactor();
mCamera.save();
// 如果旋转轴是左上角,直接旋转,
if (mPivotX == 0.0f && mPivotY == 0.0f) {
mCamera.rotateX(degrees);
// 然后将Camera计算得到的matrix值填充到Transformation的matrix里
mCamera.getMatrix(t.getMatrix());
} else {
// 有旋转中心,需要先移动到旋转中心,再旋转
// 然后再将旋转中心移回以前的位置
mCamera.translate(-mPivotX * scale, -mPivotY * scale, 0);
mCamera.rotateX(degrees);
mCamera.translate(mPivotX * scale, mPivotY * scale, 0);
// 然后将Camera计算得到的matrix值填充到Transformation的matrix里
mCamera.getMatrix(t.getMatrix());
}
mCamera.restore();
}
}
和普通的补间动画调用方法类似:
rotateXAnimation = new RotateXAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateXAnimation.setDuration(2000);
image.startAnimation(rotateXAnimation);
button.startAnimation(rotateXAnimation);
上面定义了延X轴的旋转,其实修改一下applyTransform里的matrix计算方法就可以实现y轴的旋转。
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
float scale = getScaleFactor();
mCamera.save();
if (mPivotX == 0.0f && mPivotY == 0.0f) {
mCamera.rotateY(degrees);
mCamera.getMatrix(t.getMatrix());
} else {
mCamera.translate(mPivotX * scale, mPivotY * scale, 0);
mCamera.rotateY(degrees);
mCamera.translate(-mPivotX * scale, -mPivotY * scale, 0);
mCamera.getMatrix(t.getMatrix());
}
mCamera.restore();
}