翻牌效果网上很多,一搜一大把,有各种实现方法,我就记录下我觉得简单效果好的方法,一个是属性动画ObjectAnimator实现,还有一个是android.graphics.Camera(不是拍照录像的Camera),我比较在意的是用Camera这个类实现的3d效果
网上的Camera例子基本都是用Rotate3dAnimation.java,我就随便拿了个进行测试,发现有bug,后来找到这篇文章安卓自定义View进阶-Matrix Camera,原来Rotate3dAnimation.java是一个演示demo,而这篇文章也把这个bug解决了,而且这篇文章里的推荐控件也很强大
我在Canvas内进行了简单的测试,看看Camera效果
Rotate3dView.java,翻转代码用的就是Rotate3dAnimation.java内的代码
public class Rotate3dView extends View {
private Bitmap frontBitmap;
private Bitmap backBitmap;
private Paint paint;
private Matrix matrix;
private Camera camera;
private float[] mValues = new float[9];
private float scale = 1;
public Rotate3dView(Context context) {
super(context);
init();
}
public Rotate3dView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
camera = new Camera();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
frontBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_jn,options);
backBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_m,options);
paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
matrix = new Matrix();
scale = getResources().getDisplayMetrics().density;
}
private int degrees = 0;
public void setDegrees(int degrees) {
this.degrees = degrees;
postInvalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(getWidth() == 0 || getHeight() == 0){
return;
}
float centerX = getWidth()/2;
float centerY = getHeight()/2;
float left = centerX - frontBitmap.getWidth()/2;
float top = centerY - frontBitmap.getHeight()/2;
canvas.save();
camera.save();
matrix.setTranslate(0,0);
if(degrees < 90){
camera.rotateY(degrees);
}else{
camera.rotateY(degrees - 180);
}
camera.getMatrix(matrix);
camera.restore();
matrix.getValues(mValues); //获取数值
mValues[6] = mValues[6]/scale; //数值修正
mValues[7] = mValues[7]/scale; //数值修正
matrix.setValues(mValues); //重新赋值
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
// matrix.postTranslate(100, 100);
canvas.setMatrix(matrix);
if(degrees < 90){
canvas.drawBitmap(frontBitmap,left ,top,null);
}else{
canvas.drawBitmap(backBitmap,left ,top,null);
}
canvas.restore();
canvas.drawRect(left,top,left+frontBitmap.getWidth(),top+frontBitmap.getHeight(),paint);
canvas.drawLine(centerX,0,centerX,getHeight(),paint);
canvas.drawLine(0,centerY,getWidth(),centerY,paint);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hyq.hm.testdraw.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#888">
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:max="180"/>
<com.hyq.hm.testdraw.Rotate3dView
android:id="@+id/rotate_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private SeekBar seekBar;
private Rotate3dView rotate3dView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rotate3dView = findViewById(R.id.rotate_view);
seekBar = findViewById(R.id.seek_bar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
rotate3dView.setDegrees(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
接下来贴翻牌动画代码,用属性动画ObjectAnimator实现的代码都注释掉了,可以把注释去掉,再把Rotate3dAnimation代码注释掉看看ObjectAnimator的效果,我觉得没有Rotate3dAnimation的好
Rotate3dAnimation.java
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;
private float scale = 1;
private int mXY;
/**
* 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
*
* @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,int xy) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
scale = context.getResources().getDisplayMetrics().density;
mXY = xy;
}
@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));
}
if(mXY == 1){
camera.rotateX(degrees);
}else{
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);
}
}
activity_rotate_3d.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="演示"
android:onClick="startAnimation"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/front_view"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true" />
<ImageView
android:id="@+id/back_view"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
android:visibility="invisible"
/>
</RelativeLayout>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Rotate3dActivity.java
public class Rotate3dActivity extends AppCompatActivity {
private ImageView[] imageViews = new ImageView[2];
private int index = 0;
// private ObjectAnimator frontAnim;
// private ObjectAnimator backAnim;
private Animation frontAnim;
private Animation backAnim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rotate_3d);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
imageViews[0] = findViewById(R.id.front_view);
imageViews[0].setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.ic_jn,options));
imageViews[1] = findViewById(R.id.back_view);
imageViews[1].setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.ic_m,options));
// frontAnim = new ObjectAnimator();
// frontAnim.setFloatValues(0,90f);
// frontAnim.setDuration(1000);
// frontAnim.setInterpolator(new LinearInterpolator());
// frontAnim.addListener(new Animator.AnimatorListener() {
// @Override
// public void onAnimationStart(Animator animation) {
//
// }
//
// @Override
// public void onAnimationEnd(Animator animation) {
// imageViews[index].setVisibility(View.INVISIBLE);
// index = (index+1)%2;
// imageViews[index].setVisibility(View.VISIBLE);
// backAnim.start();
// }
//
// @Override
// public void onAnimationCancel(Animator animation) {
//
// }
//
// @Override
// public void onAnimationRepeat(Animator animation) {
//
// }
// });
// ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {
// @Override
// public void onAnimationUpdate(ValueAnimator animation) {
// float value = (Float) animation.getAnimatedValue();
// imageViews[index].setRotationY(value);
// }
// };
// frontAnim.addUpdateListener(updateListener);
//
// backAnim = new ObjectAnimator();
// backAnim.setFloatValues(270f,360f);
// backAnim.setDuration(1000);
// backAnim.setInterpolator(new LinearInterpolator());
// backAnim.addListener(new Animator.AnimatorListener() {
// @Override
// public void onAnimationStart(Animator animation) {
// }
//
// @Override
// public void onAnimationEnd(Animator animation) {
// isAnim = false;
// }
//
// @Override
// public void onAnimationCancel(Animator animation) {
// }
//
// @Override
// public void onAnimationRepeat(Animator animation) {
// }
// });
// backAnim.addUpdateListener(updateListener);
}
private boolean isAnim = false;
public void startAnimation(View view){
if(isAnim){
return;
}
isAnim = true;
if(frontAnim == null){
final float centerX = imageViews[0].getWidth() / 2.0f;
final float centerY = imageViews[0].getHeight() / 2.0f;
frontAnim= new Rotate3dAnimation(this,0, 90, centerX, centerY, 0f, true,0);
frontAnim.setDuration(1000); //设置动画时长
frontAnim.setFillAfter(true); //保持旋转后效果
frontAnim.setInterpolator(new LinearInterpolator()); //设置插值器
backAnim = new Rotate3dAnimation(this,-90, 0, centerX, centerY, 0f, true,0);
backAnim.setDuration(1000);
backAnim.setFillAfter(true);
backAnim.setInterpolator(new LinearInterpolator());
frontAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
imageViews[index].setVisibility(View.INVISIBLE);
imageViews[index].clearAnimation();
index = (index+1)%2;
imageViews[index].setVisibility(View.VISIBLE);
imageViews[index].startAnimation(backAnim);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
backAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
isAnim = false;
imageViews[index].clearAnimation();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
imageViews[index].startAnimation(frontAnim);
// frontAnim.start();
}
}
还是要好好研究研究android.graphics.Camera