1,需求开发一个按照固定轨迹游泳的鱼池
2,实现思路
ViewGroup + ImageView组合,通过定时刷新,不断对子view位置进行摆放处理
- 2.1,定时刷新采用
Handler
定时发送消息 - 2.2,收到消息后调用
ViewGroup
的requestLayout()
触发回调onLayout(boolean changed, int left, int top, int right, int bottom)
函数 - 2.3,重写
ViewGroup
的onLayout方法,调用子View的layout()函数不断进行位置放.最终形成鱼宠不断游动效果.
3,鱼宠游动轨迹图
device-2021-04-02-103933.png4,坐标点的测试
P0,
x:990,y:1065
P0x:990, P0y:1065
P1,
x:540,y:572
P1x:540, P1y:572
P2,
x:90,y:572
P2x:90, P2y:572
P3,
x:990,y:80
P3x:990, P3y:80
P4,
x:990,y:572
P4x:990, P4y:572
P5,
x:990,y:80
P5x:990, P5y:990
P6,
x:90,y:80
P6x:90, P6y:80
P7,
x:990,y:572
P7x:990, P7y:572
P8,
x:540,y:572
P8x:540, P8y:572
P9,
x:90,y:1065
P9x:90, P9y:1065
P10,
x:90,y:572
P10x:90, P10y:572
P11,
x:90,y:1065
P11x:90, P11y:1065
P12,
x:990,y:1065
P12x:990, P12y:1065
5,鱼宠游动状态的定义
/**
* 鱼儿的游动状态
*/
@IntDef({
FishStatus.UN_KNOW,
FishStatus.TURN_LEFT,
FishStatus.TURN_RIGHT,
FishStatus.MOVE_LEFT,
FishStatus.MOVE_RIGHT,
})
@Retention(RetentionPolicy.SOURCE)
@interface FishStatus {
/**
* 未知状态
*/
int UN_KNOW = -1;
/**
* 向左转
*/
int TURN_LEFT = 0;
/**
* 向右转
*/
int TURN_RIGHT = 1;
/**
* 向左正常游动
*/
int MOVE_LEFT = 2;
/**
* 向右正常游动
*/
int MOVE_RIGHT = 3;
}
5,对鱼宠游动过程坐标点进行监听,当游动到了路线轨迹坐标点处进行处理并且修改游动状态
-
5.1坐标点对应鱼宠游动状态分析
-
P0和P12坐标重合
P0开始向左游动,P12开始向左转身 -
P1和P8坐标重合
P1,P8分别停留1秒钟 -
P2和P10坐标重合
P2开始向右转身,P10向下游 -
P3和P5坐标重合
P3向下游,P5开始向左转身 -
P4和P7坐标重合
P4开始向上游动,P7开始向左转身, -
P9和P11坐标重合
P9开始向上游动,P11开始向右转身
-P6单独坐标点
P6开始向右转身
-
5.2相邻两坐标点的轨迹的游动状态分析
-
1,P0--->P1
向左上方移动 -
2,P1--->P2
向左方移动 -
3,P2--->P3
向右上方移动 -
3,P3--->P4
向下方移动 -
4,P4--->P5
向上方移动 -
5,P5--->P6
向左方移动 -
6,P6--->P7
向右下方移动 -
7,P7--->P8
向左方移动 -
8,P8--->P9
向左下方移动 -
9,P9--->P10
向上方移动 -
10,P10--->P11
向下方移动 -
10,P11--->P12
向右方移动 -
5.3如何判断鱼宠坐标点是否在指定的坐标处.
-
1,鱼宠坐标和P0坐标相等,也和P12坐标相等,不同时刻的路线坐标值存在相同情况,根据坐标值无法判定是否处于指定的坐标处.
-
2,鱼宠游动方向向左,游动方向由坐标点驱动,无法用于判断是否处于坐标点处.
-
3,定义
private var progress = 0.0F
变量,表示移动路线的进度,每个坐标点对应一个进度值.- P0,progress=0
- P12,progress=1
-
4,如何建立坐标点和路线进度的映射关系
这里我们采用,
key
和value
键值对形式来保存映射关系,用key
来表示进度值progress
,用value
来表示指定坐标点容器索引posotion
,
(0.0,0),(1.0,12),综合分析无法建立较完善的映射关系,该方式无法用来判定是否处于指定坐标处. -
5,根据坐标点距离起点的总长度来判定坐标点
P0:pLength = 0,P1:pLength = (P1.x-P0.x) * (P1.x-P0.x) + (P1.y-P0.y) * (P1.y-P0.y)开根号.
-
6,根据坐标点范围以及游动状态组合判断
/**
* 根据移动的坐标点和轨迹坐标点来修改小鱼的游动状态.
* @param x
* @param y
* @param distance 距离起点的直线距离.
*/
private fun onSwimming(x: Float, y: Float, distance: Float) {
if (pointList.isEmpty()) return
if (distance == 0F || progress == 0F) {
//P0,点处开始向左上游动.
changeSwimming(FishStatus.TURN_LEFT)
changeSwimming(FishStatus.MOVE_TOP_LEFT)
}
if (isInPointRect(pointList[1], x, y)) {
//P1,P8处停留
changeSwimming(FishStatus.REST)
} else {
when (lastFishStatus) {
FishStatus.MOVE_TOP_LEFT -> {
if (fishStatus == FishStatus.REST) {
changeSwimming(FishStatus.MOVE_LEFT)
}
}
FishStatus.MOVE_LEFT -> {
if (fishStatus == FishStatus.REST) {
changeSwimming(FishStatus.MOVE_BOTTOM_LEFT)
}
}
else -> {
when {
//P2,P10
//P2点处向右转身,开始向右游动.
isInPointRect(pointList[2], x, y) -> {
//判定该点位于此P2
if (fishStatus == FishStatus.MOVE_LEFT) {
if (fishStatus != FishStatus.MOVE_TOP_RIGHT) {
changeSwimming(FishStatus.TURN_RIGHT)
changeSwimming(FishStatus.MOVE_TOP_RIGHT)
}
}
//P10->p11,下游
if (fishStatus == FishStatus.MOVE_TOP_ON_LEFT) {
changeSwimming(FishStatus.MOVE_BOTTOM_ON_LEFT)
}
}
//P3,P5,
isInPointRect(pointList[3], x, y) -> {
//P3,下游
if (fishStatus == FishStatus.MOVE_TOP_RIGHT) {
changeSwimming(FishStatus.MOVE_BOTTOM_ON_RIGHT)
}
//P5点处向左转身,开始向左游动.
if (fishStatus == FishStatus.MOVE_TOP_ON_RIGHT) {
changeSwimming(FishStatus.TURN_LEFT)
changeSwimming(FishStatus.MOVE_LEFT)
}
}
//P4,P7,
isInPointRect(pointList[4], x, y) -> {
//P4,上游
if (fishStatus == FishStatus.MOVE_BOTTOM_ON_RIGHT) {
changeSwimming(FishStatus.MOVE_TOP_ON_RIGHT)
}
//P7点处向左转身,开始向左游动.
if (fishStatus == FishStatus.MOVE_BOTTOM_RIGHT) {
changeSwimming(FishStatus.TURN_LEFT)
changeSwimming(FishStatus.MOVE_LEFT)
}
}
//P6,
isInPointRect(pointList[6], x, y) -> {
//P6点处向右转身,开始向右游动.
if (fishStatus == FishStatus.MOVE_LEFT) {
changeSwimming(FishStatus.TURN_RIGHT)
changeSwimming(FishStatus.MOVE_BOTTOM_RIGHT)
}
}
//P9,P11,
isInPointRect(pointList[9], x, y) -> {
//P9,垂直向上游
if (fishStatus == FishStatus.MOVE_BOTTOM_LEFT) {
changeSwimming(FishStatus.MOVE_TOP_ON_LEFT)
}
//P11,向右转身,向右移动
if (fishStatus == FishStatus.MOVE_BOTTOM_ON_LEFT) {
changeSwimming(FishStatus.TURN_RIGHT)
changeSwimming(FishStatus.MOVE_RIGHT)
}
}
}
}
}
}
}
-
5.4鱼宠还正在向左游动就向边转身了的视觉异常处理
-
6采用SurfaceView方案来增加动画的流畅性.
-
7动画运行相关参考资料
https://github.com/blipinsk/ViewPropertyObjectAnimator
一款强大的属性动画操作集合
https://github.com/paulyung541/ActionAnimatorSet
小船游动效果
https://github.com/WANZIzZ/RowingView
方块移动,飞机移动
https://github.com/Jetpack-Missionary/MotionChallenge
赛车动画
https://github.com/VKOOY/AnimationRacingCarForKotlin
SurfaceView实现墨迹天气的风车效果
https://blog.csdn.net/xyz_lmn/article/details/20483709
SurfaceView实现下雨与下雪动画效果(Kotlin语法)
https://www.jb51.net/article/123687.htm
SurfaceView实现鱼儿游动动画
https://www.jb51.net/article/138961.htm
SurfaceView实现红包雨平移动画
http://www.zyiz.net/tech/detail-62558.html
Android自定义View实现抖音飘动红心效果
https://www.jb51.net/article/187575.htm
Android十个小案例动画,自定义View动画实现,ValueAnimator
https://blog.csdn.net/u012835548/article/details/53887607
Android属性动画 - 平移动画
https://blog.csdn.net/u010349644/article/details/101151809
网友评论