最近在复习自定义view相关的知识点,也不是说为了面试,就是懒,这个简单一点。既然都没达到深入浅出的目的,那就看书。 《Android自定义控件高级进阶与精彩实例》 这本书挺好的,启航大佬两本书都看,别的不说,起码自定义view相关的基础知识还是补了上来。这次主要还是将这本书上第一章《3D特效》上的知识点进行汇总共享。
emmmm,还是建议看看看原文。
正文
说到3D特效,我记得昨年到处都在是3D特效的实现blog。有一丢丢的自定义view基础的我,嗯,看到脑阔大,毕竟自己矩阵学得差,自己算Matrix,感觉可以要了我的老命。但是现在不一样了,从书中看到了一个类: android.graphics.Camera
。既然3D都做的出来,那么小小的viewGroup转场动画不是信手拈来? OK,那么直接开整。
3D和2D的区别
这个概念逻辑尤为的重要,可以大致的精炼为以下几点:
- 手机屏幕就是2D,左上角到右上角为X+,左上角到左下角为Y+。
- 3D坐标系中,左上角到右上角为X+,y轴则是沿着屏幕向上,Z轴则是垂直屏幕向里。这里偷一张图:

- 3D坐标系分为,左手坐标系和右手坐标系。计算机通常用左右坐标系,
默认说的也是左手坐标系
。数学中右手坐标系用得较多。

手背向上用左手比左手坐标系的手势,就是屏幕上的3D坐标。
3D坐标系与2D坐标系的转换关系
这个贼复杂,我们能做的就是在2D屏幕上做出看起来是3D的效果,所以在屏幕上的核心还是基于Matrix,所以记住matrix即可。那么我们除了手动计算matrix,有没有工具类呢? 答案就是: android.graphics.Camera
。
android.graphics.Camera 介绍
还是上官网地址:google官网Camera 这个类的介绍相当直白:
可用于计算 3D 变换并生成可应用于
Canvas
既然3D是人眼的观看感受,那么这个动效就分为两个类别
- 基于某个方向平移,就是靠近某物,或者远离某物,对应的函数是:
translate(float x, float y, float z)
- 基于某个方向平移视角,就是旋转脑袋。对应的函数:
rotate(float x, float y, float z)
或者rotateX(float deg)
、rotateY(float deg)
、rotateZ(float deg)
等。
当然还有其他函数,比如说设置视角的开始位置:setLocation(float x, float y, float z)
。OK,道理都懂了,我们还是看一下官方提供的函数:
Public methods | |
---|---|
void |
applyToCanvas(Canvas canvas) Computes the matrix corresponding to the current transformation and applies it to the specified Canvas. |
float |
dotWithNormal(float dx, float dy, float dz) |
float |
getLocationX() Gets the x location of the camera. |
float |
getLocationY() Gets the y location of the camera. |
float |
getLocationZ() Gets the z location of the camera. |
void |
getMatrix(Matrix matrix) Computes the matrix corresponding to the current transformation and copies it to the supplied matrix object. |
void |
restore() Restores the saved state, if any. |
void |
rotate(float x, float y, float z) Applies a rotation transform around all three axis. |
void |
rotateX(float deg) Applies a rotation transform around the X axis. |
void |
rotateY(float deg) Applies a rotation transform around the Y axis. |
void |
rotateZ(float deg) Applies a rotation transform around the Z axis. |
void |
save() Saves the camera state. |
void |
setLocation(float x, float y, float z) Sets the location of the camera. |
void |
translate(float x, float y, float z) Applies a translation transform on all three axis. |
location(x,y,z)
当我们创建camera对象后,直接获取location,可以发现x=0,y=0,z=-8。那么应该怎么理解这个location呢?通俗的讲,这个就是3D坐标,这个坐标代表什么意思呢?
通俗的讲就是物品不动,人在不停的换位置去观看这个物体。
- 当z 的值约小,结合手背朝上的左手3D坐标系,说明这个屏幕离我们越近,所以说这个可以看作是基于某个点的方法。等于0的时候视角重合就看不到东西。所以无限趋近于0的且为负数的时候,图片最小。
- 当z值约大,结合手背朝上的左手3D坐标系,说明屏幕离我们越来越远,所以无限趋近于0 但是为正的时候,图片最大。
- x的值结合z轴的值,就可以概括为X加的时候就是基于视角点往左移动,X减的时候就是往右移动。
- Y的值同理。Y加的时候向屏幕下面移动,Y减的时候,向屏幕上面移动。
translate(x,y,z)
直接翻译出来就是平移的意思。这个和location有一些不同。这个感觉像是视角的移动,但是人是不移动的。比如说,频繁调用camera.translate(0f, 0f, 1f)
结合上面的知识,这个看起来就是缩小,反之 camera.translate(0f, 0f, -1f)
就是放大,x于y的变动也是类似逻辑。但是这个函数有一个特点,就是获取location获取下来的x、y、z是没变动的。
rotate(x,y,z)
这个直接翻译为旋转。所以这个逻辑上location获取下来的x、y、z是不会变动的。
- 比如说不停的设置
camera.rotateX(1f)
就是基于X轴不动,感觉物体的往屏幕上翻,那么camera.rotateX(-1f)
就是物品往屏幕下翻。总结就是:基于X轴,参数为正,往上翻,参数为负往下翻
- 结合X的经验。
camera.rotateY(1f)
就是基于Y轴,物品往屏幕里面翻,参数为正就是基于Y轴往外翻。总结就是:基于Y轴,参数为正往里面翻,参数为负往外面翻
。 - 那么Z轴也应该是;基于Z轴进行反转,参数为正往上翻,参数为负往下翻。
调试代码的实现
道理都懂,我们还是需要看一下是否是自己分析的结果。 回归初心,我们主要是基于camera 获取一个Matrix,而camera 提供Matrix 主要是两个函数:
applyToCanvas(Canvas canvas)
把canvas 传递进去,会在native层,把值赋值上去。getMatrix(Matrix matrix)
传递一个matrix进去。然后把值给matrix 对象。最后,我们需要将matrix 赋值给canvas。
那么我们基于AppCompatImageView 整一个自定view。然后通过canvas 绘制一张bitmap。核心代码:
scss
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
camera?.let {
it.save()
it.applyToCanvas(canvas)
it.restore()
}
canvas.drawBitmap(bitmap, rect, rect, paint)
canvas.restore()
}
所以调试代码运行出来:

