Android自定义控件高级进阶与精彩实例
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 Camera类用法详解

在了解了如何使用Camera类以后,再来看看Camera类具有的操作Camera的函数。本节会对这些函数进行简单的讲解,而后面还会使用具体的示例进行讲解。

1.2.1 平移

平移函数如下:

img

和2D平移函数类似,只不过本节介绍的这个函数中多出了一个维度,从只能在2D平面上平移变为在3D空间内平移。

参数介绍如下。

●float x:X轴上的偏移量。

●float y:Y轴上的偏移量。

●float z:Z轴上的偏移量。

1.沿X轴平移

如果我们将1.1节示例中的旋转代码改为:

img

此时,完整的onDraw函数代码如下:

img

完整的onDraw函数代码仅在本节中列出,后续的相关代码中都将只改变camera.translate(mProgress,0,0);这行代码。

效果如图1-10所示。

可能有的读者会想,camera.translate(5,0,0)是什么意思呢?是Camera正向移动5 px,还是物体正向移动5 px呢?

其实Camera类的各个函数操作的并不是摄像机,而是物体,我们看到的是移动物体之后屏幕上图像的变化。

比如,camera.translate(5,0,0)表示的移动与拍摄过程如图1-11所示。

img

图1-10

img

扫码查看动态效果图

img

图1-11

img

扫码查看彩色图

从图1-11可以看到,在物体向右移动之后,屏幕上形成的二维图像(如黑框所示)也向右移动了。这刚好与效果图中的效果相对应。

2.沿Y轴平移

同样地,如果沿着Y轴移动,可以将示例中的移动代码改为:

img

对应的效果如图1-12所示。

img

图1-12

img

扫码查看动态效果图

原理如图1-13所示。

img

图1-13

img

扫码查看彩色图

可以看到,在物体向上移动后,屏幕上对应的二维图像也向上移动了(黄框移动到黑框位置),这就解释了为什么会出现效果图中的效果。

3.沿Z轴平移

(1)沿Z轴正方向平移

沿Z轴平移的效果比较特殊,它能实现图像的放大与缩小,我们先来看看效果。如果把示例中的移动代码改为:

img

即沿Z轴的正方向从1 px移动到360 px,效果如图1-14所示。

img

图1-14

img

扫码查看动态效果图

在图1-14中,首先,随着物体沿Z轴正方向移动距离的增大,图像逐渐变小。其次,虽然图像在变小,但图像左上角在屏幕上的位置始终不变。下面就分别讲解这两个现象的原因。

随着物体沿Z轴正方向移动距离的增大,图像逐渐变小,原理图如图1-15所示。

从图1-15可以看出,源图像在屏幕上对应的是黑框位置,在图像向Z轴正方向移动后,其在屏幕上对应的是黄框位置。随着图像沿Z轴离屏幕越来越远,图像和Camera的连线与屏幕的交点所形成的图像越来越小。这一点不难理解,就像图1-16中的铁轨。

img

图1-15

img

扫码查看彩色图

img

图1-16

我们都知道两条铁轨之间的距离是完全相同的,但随着铁轨的远离,两条铁轨之间的距离看起来越来越小。

关于第二个现象,为什么屏幕上图像的左上角位置始终不变呢?

前面曾经提到过Camera的位置在3D坐标系的(0,0,-576)处,其投影到View上位于View的左上角处,所以当图像远离Camera时,都是以左上角为原点来缩小的,如图1-17所示。

img

图1-17

img

扫码查看彩色图

(2)沿Z轴负方向平移

相反地,如果我们让物体沿Z轴负方向移动,即将示例中的移动代码改为:

img

需要注意,为了更好地演示效果,这里不仅取了负号而且乘以了2,Z轴负方向的最大移动距离变为-720,效果如图1-18所示。

img

图1-18

img

扫码查看动态图

从图1-18可以看出,随着物体向Z轴负方向移动,图像逐渐变大,但在达到一定的大小后,图像却突然不见了。

