一.目的
应项目需求需要制作一个签到动画,类似于翻牌.每次点击签到,下面的卡牌翻转一圈.
(源码放末尾)
二.自定义动画
在Android实现3D效果方法有Open GL ES和Camera.我使用的是Camera.
关于canmara的解读,参考了博客 http://www.gcssloop.com/customview/matrix-3d-camera
下面是Rotate3dAnimation的代码:
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
float scale = 1; // <------- 像素密度
/**
* 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
* @param context <------- 添加上下文,为获取像素密度准备
* @param fromDegrees 起始时角度
* @param toDegrees 结束时角度
* @param centerX 旋转中心x坐标
* @param centerY 旋转中心y坐标
* @param depthZ 最远到达的z轴坐标
* @param reverse true 表示由从0到depthZ,false相反
*/
public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
// 获取手机像素密度 (即dp与px的比例)
scale = context.getResources().getDisplayMetrics().density;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
// 调节深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 绕y轴旋转
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //获取数值
mValues[6] = mValues[6]/scale; //数值修正
mValues[7] = mValues[7]/scale; //数值修正
matrix.setValues(mValues); //重新赋值
// 调节中心点
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
三.使用
这部分的代码相对简单,主要就是为卡牌加上点击事件,点击时执行分步动画.
注意
1.第一段动画:卡牌旋转90度.旋转至看不见牌面的角度时--即动画结束时加入监听.此时将卡牌的画面变换成你需要的样子.再执行第二段动画
2.第二段动画:让卡牌从270度旋转到360度.为什么不是90到180,是因为转过去后卡面变镜像了.有疑惑的话可以自己试一下.
3.取卡牌宽高需要用到post方法否则取不到.
4.isDark是我用来判断正反面的,正面在上旋转时设置反面,反之亦然.如果是签到这种只需要旋转一次的就不需要加这个参数了.
package com.maomao.technology.rotate3ddemo;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
public class MainActivity extends Activity {
boolean isDark = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initview();
}
private void initview() {
final ImageView card = findViewById(R.id.card);
card.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//用post方法取card的宽高
card.post(new Runnable() {
@Override
public void run() {
//取card中心点
final float centerX = card.getWidth() / 2f;
final float centerY = card.getHeight() / 2f;
// 构建3D旋转动画对象,旋转角度为0到90度
final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 0, 90, centerX, centerY,
0f, false);
// 动画持续时间500毫秒
rotation.setDuration(500);
// 动画完成后保持完成的状态
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
card.startAnimation(rotation);
//监听器 翻转到90度的时候 卡面图片改变 然后将卡牌从270度翻转到360度刚好转回来
//这里注意不是90-180度,因为90-180翻转过来的图片是左右相反的镜像图
rotation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//正反面判断
if (isDark) {
isDark = false;
} else {
isDark = true;
}
//点正面切换背面,反之亦然
if (isDark) {
Glide.with(MainActivity.this).load(R.drawable.light).into(card);
} else {
Glide.with(MainActivity.this).load(R.drawable.dark).into(card);
}
//270度翻转到360度
final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 270, 360, centerX, centerY,
0f, true);
rotation.setDuration(500);
// 动画完成后保持完成的状态
rotation.setFillAfter(false);
card.startAnimation(rotation);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
});
}
});
}
}
三.源码
https://github.com/liumaomao0209/Rotate3DDemo