浮动到上面的是我们获取到的matrix 绘制的bitmap。 完整代码
总结
全篇涉及到的知识点还是有的,只是说有些和2D图像逻辑有点差异。下面罗列下知识点:
-
手机屏幕就是2D,左上角到右上角为X+,左上角到左下角为Y+。
-
3D坐标系中,左上角到右上角为X+,y轴则是沿着屏幕向上,Z轴则是垂直屏幕向里。
-
手背向上用左手比左手坐标系的手势,就是屏幕上的3D坐标。
-
location 里面:
- 当z 的值约小,结合手背朝上的左手3D坐标系,说明这个屏幕离我们越近,所以说这个可以看作是基于某个点的方法。等于0的时候视角重合就看不到东西。所以无限趋近于0的且为负数的时候,图片最小。
- 当z值约大,结合手背朝上的左手3D坐标系,说明屏幕离我们越来越远,所以无限趋近于0 但是为正的时候,图片最大。
- x的值结合z轴的值,就可以概括为X加的时候就是基于视角点往左移动,X减的时候就是往右移动。
- Y的值同理。Y加的时候向屏幕下面移动,Y减的时候,向屏幕上面移动。
-
频繁调用
camera.translate(0f, 0f, 1f)
结合上面的知识,这个看起来就是缩小,反之camera.translate(0f, 0f, -1f)
就是放大,x于y的变动也是类似逻辑。但是这个函数有一个特点,就是获取location获取下来的x、y、z是没变动的。 -
rotate:
基于X轴,参数为正,往上翻,参数为负往下翻
基于Y轴,参数为正往里面翻,参数为负往外面翻
基于Z轴进行反转,参数为正往上翻,参数为负往下翻
-
applyToCanvas 和getMatrix 均可以设置变换。建议用applyToCanvas。因为会减少一个Java 层的对象。
整体来说,这篇更像认知的扩展,通过提高认知,那么面对所谓的3D特效可能更容易切换到编码思维。ok ,水完了,嗯,今天又是没有卡12点的一天哦。