在这里读者可能会产生两个疑问:

●为什么图像会变大呢?

●为什么图像会消失呢?

首先,对于第一个问题,随着物体向Z轴负方向移动,图像逐渐变大,这是因为Camera在Z轴上位于-576 px处,所以当物体向Z轴负方向移动时,其实是距离Camera越来越近了,当然图像会越来越大。而且,当图像大到一定程度时,我们就只能看到其中一部分。同样地,由于Camera投影到View上时位于View的左上角,所以图像在变大的过程中仍然是以左上角为原点变大的。

但当物体移动到Camera位置时,就看不到物体了,这时的表现是屏幕上没有对应的图像。这一点其实也很好理解,你可以拿着一个水杯,将其由远及近地靠近你的一只眼睛,当水杯跟你的眼睛平齐时,即水杯在耳朵位置时,你就看不到水杯了。

到这里,有关3D效果中的平移就介绍完了。相对而言,3D效果中的平移理解起来困难一些,我们可以用生活中的例子来演示这个效果。比如,你把眼睛当作Camera,用一个物体来模拟View,在眼前来回移动这个物体,这样就可以辅助理解camera.translate相关参数的意义与效果。

1.2.2 旋转

旋转是3D动画的核心效果,但因为单个View是没有厚度的,所以通过它制作出来的旋转效果并不算是真正的3D效果,而是伪3D效果。但我们可以像开始介绍的StereoView控件一样,通过多个View的组合旋转来实现看起来更真实的3D效果。

img
img

旋转的函数总共有4个,其中rotate(float x,float y,float z)可以同时指定3个维度的旋转角度,但它是API 12后才加入的新函数,而其他3个函数都只能一次绕一个维度旋转,它们在API 1时就已经存在。当然,如果你需要在API 12之前同时绕多个维度旋转,可以多次调用旋转函数。

1.绕X轴旋转

将示例中的Camera操作代码改为:

img

效果如图1-19所示。

img

图1-19

img

扫码查看动态图

因为3D坐标系的原点位于View的左上角,所以,当图像绕X轴旋转时,是直接绕X轴旋转的。但是,在旋转到90°时,就看不到图像了。

首先,绕X轴的正方向旋转,如图1-20所示。

img

图1-20

img

扫码查看彩色图

图1-20展示了XYZ轴各自的旋转正方向,在后面的demo效果图中,我们也可以具体查看。

旋转过程中的示意图如图1-21所示。

img

图1-21

img

扫码查看彩色图

图1-21显示了旋转前和旋转后屏幕上图像的示意图。原始图像是黑框,旋转后的图像是黄框。大家也可以拿一张纸在摄像机前旋转并模拟这个过程,以更好地理解旋转功能。

因为Camera的位置在(0,0,-576)处,也就是说Camera在3D坐标系Z轴上的-576 px处,当图像旋转到90°时,Camera与图像在一个平面上,这时屏幕上的效果就是在图像旋转90°以后(大于90°且小于270°)什么也看不到了。

2.绕Y轴旋转

将demo的代码改为:

img

效果如图1-22所示。

img

图1-22

img

扫码查看动态效果图

从图1-22可以看出,图像默认是绕Y轴旋转的,在旋转90°以后(大于90°且小于270°),Camera与图像在一个平面上,图像与屏幕的View区域没有交集,所以不显示任何内容。

3.绕Z轴旋转

将demo的代码改为:

img

效果如图1-23所示。

img

图1-23

img

扫码查看动态效果图

同样地,以坐标系原点为中心绕Z轴旋转。在图像旋转90°以后(大于90°且小于270°),图像就与View没有交集了,所以屏幕上没有任何图像,即屏幕上不显示任何内容。

4.调整旋转中心点

从上面的效果可以看出,默认都是围绕XYZ轴旋转的,有时我们想实现翻转卡片的效果,扫码查看右侧效果图。

img

