演示圖.gif
一.目的
應(yīng)項目需求需要制作一個簽到動畫,類似于翻牌.每次點擊簽到,下面的卡牌翻轉(zhuǎn)一圈.
(源碼放末尾)
需求圖
二.自定義動畫
在Android實現(xiàn)3D效果方法有Open GL ES和Camera.我使用的是Camera.
關(guān)于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; // <------- 像素密度
/**
* 創(chuàng)建一個繞y軸旋轉(zhuǎn)的3D動畫效果,旋轉(zhuǎn)過程中具有深度調(diào)節(jié)贮懈,可以指定旋轉(zhuǎn)中心南用。
* @param context <------- 添加上下文,為獲取像素密度準(zhǔn)備
* @param fromDegrees 起始時角度
* @param toDegrees 結(jié)束時角度
* @param centerX 旋轉(zhuǎn)中心x坐標(biāo)
* @param centerY 旋轉(zhuǎn)中心y坐標(biāo)
* @param depthZ 最遠到達的z軸坐標(biāo)
* @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();
// 調(diào)節(jié)深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 繞y軸旋轉(zhuǎn)
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真彩届,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //獲取數(shù)值
mValues[6] = mValues[6]/scale; //數(shù)值修正
mValues[7] = mValues[7]/scale; //數(shù)值修正
matrix.setValues(mValues); //重新賦值
// 調(diào)節(jié)中心點
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
三.使用
這部分的代碼相對簡單,主要就是為卡牌加上點擊事件,點擊時執(zhí)行分步動畫.
注意
1.第一段動畫:卡牌旋轉(zhuǎn)90度.旋轉(zhuǎn)至看不見牌面的角度時--即動畫結(jié)束時加入監(jiān)聽.此時將卡牌的畫面變換成你需要的樣子.再執(zhí)行第二段動畫
2.第二段動畫:讓卡牌從270度旋轉(zhuǎn)到360度.為什么不是90到180,是因為轉(zhuǎn)過去后卡面變鏡像了.有疑惑的話可以自己試一下.
3.取卡牌寬高需要用到post方法否則取不到.
4.isDark是我用來判斷正反面的,正面在上旋轉(zhuǎn)時設(shè)置反面,反之亦然.如果是簽到這種只需要旋轉(zhuǎn)一次的就不需要加這個參數(shù)了.
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;
// 構(gòu)建3D旋轉(zhuǎn)動畫對象,旋轉(zhuǎn)角度為0到90度
final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 0, 90, centerX, centerY,
0f, false);
// 動畫持續(xù)時間500毫秒
rotation.setDuration(500);
// 動畫完成后保持完成的狀態(tài)
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
card.startAnimation(rotation);
//監(jiān)聽器 翻轉(zhuǎn)到90度的時候 卡面圖片改變 然后將卡牌從270度翻轉(zhuǎn)到360度剛好轉(zhuǎn)回來
//這里注意不是90-180度,因為90-180翻轉(zhuǎn)過來的圖片是左右相反的鏡像圖
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度翻轉(zhuǎn)到360度
final Rotate3dAnimation rotation = new Rotate3dAnimation(MainActivity.this, 270, 360, centerX, centerY,
0f, true);
rotation.setDuration(500);
// 動畫完成后保持完成的狀態(tài)
rotation.setFillAfter(false);
card.startAnimation(rotation);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
});
}
});
}
}