饿了么的下拉刷新动画不能用帧动画实现,因为它是先根据下滑手势,左右两边的手柄摆动幅度会跟着变化,然后手指抬起再做食物图片从箱子里面抛出的动画,动画开始是只有一个箱子而且以后一直不断重复抛出食物图片,所以帧动画做不出想要的效果。只能靠自己画图。
画图分为三类(从易到难): 1.固定不动的箱子,2.箱子左右两边的手柄,3.食物
第一类很好画图,通过width和height确定箱子的中心点坐标,然后根据箱子的宽高boxWidth和boxHeight,得到一个矩阵,在这个矩阵中画出箱子。
第二类也好画,但是需要根据手势做摆动。待会结合代码详细讲解
第三类:画第三类的时候做了很多测试和调整,因为需要注意一些细节( 细节一:食物图片运动轨迹,细节二:食物先后顺序,它们不是同时抛出,细节三:如何做到一直循环反复的抛出食物)
细节二:
假设整个动画时间是1000ms, 运动的角度是120度的情况下,食物的图片总共5个,那么有两种方法(根据时间和根据角度)可以确定先后抛出食物图片的顺序。
角度的方法:120分成4组的话就是30度角,如果第一张图片抛到了30度角的话,那么第二张图要跟着抛出,后面同理。
但是这样会引入新的问题: 比如第一张图片抛到120度的位置要重新从0度的位置开始抛,这样原来第二张图还在抛物线上,突然第一张图片位置变为小于30度角了,就会停止跑动。 解决办法:第一个周期通过角度抛出,其他周期交给时间去控制
时间控制: 每个食物对象记录下开始抛出的时间,然后通过公式
移动的角度 = (当前时间 - 开始抛出时间) / 整个动画周期时间 * 整个动画移动角度(即120度)
得到抛出的角度算出弧度(弧度计算公式:2* Math.PI/360*angle),再通过Math的sin和cos计算出食物图片的中心点坐标X,Y;最后通过中心点坐标和食物的宽高foodWidth,foodHeight得到一个矩阵,在矩阵中画出食物图片。
解决完细节二,发现细节三也跟着解决了。
看完上面解释没听懂,没关系,接下来结合代码解释
private void onDrawBox(Canvas canvas) {
//指定个区域画box
RectF rectF = new RectF();
rectF.left = boxCenterPoint.x - boxWidth/2;
rectF.right = boxCenterPoint.x + boxWidth/2;
rectF.top = boxCenterPoint.y - boxHeight/2;
rectF.bottom = boxCenterPoint.y + boxHeight/2;
canvas.drawBitmap(boxBitmap, null, rectF, bitmapPaint);
}
//画左右手柄
private void onDrawHand(Canvas canvas) {
canvas.save();
canvas.rotate(-(currentHandAngle - 180), leftHandPoint.x, leftHandPoint.y);
RectF rectLeft = new RectF();
rectLeft.left = leftHandPoint.x - handWidth;
rectLeft.right = leftHandPoint.x;
rectLeft.top = leftHandPoint.y;
rectLeft.bottom = leftHandPoint.y + handHeight;
canvas.drawBitmap(leftHandBitmap, null, rectLeft, bitmapPaint);
canvas.restore();
canvas.save();
canvas.rotate((currentHandAngle - 180), rightHandPoint.x, rightHandPoint.y);
RectF rectRight = new RectF();
rectRight.left = rightHandPoint.x;
rectRight.right = rightHandPoint.x + handWidth;
rectRight.top = rightHandPoint.y;
rectRight.bottom = rightHandPoint.y + handHeight;
canvas.drawBitmap(rightHandBitmap, null, rectRight, bitmapPaint);
canvas.restore();
}
左右两个手柄摆动的现象就是一张图在做旋转。比如左边的手柄是固定右端点,旋转角度也有一定范围。旋转角度是由手势滑动控制,所以由外部传入,看下面代码
public void setPullPositionChanged(float percent){
currentHandAngle = (percent * (HAND_END_ANGLE - HAND_START_ANGLE) + HAND_START_ANGLE);
if(currentHandAngle < HAND_START_ANGLE)
currentHandAngle = HAND_START_ANGLE;
if(currentHandAngle > HAND_END_ANGLE)
currentHandAngle = HAND_END_ANGLE;
if(status == STATUS_MOVING)
postInvalidate();
}
//画批量食物
private void onDrawFood(Canvas canvas) {
for(int i=0; i 27) {
drawFood(i, canvas);
if(i == elmFoodList.size() -1)
isFirstAnimator = false;
}
} else {
drawFood(i, canvas);
}
}
}
//画单个食物
private void drawFood(int foodPosition, Canvas canvas){
RectF rectF = new RectF();
ElmFood food = elmFoodList.get(foodPosition);
if(food.startTime == 0){
food.angle = 0;
food.startTime = System.currentTimeMillis();
} else {
food.angle = (System.currentTimeMillis() - food.startTime) * 1.0f / ANIMATOR_DURATION * MOVE_END_ANGLE;
}
if(food.angle > MOVE_END_ANGLE) {
food.angle = 0;
food.startTime = System.currentTimeMillis();
if(food.direction == 0)
food.direction = 1;
else
food.direction = 0;
}
if(food.direction == 0) {
food.x = boxCenterPoint.x - getXByAngle(food.angle, MOVE_RADIUS);
food.y = boxCenterPoint.y - getYByAngle(food.angle, MOVE_RADIUS);
} else {
food.x = boxCenterPoint.x + getXByAngle(food.angle, MOVE_RADIUS);
food.y = boxCenterPoint.y - getYByAngle(food.angle, MOVE_RADIUS);
}
rectF.left = food.x - foodWidth / 2;
rectF.right = food.x + foodWidth / 2;
rectF.top = food.y - foodHeight / 2;
rectF.bottom = food.y + foodHeight / 2;
canvas.drawBitmap(foodBitmap[foodPosition], null, rectF, bitmapPaint);
}
food.angle = (System.currentTimeMillis() - food.startTime) * 1.0f / ANIMATOR_DURATION * MOVE_END_ANGLE;
这段代码不就是我之前说的那个公式么。
请注意如果食物的角度大于MOVE_END_ANGLE,表明这个食物已经抛到最远点了,要重新从0点抛,需要做三部操作:初始化角度为0,初始化抛出时间为当前时间,最后改变抛出方向。
题外话:下拉刷新用的是android-Ultra-Pull-To-Refresh,挺好用的下拉刷新控件,可自定义刷新视图
最后附上项目地址:https://github.com/zx391324751/ElmRefreshViewDemo
如果觉的阔以,请给个猩猩。 谢谢各位。
欢迎转载,当请注明原文地址