扫码查看动态效果图

在这个示例中,很明显,旋转中心点变为了图像中心点。那么怎么调整旋转中心点呢?

如果我们想将默认的3D坐标系中心点调整到(xy)位置,只需要在获取矩阵后,通过下面的代码将中心点位置调整到(xy)位置:

img

有关这段代码,我们会放在第2章讲解位置矩阵时详细说明,在这里,大家只要会用就行。但需要特别注意的一点是,这里是通过后期改变矩阵的值来改变旋转中心点的,并没有改变原始Camera的位置,Camera在View所在的2D坐标系中的投影位置依然在View的左上角(0,0)处。

比如,我们修改一下demo中的代码,将旋转中心点改为图像中心点:

img
img

这段代码比较好理解,只是在操作了Camera以后,通过camera.getMatrix(matrix)获取对应操作的matrix数组,然后通过调整旋转中心点的代码,将旋转中心点调整到图像中心点。

调整中心点后绕X轴旋转的效果如图1-24所示。

img

图1-24

img

扫码查看动态效果图

同样地,调整旋转中心点到图像中心点后,绕Y轴旋转的效果如图1-25所示。

img

图1-25

img

扫码查看动态效果图

最后,再来看看在调整旋转中心点到图像中心点后,绕Z轴旋转的效果如图1-26所示。

img

图1-26

img

扫码查看动态效果图

1.2.3 改变Camera的位置

在API≥15时,Camera类新增了几个函数来获取和改变Camera的位置:

img

1.Camera的坐标单位

这里有一个非常奇怪的问题,那就是Camera的坐标单位不是px,而是其每个单位表示72 px。

这个单位的表示函数可以在Android底层的图像引擎Skia中找到。在Skia中,Camera的位置单位是英寸,1英寸可换算为72 px,而在Android中把这个换算函数照搬了过来。这个换算函数是固定的,不会随着手机分辨率的改变而变化。

比如,我们在onDraw中通过日志获取Camera默认的位置信息,代码如下:

img

日志如图1-27所示。

img

图1-27

可以看到,在默认的情况下,Camera的位置在View左上角外部,并且位于Z轴的-576 px位置(-8×72=-576)。

2.更改Camera的位置

我们可以通过void setLocation(float x,float y,float z)来设置Camera的位置。但需要注意的是,设置位置时,一个单位相当于72 px,所以这里的参数值需要做单位变换。

假如我们将Camera的位置从默认的左上角外部移到图像中心点外部,代码如下:

img
img

从上面的代码可以看出,我们通过getWidth()/2得到了中心点位置的坐标,然后除以72换算成英寸单位。需要注意的是,Y轴的正方向是沿着屏幕向上的,所以,我们若要让Camera向下移动,需要将移动距离设置为-centerY。

效果如图1-28所示。

img

图1-28

img

扫码查看动态效果图

这里有两点需要注意:

(1)当将Camera移到图像中心点外部以后,图像只显示右下角的四分之一,这是为什么呢?

(2)当将Camera移到图像中心点外部以后,图像的旋转中心仍在图像的左上角。Camera的位置可以通过setLocation函数来改变,而如果我们想要改变3D坐标系的位置,则需要通过Matrix操作来实现。

下面的示意图展示了Camera从图像的左上角外部移到图像中心点外部的过程,如图1-29所示。

img

图1-29

img

扫码查看彩色图

在图1-29中,红色框表示当Camera在图像左上角外部时显示的图像范围,此时,整个图像都可以得到显示;绿色框表示在Camera移到图像中心点外部以后,显示范围随着Camera移动的情况。从这个示意图可以看出,在Camera移到图像中心点外部以后,只能显示右下角部分的图像,与代码运行效果相同。

Camera的移动与图像显示理解起来相对有些难度,大家可以把A4纸当作View,把手机摄像头当作Camera,通过移动手机摄像头来查看手机屏幕中显示内容的变化情况,两者的原理完全相同。