package com.scwang.smart.refresh.header;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import com.scwang.smart.drawable.PathsDrawable;
import com.scwang.smart.refresh.layout.api.RefreshHeader;
import com.scwang.smart.refresh.layout.api.RefreshKernel;
import com.scwang.smart.refresh.layout.api.RefreshLayout;
import com.scwang.smart.refresh.layout.simple.SimpleComponent;
import com.scwang.smart.refresh.layout.constant.SpinnerStyle;
import com.scwang.smart.refresh.layout.util.SmartUtil;
import com.scwang.smartrefresh.header.R;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* Taurus
* Created by scwang on 2017/5/31.
* from https://github.com/Yalantis/Taurus
*/
public class TaurusHeader extends SimpleComponent implements RefreshHeader {
//
protected static String[] airplanePaths = new String[]{
"m23.01,81.48c-0.21,-0.3 -0.38,-0.83 -0.38,-1.19 0,-0.55 0.24,-0.78 1.5,-1.48 1.78,-0.97 2.62,-1.94 2.24,-2.57 -0.57,-0.93 -1.97,-1.24 -11.64,-2.59 -5.35,-0.74 -10.21,-1.44 -10.82,-1.54l-1.09,-0.18 1.19,-0.91c0.99,-0.76 1.38,-0.91 2.35,-0.91 0.64,0 6.39,0.33 12.79,0.74 6.39,0.41 12.09,0.71 12.65,0.67l1.03,-0.07 -1.24,-2.19C30.18,66.77 15.91,42 15.13,40.68l-0.51,-0.87 4.19,-1.26c2.3,-0.69 4.27,-1.26 4.37,-1.26 0.1,0 5.95,3.85 13,8.55 14.69,9.81 17.1,11.31 19.7,12.31 4.63,1.78 6.45,1.69 12.94,-0.64 13.18,-4.73 25.22,-9.13 25.75,-9.4 0.69,-0.36 3.6,1.33 -24.38,-14.22L50.73,23.07 46.74,16.42 42.75,9.77 43.63,8.89c0.83,-0.83 0.91,-0.86 1.46,-0.52 0.32,0.2 3.72,3.09 7.55,6.44 3.83,3.34 7.21,6.16 7.5,6.27 0.29,0.11 13.6,2.82 29.58,6.03 15.98,3.21 31.86,6.4 35.3,7.1l6.26,1.26 3.22,-1.13c41.63,-14.63 67.88,-23.23 85.38,-28 14.83,-4.04 23.75,-4.75 32.07,-2.57 7.04,1.84 9.87,4.88 7.71,8.27 -1.6,2.5 -4.6,4.63 -10.61,7.54 -5.94,2.88 -10.22,4.46 -25.4,9.41 -8.15,2.66 -16.66,5.72 -39.01,14.02 -66.79,24.82 -88.49,31.25 -121.66,36.07 -14.56,2.11 -24.17,2.95 -34.08,2.95 -5.43,0 -5.52,-0.01 -5.89,-0.54z"
};
protected static int[] airplaneColors = new int[]{
0xffffffff
};
protected static String[] cloudPaths = new String[]{
"M551.81,1.01A65.42,65.42 0,0 0,504.38 21.5A50.65,50.65 0,0 0,492.4 20A50.65,50.65 0,0 0,441.75 70.65A50.65,50.65 0,0 0,492.4 121.3A50.65,50.65 0,0 0,511.22 117.64A65.42,65.42 0,0 0,517.45 122L586.25,122A65.42,65.42 0,0 0,599.79 110.78A59.79,59.79 0,0 0,607.81 122L696.34,122A59.79,59.79 0,0 0,711.87 81.9A59.79,59.79 0,0 0,652.07 22.11A59.79,59.79 0,0 0,610.93 38.57A65.42,65.42 0,0 0,551.81 1.01zM246.2,1.71A54.87,54.87 0,0 0,195.14 36.64A46.78,46.78 0,0 0,167.77 27.74A46.78,46.78 0,0 0,120.99 74.52A46.78,46.78 0,0 0,167.77 121.3A46.78,46.78 0,0 0,208.92 96.74A54.87,54.87 0,0 0,246.2 111.45A54.87,54.87 0,0 0,268.71 106.54A39.04,39.04 0,0 0,281.09 122L327.6,122A39.04,39.04 0,0 0,343.38 90.7A39.04,39.04 0,0 0,304.34 51.66A39.04,39.04 0,0 0,300.82 51.85A54.87,54.87 0,0 0,246.2 1.71z",
"m506.71,31.37a53.11,53.11 0,0 0,-53.11 53.11,53.11 53.11,0 0,0 15.55,37.5h75.12a53.11,53.11 0,0 0,1.88 -2.01,28.49 28.49,0 0,0 0.81,2.01h212.96a96.72,96.72 0,0 0,-87.09 -54.85,96.72 96.72,0 0,0 -73.14,33.52 28.49,28.49 0,0 0,-26.74 -18.74,28.49 28.49,0 0,0 -13.16,3.23 53.11,53.11 0,0 0,0.03 -0.66,53.11 53.11,0 0,0 -53.11,-53.11zM206.23,31.81a53.81,53.81 0,0 0,-49.99 34.03,74.91 74.91,0 0,0 -47.45,-17 74.91,74.91 0,0 0,-73.54 60.82,31.3 31.3,0 0,0 -10.17,-1.73 31.3,31.3 0,0 0,-26.09 14.05L300.86,121.98a37.63,37.63 0,0 0,0.2 -3.85,37.63 37.63,0 0,0 -37.63,-37.63 37.63,37.63 0,0 0,-3.65 0.21,53.81 53.81,0 0,0 -53.54,-48.9z",
"m424.05,36.88a53.46,53.46 0,0 0,-40.89 19.02,53.46 53.46,0 0,0 -1.34,1.76 62.6,62.6 0,0 0,-5.39 -0.27,62.6 62.6,0 0,0 -61.36,50.17 62.6,62.6 0,0 0,-0.53 3.51,15.83 15.83,0 0,0 -10.33,-3.84 15.83,15.83 0,0 0,-8.06 2.23,21.1 21.1,0 0,0 -18.31,-10.67 21.1,21.1 0,0 0,-19.47 12.97,21.81 21.81,0 0,0 -6.56,-1.01 21.81,21.81 0,0 0,-19.09 11.32L522.84,122.07a43.61,43.61 0,0 0,-43.11 -37.35,43.61 43.61,0 0,0 -2.57,0.09 53.46,53.46 0,0 0,-53.11 -47.93zM129.08,38.4a50.29,50.29 0,0 0,-50.29 50.29,50.29 50.29,0 0,0 2.37,15.06 15.48,15.83 0,0 0,-5.87 1.68,15.48 15.83,0 0,0 -0.98,0.58 16.53,16.18 0,0 0,-0.19 -0.21,16.53 16.18,0 0,0 -11.86,-4.91 16.53,16.18 0,0 0,-16.38 14.13,20.05 16.18,0 0,0 -14.97,7.04L223.95,122.07a42.56,42.56 0,0 0,1.14 -9.56,42.56 42.56,0 0,0 -42.56,-42.56 42.56,42.56 0,0 0,-6.58 0.54,50.29 50.29,0 0,0 -0,-0.01 50.29,50.29 0,0 0,-46.88 -32.07zM631.67,82.61a64.01,64.01 0,0 0,-44.9 18.42,26.73 26.73,0 0,0 -10.67,-2.24 26.73,26.73 0,0 0,-22.72 12.71,16.88 16.88,0 0,0 -0.25,-0.12 16.88,16.88 0,0 0,-6.57 -1.33,16.88 16.88,0 0,0 -16.15,12.03h160.36a64.01,64.01 0,0 0,-59.1 -39.46z"
};
protected static int[] cloudColors = new int[]{
0xaac7dcf1,0xdde8f3fd,0xfffdfdfd
};
//
//
protected static final float SCALE_START_PERCENT = 0.5f;
protected static final int ANIMATION_DURATION = 1000;
protected static final float SIDE_CLOUDS_INITIAL_SCALE = 0.6f;//1.05f;
protected static final float SIDE_CLOUDS_FINAL_SCALE = 1f;//1.55f;
protected static final float CENTER_CLOUDS_INITIAL_SCALE = 0.8f;//0.8f;
protected static final float CENTER_CLOUDS_FINAL_SCALE = 1f;//1.30f;
protected static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
// Multiply with this animation interpolator time
protected static final int LOADING_ANIMATION_COEFFICIENT = 80;
protected static final int SLOW_DOWN_ANIMATION_COEFFICIENT = 6;
// Amount of lines when is going lading animation
protected static final int WIND_SET_AMOUNT = 10;
protected static final int Y_SIDE_CLOUDS_SLOW_DOWN_COF = 4;
protected static final int X_SIDE_CLOUDS_SLOW_DOWN_COF = 2;
protected static final int MIN_WIND_LINE_WIDTH = 50;
protected static final int MAX_WIND_LINE_WIDTH = 300;
protected static final int MIN_WIND_X_OFFSET = 1000;
protected static final int MAX_WIND_X_OFFSET = 2000;
protected static final int RANDOM_Y_COEFFICIENT = 5;
protected Drawable mAirplane;
protected Drawable mCloudCenter;
protected Matrix mMatrix;
protected float mPercent;
protected int mHeight;
protected int mHeaderHeight;
protected Animation mAnimation;
protected boolean isRefreshing = false;
protected float mLoadingAnimationTime;
protected float mLastAnimationTime;
protected Random mRandom;
// protected boolean mEndOfRefreshing;
//KEY: Y position, Value: X offset of wind
protected Map mWinds;
protected Paint mWindPaint;
protected float mWindLineWidth;
protected boolean mNewWindSet;
protected boolean mInverseDirection;
protected float mFinishTransformation;
protected int mBackgroundColor;
protected RefreshKernel mKernel;
protected enum AnimationPart {
FIRST,
SECOND,
THIRD,
FOURTH
}
//
//
public TaurusHeader(Context context) {
this(context, null);
}
public TaurusHeader(Context context, AttributeSet attrs) {
super(context, attrs, 0);
final View thisView = this;
thisView.setMinimumHeight(SmartUtil.dp2px(100));
mMatrix = new Matrix();
mWinds = new HashMap<>();
mRandom = new Random();
mWindPaint = new Paint();
mWindPaint.setColor(0xffffffff);
mWindPaint.setStrokeWidth(SmartUtil.dp2px(3));
mWindPaint.setAlpha(50);
mSpinnerStyle = SpinnerStyle.FixedBehind;
//
mAnimation = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, @NonNull Transformation t) {
/*SLOW DOWN ANIMATION IN {@link #SLOW_DOWN_ANIMATION_COEFFICIENT} time */
mLoadingAnimationTime = LOADING_ANIMATION_COEFFICIENT * (interpolatedTime / SLOW_DOWN_ANIMATION_COEFFICIENT);
thisView.invalidate();
}
};
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
mAnimation.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
mAnimation.setDuration(ANIMATION_DURATION);
//
//
PathsDrawable airplane = new PathsDrawable();
if (!airplane.parserPaths(airplanePaths)) {
airplane.declareOriginal(3, 3, 257, 79);
}
// airplane.printOriginal("airplane");
airplane.parserColors(airplaneColors);
PathsDrawable cloudCenter = new PathsDrawable();
if(!cloudCenter.parserPaths(cloudPaths)) {
cloudCenter.declareOriginal(-1, 1, 761, 121);
}
// cloudCenter.printOriginal("cloudCenter");
cloudCenter.parserColors(cloudColors);
mAirplane = airplane;
mCloudCenter = cloudCenter;
mAirplane.setBounds(0, 0, SmartUtil.dp2px(65), SmartUtil.dp2px(20));
mCloudCenter.setBounds(0, 0, SmartUtil.dp2px(260), SmartUtil.dp2px(45));
//
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TaurusHeader);
int primaryColor = ta.getColor(R.styleable.TaurusHeader_thPrimaryColor, 0);
if (primaryColor != 0) {
mBackgroundColor = primaryColor;
// thisView.setBackgroundColor(primaryColor);
} else {
mBackgroundColor = 0xff11bbff;
// thisView.setBackgroundColor(0xff11bbff);
}
ta.recycle();
}
//
//
@Override
public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {
mKernel = kernel;
kernel.requestDrawBackgroundFor(this, mBackgroundColor);
}
@Override
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
mHeight = offset;
mPercent = percent;
mHeaderHeight = height;
if (isDragging) {
mFinishTransformation = 0;
}
this.invalidate();
}
@Override
public void onStartAnimator(@NonNull RefreshLayout layout, int height, int maxDragHeight) {
isRefreshing = true;
mFinishTransformation = 0;
final View thisView = this;
thisView.startAnimation(mAnimation);
}
@Override
public int onFinish(@NonNull RefreshLayout layout, boolean success) {
final View thisView = this;
thisView.clearAnimation();
if (success) {
thisView.startAnimation(new Animation() {{
super.setDuration(100);
super.setInterpolator(new AccelerateInterpolator());
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1) {
isRefreshing = false;
}
mFinishTransformation = interpolatedTime;
thisView.invalidate();
}
});
return 200;
} else {
isRefreshing = false;
return 0;
}
}
/**
* @param colors 对应Xml中配置的 srlPrimaryColor srlAccentColor
* @deprecated 请使用 {@link RefreshLayout#setPrimaryColorsId(int...)}
*/
@Override@Deprecated
public void setPrimaryColors(@ColorInt int ... colors) {
// final View thisView = this;
// thisView.setBackgroundColor(colors[0]);
mBackgroundColor = colors[0];
if (mKernel != null) {
mKernel.requestDrawBackgroundFor(this, mBackgroundColor);
}
}
//
//
@Override
protected void dispatchDraw(Canvas canvas) {
final View thisView = this;
final int width = thisView.getWidth();
final int height = mHeight;//thisView.getHeight();
//noinspection EqualsBetweenInconvertibleTypes
final boolean footer = mKernel != null && (this.equals(mKernel.getRefreshLayout().getRefreshFooter()));
if (footer) {
canvas.save();
canvas.translate(0, thisView.getHeight() - mHeight);
}
drawWinds(canvas, width);
drawAirplane(canvas, width, height);
drawSideClouds(canvas, width, height);
drawCenterClouds(canvas, width, height);
if (footer) {
canvas.restore();
}
super.dispatchDraw(canvas);
}
protected void drawWinds(Canvas canvas, int width) {
if (isRefreshing) {
// Set up new set of wind
while (mWinds.size() < WIND_SET_AMOUNT) {
float y = (float) (mHeaderHeight / (Math.random() * RANDOM_Y_COEFFICIENT));
float x = random(MIN_WIND_X_OFFSET, MAX_WIND_X_OFFSET);
// Magic with checking interval between winds
if (mWinds.size() > 1) {
y = 0;
while (y == 0) {
float tmp = (float) (mHeaderHeight / (Math.random() * RANDOM_Y_COEFFICIENT));
for (Map.Entry wind : mWinds.entrySet()) {
// We want that interval will be greater than fifth part of draggable distance
if (Math.abs(wind.getKey() - tmp) > mHeaderHeight / RANDOM_Y_COEFFICIENT) {
y = tmp;
} else {
y = 0;
break;
}
}
}
}
mWinds.put(y, x);
drawWind(canvas, y, x, width);
}
// Draw current set of wind
//noinspection ConstantConditions
if (mWinds.size() >= WIND_SET_AMOUNT) {
for (Map.Entry wind : mWinds.entrySet()) {
drawWind(canvas, wind.getKey(), wind.getValue(), width);
}
}
// We should to create new set of winds
if (mInverseDirection && mNewWindSet) {
mWinds.clear();
mNewWindSet = false;
mWindLineWidth = random(MIN_WIND_LINE_WIDTH, MAX_WIND_LINE_WIDTH);
}
// needed for checking direction
mLastAnimationTime = mLoadingAnimationTime;
}
}
/**
* Draw wind on loading animation
*
* @param canvas - area where we will draw
* @param y - y position fot one of lines
* @param xOffset - x offset for on of lines
*/
protected void drawWind(Canvas canvas, float y, float xOffset, int width) {
/* We should multiply current animation time with this coefficient for taking all screen width in time
Removing slowing of animation with dividing on {@LINK #SLOW_DOWN_ANIMATION_COEFFICIENT}
And we should don't forget about distance that should "fly" line that depend on screen of device and x offset
*/
float cof = (width + xOffset) / (1f * LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT);
float time = mLoadingAnimationTime;
// HORRIBLE HACK FOR REVERS ANIMATION THAT SHOULD WORK LIKE RESTART ANIMATION
if (mLastAnimationTime - mLoadingAnimationTime > 0) {
mInverseDirection = true;
// take time from 0 to end of animation time
time = (1f * LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT) - mLoadingAnimationTime;
} else {
mNewWindSet = true;
mInverseDirection = false;
}
// Taking current x position of drawing wind
// For fully disappearing of line we should subtract wind line width
float x = (width - (time * cof)) + xOffset - mWindLineWidth;
float xEnd = x + mWindLineWidth;
canvas.drawLine(x, y, xEnd, y, mWindPaint);
}
protected void drawSideClouds(Canvas canvas, int width, int height) {
Matrix matrix = mMatrix;
matrix.reset();
Drawable mCloudLeft = mCloudCenter;
Drawable mCloudRight = mCloudCenter;
// Drag percent will newer get more then 1 here
float dragPercent = Math.min(1f, Math.abs(mPercent));
final View thisView = this;
if (thisView.isInEditMode()) {
dragPercent = 1;
mHeaderHeight = height;
}
float scale;
float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
if (scalePercentDelta > 0) {
float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
scale = SIDE_CLOUDS_INITIAL_SCALE + (SIDE_CLOUDS_FINAL_SCALE - SIDE_CLOUDS_INITIAL_SCALE) * scalePercent;
} else {
scale = SIDE_CLOUDS_INITIAL_SCALE;
}
// Current y position of clouds
float dragYOffset = mHeaderHeight * (1.0f - dragPercent);
// Position where clouds fully visible on screen and we should drag them with content of listView
// int cloudsVisiblePosition = mHeaderHeight / 2 - mCloudCenter.height() / 2;
// boolean needMoveCloudsWithContent = false;
// if (dragYOffset < cloudsVisiblePosition) {
// needMoveCloudsWithContent = true;
// }
float offsetLeftX = 0 - mCloudLeft.getBounds().width() / 2f;
float offsetLeftY = (//needMoveCloudsWithContent
//? mHeaderHeight * dragPercent - mCloudLeftgetBounds().height() :
dragYOffset);
float offsetRightX = width - mCloudRight.getBounds().width() / 2f;
float offsetRightY = (//needMoveCloudsWithContent
//? mHeaderHeight * dragPercent - mCloudRightgetBounds().height() :
dragYOffset);
// Magic with animation on loading process
if (isRefreshing) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
offsetLeftX -= 2*getAnimationPartValue(AnimationPart.FIRST) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += getAnimationPartValue(AnimationPart.FIRST) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
offsetLeftX -= 2*getAnimationPartValue(AnimationPart.SECOND) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += getAnimationPartValue(AnimationPart.SECOND) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
offsetLeftX -= getAnimationPartValue(AnimationPart.THIRD) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += 2*getAnimationPartValue(AnimationPart.THIRD) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
offsetLeftX -= getAnimationPartValue(AnimationPart.FOURTH) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += 2*getAnimationPartValue(AnimationPart.FOURTH) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
}
}
if (offsetLeftY + scale * mCloudLeft.getBounds().height() < height + 2) {
offsetLeftY = height + 2 - scale * mCloudLeft.getBounds().height();
}
if (offsetRightY + scale * mCloudRight.getBounds().height() < height + 2) {
offsetRightY = height + 2 - scale * mCloudRight.getBounds().height();
}
final int saveCount = canvas.getSaveCount();
canvas.save();
canvas.translate(offsetLeftX, offsetLeftY);
matrix.postScale(scale, scale, mCloudLeft.getBounds().width() * 3 / 4f, mCloudLeft.getBounds().height());
canvas.concat(matrix);
mCloudLeft.setAlpha(100);
mCloudLeft.draw(canvas);
mCloudLeft.setAlpha(255);
canvas.restoreToCount(saveCount);
canvas.save();
canvas.translate(offsetRightX, offsetRightY);
matrix.postScale(scale, scale, 0, mCloudRight.getBounds().height());
canvas.concat(matrix);
mCloudRight.setAlpha(100);
mCloudRight.draw(canvas);
mCloudRight.setAlpha(255);
canvas.restoreToCount(saveCount);
}
protected void drawCenterClouds(Canvas canvas, int width, int height) {
Matrix matrix = mMatrix;
matrix.reset();
float dragPercent = Math.min(1f, Math.abs(mPercent));
final View thisView = this;
if (thisView.isInEditMode()) {
dragPercent = 1;
mHeaderHeight = height;
}
float scale;
float overDragPercent = 0;
boolean overDrag = false;
if (mPercent > 1.0f) {
overDrag = true;
// Here we want know about how mach percent of over drag we done
overDragPercent = Math.abs(1.0f - mPercent);
}
float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
if (scalePercentDelta > 0) {
float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
scale = CENTER_CLOUDS_INITIAL_SCALE + (CENTER_CLOUDS_FINAL_SCALE - CENTER_CLOUDS_INITIAL_SCALE) * scalePercent;
} else {
scale = CENTER_CLOUDS_INITIAL_SCALE;
}
float parallaxPercent = 0;
boolean parallax = false;
// Current y position of clouds
float dragYOffset = mHeaderHeight * dragPercent;
// Position when should start parallax scrolling
int startParallaxHeight = mHeaderHeight - mCloudCenter.getBounds().height()/2;
if (dragYOffset > startParallaxHeight) {
parallax = true;
parallaxPercent = dragYOffset - startParallaxHeight;
}
float offsetX = (width / 2f) - mCloudCenter.getBounds().width() / 2f;
float offsetY = dragYOffset
- (parallax ? mCloudCenter.getBounds().height()/2f + parallaxPercent : mCloudCenter.getBounds().height()/2f);
float sx = overDrag ? scale + overDragPercent / 4 : scale;
float sy = overDrag ? scale + overDragPercent / 2 : scale;
if (isRefreshing && !overDrag) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
sx = scale - (getAnimationPartValue(AnimationPart.FIRST) / LOADING_ANIMATION_COEFFICIENT) / 8;
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
sx = scale - (getAnimationPartValue(AnimationPart.SECOND) / LOADING_ANIMATION_COEFFICIENT) / 8;
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
sx = scale + (getAnimationPartValue(AnimationPart.THIRD) / LOADING_ANIMATION_COEFFICIENT) / 6;
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
sx = scale + (getAnimationPartValue(AnimationPart.FOURTH) / LOADING_ANIMATION_COEFFICIENT) / 6;
}
sy = sx;
}
matrix.postScale(sx, sy, mCloudCenter.getBounds().width() / 2f, 0);
if (offsetY + sy * mCloudCenter.getBounds().height() < height + 2) {
offsetY = height + 2 - sy * mCloudCenter.getBounds().height();
}
final int saveCount = canvas.getSaveCount();
canvas.save();
canvas.translate(offsetX, offsetY);
canvas.concat(matrix);
mCloudCenter.draw(canvas);
canvas.restoreToCount(saveCount);
}
protected void drawAirplane(Canvas canvas, int width, int height) {
Matrix matrix = mMatrix;
matrix.reset();
float dragPercent = mPercent;
float rotateAngle = 0;
final View thisView = this;
if (thisView.isInEditMode()) {
dragPercent = 1;
mHeaderHeight = height;
}
// Check overDrag
if (dragPercent > 1.0f) {
rotateAngle = 20 * (float) (1 - Math.pow(100, -(dragPercent - 1) / 2));
dragPercent = 1.0f;
}
float offsetX = ((width * dragPercent) / 2) - mAirplane.getBounds().width() / 2f;
float offsetY = mHeaderHeight * (1 - dragPercent/2) - mAirplane.getBounds().height() / 2f;
if (mFinishTransformation > 0) {
offsetY += (0 - offsetY) * mFinishTransformation;
offsetX += (width + mAirplane.getBounds().width() - offsetX) * mFinishTransformation;
}
if (isRefreshing) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
offsetY -= getAnimationPartValue(AnimationPart.FIRST);
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
offsetY -= getAnimationPartValue(AnimationPart.SECOND);
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
offsetY += getAnimationPartValue(AnimationPart.THIRD);
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
offsetY += getAnimationPartValue(AnimationPart.FOURTH);
}
}
if (rotateAngle > 0) {
matrix.postRotate(rotateAngle,
mAirplane.getBounds().width() / 2f,
mAirplane.getBounds().height() / 2f);
}
final int saveCount = canvas.getSaveCount();
canvas.save();
canvas.translate(offsetX, offsetY);
canvas.concat(matrix);
mAirplane.draw(canvas);
canvas.restoreToCount(saveCount);
}
//
//
protected float random(int min, int max) {
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
return mRandom.nextInt((max - min) + 1) + min;
}
/**
* We need a special value for different part of animation
*
* @param part - needed part
* @return - value for needed part
*/
protected float getAnimationPartValue(AnimationPart part) {
switch (part) {
case FIRST: {
return mLoadingAnimationTime;
}
case SECOND: {
return getAnimationTimePart(AnimationPart.FOURTH) - (mLoadingAnimationTime - getAnimationTimePart(AnimationPart.FOURTH));
}
case THIRD: {
return mLoadingAnimationTime - getAnimationTimePart(AnimationPart.SECOND);
}
case FOURTH: {
return getAnimationTimePart(AnimationPart.THIRD) - (mLoadingAnimationTime - getAnimationTimePart(AnimationPart.FOURTH));
}
default:
return 0;
}
}
/**
* On drawing we should check current part of animation
*
* @param part - needed part of animation
* @return - return true if current part
*/
protected boolean checkCurrentAnimationPart(AnimationPart part) {
switch (part) {
case FIRST: {
return mLoadingAnimationTime < getAnimationTimePart(AnimationPart.FOURTH);
}
case SECOND:
case THIRD: {
return mLoadingAnimationTime < getAnimationTimePart(part);
}
case FOURTH: {
return mLoadingAnimationTime > getAnimationTimePart(AnimationPart.THIRD);
}
default:
return false;
}
}
/**
* Get part of animation duration
*
* @param part - needed part of time
* @return - interval of time
*/
protected int getAnimationTimePart(AnimationPart part) {
switch (part) {
case SECOND: {
return LOADING_ANIMATION_COEFFICIENT / 2;
}
case THIRD: {
return getAnimationTimePart(AnimationPart.FOURTH) * 3;
}
case FOURTH: {
return LOADING_ANIMATION_COEFFICIENT / 4;
}
default:
return 0;
}
}
//
}