What hungry drop-down refresh effect
Design sketch
Summary:
A hungry drop-down refresh animation cannot be achieved with a frame animation because it starts with a sliding gesture, and the swing amplitude of the left and right handles changes with it. Then the finger is lifted and the food picture is thrown out of the box. The animation starts with only one box and keeps throwing food pictures over and over again, so the frame animation does not achieve the desired effect.You can only draw by yourself.
Analyze before writing code:
Drawings fall into three categories (from easy to difficult): 1. Fixed boxes, 2. Handles on the left and right sides of boxes, 3. Food
The first is a good drawing. The coordinates of the center point of the box are determined by width and heights. Then, based on the width and height of the box, a matrix is obtained, in which the box is drawn.
The second type is also good to draw, but needs to swing according to the gesture.Explain the code in more detail later
Category 3: When drawing Category 3, a lot of tests and adjustments have been made, because some details need to be noticed (Details 1: food picture motion track, Details 2: food order, they are not thrown at the same time, Details 3: how to keep throwing food repeatedly and repeatedly)
Detail 1:
We specify a circular arc track with a path of 120 degrees, which can be solved by calling the sin and cos methods in the Math class (code-specific combination)
Details 2:
Assuming that the entire animation time is 1000ms and the angle of motion is 120 degrees, there are five food pictures in total, then there are two ways (depending on time and angle) to determine the order in which the food pictures are thrown.
Angle method: 120 is divided into four groups, that is, 30 degrees. If the first picture is thrown at 30 degrees, then the second picture is thrown along with it, and the same is true afterwards.
But this introduces new problems: for example, if the first picture is thrown at 120 degrees, it will start throwing at 0 degrees again, so the original second picture is still on the parabola, and suddenly the position of the first picture becomes less than 30 degrees, and you will stop running.Solution: The first cycle is thrown at an angle, while the other cycle is controlled by time
Time control: Each food object records the time it starts throwing and then passes the formula
Move angle = (current time - start throw time) / entire animation cycle time * entire animation move angle (i.e. 120 degrees)
The angle thrown is calculated in radians (radians formula: 2* Math.PI/360*angle), then the coordinates X,Y of the center point of the food picture are calculated by Math sin and cos. Finally, a matrix is obtained by the coordinates of the center point and the width and height of the food foodWidth,foodHeight, and a picture of the food is drawn in the matrix.
After solving detail two, we found that detail three was also solved.
After reading the above explanation, I don't understand it. That's okay. Next, I'll combine the code explanation.
private void onDrawBox(Canvas canvas) { //Specify an area to draw a 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); }
The code above is simple and draws a fixed box
The swing of the left and right handles is that a graph is rotating.For example, the handle on the left is fixed to the right end point, and the rotation angle has a certain range.The rotation angle is controlled by a gesture slide, so it is passed in from the outside, see the code below//Draw left and right handles 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(); }
Next, look at the code for drawing a picture of food
//Draw batch food private void onDrawFood(Canvas canvas) { for(int i=0; i<elmFoodList.size(); i++){ if(i != 0 && isFirstAnimator) { if(elmFoodList.get(i-1).angle > 27) { drawFood(i, canvas); if(i == elmFoodList.size() -1) isFirstAnimator = false; } } else { drawFood(i, canvas); } } } //Draw a single food 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); }
There's an isFirstAnimator inside to determine if it's the first cycle, and if so, to determine if the previous food picture has an angle greater than 30, I wrote 27 here, which is not a big problem.If not, see drawFood's code
food.angle = (System.currentTimeMillis() - food.startTime) * 1.0f / ANIMATOR_DURATION * MOVE_END_ANGLE;
Isn't this code the same formula I used to say?
Note that if the angle of the food is greater than MOVE_END_ANGLE, it indicates that the food has been thrown farthest away. To re-throw it from 0, three operations are required: initialization angle is 0, initialization throw time is the current time, and finally change throwing direction.
Off-topic: The drop-down refresh uses android-Ultra-Pull-To-Refresh, a useful drop-down refresh control that allows you to customize the refresh view
Finally, attach the project address: https://github.com/zx391324751/ElmRefreshViewDemo
If you think it's wide, give it to a gorilla.Thank you.
Welcome to upload, please indicate the original address