仿饿了么下拉刷新效果

仿饿了么下拉刷新效果

效果图

仿饿了么下拉刷新效果_第1张图片

概述:

饿了么的下拉刷新动画不能用帧动画实现,因为它是先根据下滑手势,左右两边的手柄摆动幅度会跟着变化,然后手指抬起再做食物图片从箱子里面抛出的动画,动画开始是只有一个箱子而且以后一直不断重复抛出食物图片,所以帧动画做不出想要的效果。只能靠自己画图。

编写代码前分析:

画图分为三类(从易到难):  1.固定不动的箱子,2.箱子左右两边的手柄,3.食物

第一类很好画图,通过width和height确定箱子的中心点坐标,然后根据箱子的宽高boxWidth和boxHeight,得到一个矩阵,在这个矩阵中画出箱子。

第二类也好画,但是需要根据手势做摆动。待会结合代码详细讲解

第三类:画第三类的时候做了很多测试和调整,因为需要注意一些细节( 细节一:食物图片运动轨迹,细节二:食物先后顺序,它们不是同时抛出,细节三:如何做到一直循环反复的抛出食物)

细节一:

我们规定轨迹为120度的圆弧轨迹,调用Math类里面的sin和cos方法可以解决该问题(具体结合代码)

细节二:

假设整个动画时间是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);
    }

里面有个isFirstAnimator用来判断是否是第一个周期,如果是,则去判断前面的食物图片的角度是否大于30,我这边写了27,问题不大。如果否,请看drawFood的代码

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

如果觉的阔以,请给个猩猩。 谢谢各位。


欢迎转载,当请注明原文地址



你可能感兴趣的:(android)