Android利用Camera实现中轴3D卡牌翻转效果

时间:2021-09-04 07:07:24

在android系统api中,有两个camera类:

  • android.graphics.camera
  • android.hardware.camera

第二个应用于手机硬件中的相机相关的操作,本文讲述的是利用第一个camera类实现中轴3d转换的卡牌翻转效果,开始之前,先看一下android系统中的坐标系:

Android利用Camera实现中轴3D卡牌翻转效果

对应于三维坐标系中的三个方向,camera提供了三种旋转方法:

  • rotatex()
  • rotatey()
  • rotatex()

调用这三种方法,传入旋转角度参数,即可实现视图沿着坐标轴旋转的功能。本文的中轴3d旋转效果就是让视图沿着y轴旋转的。

系统api demos中已经为我们提供了一个非常好用的3d旋转动画的工具类:
rotate3danimation.java:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.feng.androidtest;
 
import android.graphics.camera;
import android.graphics.matrix;
import android.util.log;
import android.view.animation.animation;
import android.view.animation.transformation;
 
/**
 * an animation that rotates the view on the y axis between two specified angles.
 * this animation also adds a translation on the z axis (depth) to improve the effect.
 */
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;
 
 /**
  * creates a new 3d rotation on the y axis. the rotation is defined by its
  * start angle and its end angle. both angles are in degrees. the rotation
  * is performed around a center point on the 2d space, definied by a pair
  * of x and y coordinates, called centerx and centery. when the animation
  * starts, a translation on the z axis (depth) is performed. the length
  * of the translation can be specified, as well as whether the translation
  * should be reversed in time.
  *
  * @param fromdegrees the start angle of the 3d rotation
  * @param todegrees the end angle of the 3d rotation
  * @param centerx the x center of the 3d rotation
  * @param centery the y center of the 3d rotation
  * @param reverse true if the translation should be reversed, false otherwise
  */
 public rotate3danimation(float fromdegrees, float todegrees,
   float centerx, float centery, float depthz, boolean reverse) {
  mfromdegrees = fromdegrees;
  mtodegrees = todegrees;
  mcenterx = centerx;
  mcentery = centery;
  mdepthz = depthz;
  mreverse = reverse;
 }
 
 @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();
 
  log.i("interpolatedtime", interpolatedtime+"");
  camera.save();
  if (mreverse) {
   camera.translate(0.0f, 0.0f, mdepthz * interpolatedtime);
  } else {
   camera.translate(0.0f, 0.0f, mdepthz * (1.0f - interpolatedtime));
  }
  camera.rotatey(degrees);
  camera.getmatrix(matrix);
  camera.restore();
 
  matrix.pretranslate(-centerx, -centery);
  matrix.posttranslate(centerx, centery);
 }
}

可以看出, rotate3danimation 总共做了两件事:在构造函数中赋值了旋转动画所需要的参数,以及重写(override)父类animation中的applytransformation()方法,下面分类阐述一下:

  • fromdegrees与todegrees
  • 视图旋转的开始角度和结束角度,当todegree处于90倍数时,视图将变得不可见。
  • centerx与centery
  • 视图旋转的中心点。
  • depthz
  • z轴移动基数,用于计算camera在z轴移动距离
  • reverse
  • boolean类型,控制z轴移动方向,达到视觉远近移动导致的视图放大缩小效果。
  • applytransformation()
  • 根据动画播放的时间 interpolatedtime (动画start到end的过程,interpolatedtime从0.0变化到1.0),让camera在z轴方向上进行相应距离的移动,实现视觉上远近移动的效果。然后调用 rotatex()方法,让视图围绕y轴进行旋转,产生3d立体旋转效果。最后再通过matrix来确定旋转的中心点的位置。

activity_main.xml布局文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="@android:color/white" >
 
 <button
  android:id="@+id/btn_open"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_margin="16dp"
  android:onclick="onclickview"
  android:text="打开"
  android:textcolor="@android:color/black"
  android:textsize="16sp" />
 
 <relativelayout
  android:id="@+id/rl_content"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_below="@id/btn_open"
  android:layout_margintop="16dp"
  android:background="@android:color/black">
 
  <imageview
   android:id="@+id/iv_logo"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:contentdescription="@null"
   android:src="@drawable/ic_qrcode"
   android:scaletype="centerinside"/>
 
  <textview
   android:id="@+id/tv_desc"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:padding="16dp"
   android:text="服务器之家。"
   android:textcolor="@android:color/white"
   android:textsize="18sp"
   android:visibility="gone"/>
 </relativelayout>
 
</relativelayout>

布局中配置了卡牌正面的图片控件,卡牌背面的文本控件,以及他们的parent容器,也就是本文中的旋转动画的执行对象。

mainactivity.java文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package com.feng.androidtest;
 
import android.app.activity;
import android.os.bundle;
import android.view.view;
import android.view.animation.accelerateinterpolator;
import android.view.animation.animation;
import android.view.animation.animation.animationlistener;
import android.view.animation.decelerateinterpolator;
import android.widget.button;
import android.widget.imageview;
import android.widget.relativelayout;
import android.widget.textview;
 
import com.example.androidtest.r;
 
public class mainactivity extends activity {
 
 private relativelayout mcontentrl;
 private imageview mlogoiv;
 private textview mdesctv;
 private button mopenbtn;
 
 private int centerx;
 private int centery;
 private int depthz = 400;
 private int duration = 600;
 private rotate3danimation openanimation;
 private rotate3danimation closeanimation;
 
 private boolean isopen = false;
 
 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
 
  mcontentrl = (relativelayout) findviewbyid(r.id.rl_content);
  mlogoiv = (imageview) findviewbyid(r.id.iv_logo);
  mdesctv = (textview) findviewbyid(r.id.tv_desc);
  mopenbtn = (button) findviewbyid(r.id.btn_open);
 
 }
 
 /**
  * 卡牌文本介绍打开效果:注意旋转角度
  */
 private void initopenanim() {
  //从0到90度,顺时针旋转视图,此时reverse参数为true,达到90度时动画结束时视图变得不可见,
  openanimation = new rotate3danimation(0, 90, centerx, centery, depthz, true);
  openanimation.setduration(duration);
  openanimation.setfillafter(true);
  openanimation.setinterpolator(new accelerateinterpolator());
  openanimation.setanimationlistener(new animationlistener() {
 
   @override
   public void onanimationstart(animation animation) {
 
   }
 
   @override
   public void onanimationrepeat(animation animation) {
 
   }
 
   @override
   public void onanimationend(animation animation) {
    mlogoiv.setvisibility(view.gone);
    mdesctv.setvisibility(view.visible);
 
    //从270到360度,顺时针旋转视图,此时reverse参数为false,达到360度动画结束时视图变得可见
    rotate3danimation rotateanimation = new rotate3danimation(270, 360, centerx, centery, depthz, false);
    rotateanimation.setduration(duration);
    rotateanimation.setfillafter(true);
    rotateanimation.setinterpolator(new decelerateinterpolator());
    mcontentrl.startanimation(rotateanimation);
   }
  });
 }
 
 /**
  * 卡牌文本介绍关闭效果:旋转角度与打开时逆行即可
  */
 private void initcloseanim() {
  closeanimation = new rotate3danimation(360, 270, centerx, centery, depthz, true);
  closeanimation.setduration(duration);
  closeanimation.setfillafter(true);
  closeanimation.setinterpolator(new accelerateinterpolator());
  closeanimation.setanimationlistener(new animationlistener() {
 
   @override
   public void onanimationstart(animation animation) {
 
   }
 
   @override
   public void onanimationrepeat(animation animation) {
 
   }
 
   @override
   public void onanimationend(animation animation) {
    mlogoiv.setvisibility(view.visible);
    mdesctv.setvisibility(view.gone);
 
    rotate3danimation rotateanimation = new rotate3danimation(90, 0, centerx, centery, depthz, false);
    rotateanimation.setduration(duration);
    rotateanimation.setfillafter(true);
    rotateanimation.setinterpolator(new decelerateinterpolator());
    mcontentrl.startanimation(rotateanimation);
   }
  });
 }
 
 public void onclickview(view v) {
  //以旋转对象的中心点为旋转中心点,这里主要不要再oncreate方法中获取,因为视图初始绘制时,获取的宽高为0
  centerx = mcontentrl.getwidth()/2;
   centery = mcontentrl.getheight()/2;
   if (openanimation == null) {
   initopenanim();
   initcloseanim();
  }
 
   //用作判断当前点击事件发生时动画是否正在执行
  if (openanimation.hasstarted() && !openanimation.hasended()) {
   return;
  }
  if (closeanimation.hasstarted() && !closeanimation.hasended()) {
   return;
  }
 
  //判断动画执行
  if (isopen) {
   mcontentrl.startanimation(closeanimation);
 
  }else {
 
   mcontentrl.startanimation(openanimation);
  }
 
  isopen = !isopen;
  mopenbtn.settext(isopen ? "关闭" : "打开");
 }
}

代码中已对核心的地方做了注释解释,主要弄清楚 rotate3danimation构造参数中的 fromdegrees和todegrees、depthz、reverse参数,同时在动画中设置了速度插播器,如动画的前半程使用加速器 accelerateinterpolator,后半程使用减速器 decelerateinterpolator,使动画体验更加人性化。

以上就是本文的全部内容,希望对大家的学习android软件编程有所帮助。