Add RefreshLayout
This commit is contained in:
1 parent
7cb04f39b3
commit
0804bfc88d
5 files changed
+2295
No files matched your search
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.widget.refreshlayout;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
/**
|
||||
* A pre-baked bezier-curved interpolator for indeterminate progress animations.
|
||||
*/
|
||||
final class BakedBezierInterpolator implements Interpolator {
|
||||
private static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
|
||||
|
||||
public final static BakedBezierInterpolator getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use getInstance instead of instantiating.
|
||||
*/
|
||||
private BakedBezierInterpolator() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup table values.
|
||||
* Generated using a Bezier curve from (0,0) to (1,1) with control points:
|
||||
* P0 (0,0)
|
||||
* P1 (0.4, 0)
|
||||
* P2 (0.2, 1.0)
|
||||
* P3 (1.0, 1.0)
|
||||
*
|
||||
* Values sampled with x at regular intervals between 0 and 1.
|
||||
*/
|
||||
private static final float[] VALUES = new float[] {
|
||||
0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
|
||||
0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
|
||||
0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
|
||||
0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
|
||||
0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
|
||||
0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
|
||||
0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
|
||||
0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
|
||||
0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
|
||||
0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
|
||||
};
|
||||
|
||||
private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
|
||||
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
if (input >= 1.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (input <= 0f) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
int position = Math.min(
|
||||
(int)(input * (VALUES.length - 1)),
|
||||
VALUES.length - 2);
|
||||
|
||||
float quantized = position * STEP_SIZE;
|
||||
float difference = input - quantized;
|
||||
float weight = difference / STEP_SIZE;
|
||||
|
||||
return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.widget.refreshlayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RadialGradient;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.OvalShape;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* Private class created to work around issues with AnimationListeners being
|
||||
* called before the animation is actually complete and support shadows on older
|
||||
* platforms.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class CircleImageView extends ImageView {
|
||||
|
||||
private static final int KEY_SHADOW_COLOR = 0x1E000000;
|
||||
private static final int FILL_SHADOW_COLOR = 0x3D000000;
|
||||
// PX
|
||||
private static final float X_OFFSET = 0f;
|
||||
private static final float Y_OFFSET = 1.75f;
|
||||
private static final float SHADOW_RADIUS = 3.5f;
|
||||
private static final int SHADOW_ELEVATION = 4;
|
||||
|
||||
private Animation.AnimationListener mListener;
|
||||
private int mShadowRadius;
|
||||
|
||||
public CircleImageView(Context context, int color, final float radius) {
|
||||
super(context);
|
||||
final float density = getContext().getResources().getDisplayMetrics().density;
|
||||
final int diameter = (int) (radius * density * 2);
|
||||
final int shadowYOffset = (int) (density * Y_OFFSET);
|
||||
final int shadowXOffset = (int) (density * X_OFFSET);
|
||||
|
||||
mShadowRadius = (int) (density * SHADOW_RADIUS);
|
||||
|
||||
ShapeDrawable circle;
|
||||
if (elevationSupported()) {
|
||||
circle = new ShapeDrawable(new OvalShape());
|
||||
ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
|
||||
} else {
|
||||
OvalShape oval = new OvalShadow(mShadowRadius, diameter);
|
||||
circle = new ShapeDrawable(oval);
|
||||
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
|
||||
circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
|
||||
KEY_SHADOW_COLOR);
|
||||
final int padding = (int) mShadowRadius;
|
||||
// set padding so the inner image sits correctly within the shadow.
|
||||
setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
circle.getPaint().setColor(color);
|
||||
setBackgroundDrawable(circle);
|
||||
}
|
||||
|
||||
private boolean elevationSupported() {
|
||||
return android.os.Build.VERSION.SDK_INT >= 21;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (!elevationSupported()) {
|
||||
setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight()
|
||||
+ mShadowRadius*2);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAnimationListener(Animation.AnimationListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart() {
|
||||
super.onAnimationStart();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationStart(getAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationEnd(getAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the background color of the circle image view.
|
||||
*/
|
||||
public void setBackgroundColor(int colorRes) {
|
||||
if (getBackground() instanceof ShapeDrawable) {
|
||||
final Resources res = getResources();
|
||||
((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));
|
||||
}
|
||||
}
|
||||
|
||||
private class OvalShadow extends OvalShape {
|
||||
private RadialGradient mRadialGradient;
|
||||
private int mShadowRadius;
|
||||
private Paint mShadowPaint;
|
||||
private int mCircleDiameter;
|
||||
|
||||
public OvalShadow(int shadowRadius, int circleDiameter) {
|
||||
super();
|
||||
mShadowPaint = new Paint();
|
||||
mShadowRadius = shadowRadius;
|
||||
mCircleDiameter = circleDiameter;
|
||||
mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
|
||||
mShadowRadius, new int[] {
|
||||
FILL_SHADOW_COLOR, Color.TRANSPARENT
|
||||
}, null, Shader.TileMode.CLAMP);
|
||||
mShadowPaint.setShader(mRadialGradient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, Paint paint) {
|
||||
final int viewWidth = CircleImageView.this.getWidth();
|
||||
final int viewHeight = CircleImageView.this.getHeight();
|
||||
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
|
||||
mShadowPaint);
|
||||
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.widget.refreshlayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Fancy progress indicator for Material theme.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class MaterialProgressDrawable extends Drawable implements Animatable {
|
||||
private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
|
||||
private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();
|
||||
private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();
|
||||
private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@IntDef({LARGE, DEFAULT})
|
||||
public @interface ProgressDrawableSize {}
|
||||
// Maps to ProgressBar.Large style
|
||||
static final int LARGE = 0;
|
||||
// Maps to ProgressBar default style
|
||||
static final int DEFAULT = 1;
|
||||
|
||||
// Maps to ProgressBar default style
|
||||
private static final int CIRCLE_DIAMETER = 40;
|
||||
private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
|
||||
private static final float STROKE_WIDTH = 2.5f;
|
||||
|
||||
// Maps to ProgressBar.Large style
|
||||
private static final int CIRCLE_DIAMETER_LARGE = 56;
|
||||
private static final float CENTER_RADIUS_LARGE = 12.5f;
|
||||
private static final float STROKE_WIDTH_LARGE = 3f;
|
||||
|
||||
private final int[] COLORS = new int[] {
|
||||
Color.BLACK
|
||||
};
|
||||
|
||||
/** The duration of a single progress spin in milliseconds. */
|
||||
private static final int ANIMATION_DURATION = 1000 * 80 / 60;
|
||||
|
||||
/** The number of points in the progress "star". */
|
||||
private static final float NUM_POINTS = 5f;
|
||||
/** The list of animators operating on this drawable. */
|
||||
private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();
|
||||
|
||||
/** The indicator ring, used to manage animation state. */
|
||||
private final Ring mRing;
|
||||
|
||||
/** Canvas rotation in degrees. */
|
||||
private float mRotation;
|
||||
|
||||
/** Layout info for the arrowhead in dp */
|
||||
private static final int ARROW_WIDTH = 10;
|
||||
private static final int ARROW_HEIGHT = 5;
|
||||
private static final float ARROW_OFFSET_ANGLE = 5;
|
||||
|
||||
/** Layout info for the arrowhead for the large spinner in dp */
|
||||
private static final int ARROW_WIDTH_LARGE = 12;
|
||||
private static final int ARROW_HEIGHT_LARGE = 6;
|
||||
private static final float MAX_PROGRESS_ARC = .8f;
|
||||
|
||||
private Resources mResources;
|
||||
private View mParent;
|
||||
private Animation mAnimation;
|
||||
private float mRotationCount;
|
||||
private double mWidth;
|
||||
private double mHeight;
|
||||
boolean mFinishing;
|
||||
|
||||
public MaterialProgressDrawable(Context context, View parent) {
|
||||
mParent = parent;
|
||||
mResources = context.getResources();
|
||||
|
||||
mRing = new Ring(mCallback);
|
||||
mRing.setColors(COLORS);
|
||||
|
||||
updateSizes(DEFAULT);
|
||||
setupAnimators();
|
||||
}
|
||||
|
||||
private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
|
||||
double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
|
||||
final Ring ring = mRing;
|
||||
final DisplayMetrics metrics = mResources.getDisplayMetrics();
|
||||
final float screenDensity = metrics.density;
|
||||
|
||||
mWidth = progressCircleWidth * screenDensity;
|
||||
mHeight = progressCircleHeight * screenDensity;
|
||||
ring.setStrokeWidth((float) strokeWidth * screenDensity);
|
||||
ring.setCenterRadius(centerRadius * screenDensity);
|
||||
ring.setColorIndex(0);
|
||||
ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
|
||||
ring.setInsets((int) mWidth, (int) mHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the overall size for the progress spinner. This updates the radius
|
||||
* and stroke width of the ring.
|
||||
*
|
||||
* @param size One of {@link #LARGE} or
|
||||
* {@link #DEFAULT}
|
||||
*/
|
||||
public void updateSizes(@ProgressDrawableSize int size) {
|
||||
if (size == LARGE) {
|
||||
setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
|
||||
STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
|
||||
} else {
|
||||
setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
|
||||
ARROW_WIDTH, ARROW_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param show Set to true to display the arrowhead on the progress spinner.
|
||||
*/
|
||||
public void showArrow(boolean show) {
|
||||
mRing.setShowArrow(show);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale Set the scale of the arrowhead for the spinner.
|
||||
*/
|
||||
public void setArrowScale(float scale) {
|
||||
mRing.setArrowScale(scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start and end trim for the progress spinner arc.
|
||||
*
|
||||
* @param startAngle start angle
|
||||
* @param endAngle end angle
|
||||
*/
|
||||
public void setStartEndTrim(float startAngle, float endAngle) {
|
||||
mRing.setStartTrim(startAngle);
|
||||
mRing.setEndTrim(endAngle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of rotation to apply to the progress spinner.
|
||||
*
|
||||
* @param rotation Rotation is from [0..1]
|
||||
*/
|
||||
public void setProgressRotation(float rotation) {
|
||||
mRing.setRotation(rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the background color of the circle image view.
|
||||
*/
|
||||
public void setBackgroundColor(int color) {
|
||||
mRing.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors used in the progress animation from color resources.
|
||||
* The first color will also be the color of the bar that grows in response
|
||||
* to a user swipe gesture.
|
||||
*
|
||||
* @param colors
|
||||
*/
|
||||
public void setColorSchemeColors(int... colors) {
|
||||
mRing.setColors(colors);
|
||||
mRing.setColorIndex(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return (int) mHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return (int) mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas c) {
|
||||
final Rect bounds = getBounds();
|
||||
final int saveCount = c.save();
|
||||
c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
|
||||
mRing.draw(c, bounds);
|
||||
c.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mRing.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public int getAlpha() {
|
||||
return mRing.getAlpha();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mRing.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
void setRotation(float rotation) {
|
||||
mRotation = rotation;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private float getRotation() {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
final ArrayList<Animation> animators = mAnimators;
|
||||
final int N = animators.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final Animation animator = animators.get(i);
|
||||
if (animator.hasStarted() && !animator.hasEnded()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
mAnimation.reset();
|
||||
mRing.storeOriginals();
|
||||
// Already showing some part of the ring
|
||||
if (mRing.getEndTrim() != mRing.getStartTrim()) {
|
||||
mFinishing = true;
|
||||
mAnimation.setDuration(ANIMATION_DURATION/2);
|
||||
mParent.startAnimation(mAnimation);
|
||||
} else {
|
||||
mRing.setColorIndex(0);
|
||||
mRing.resetOriginals();
|
||||
mAnimation.setDuration(ANIMATION_DURATION);
|
||||
mParent.startAnimation(mAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
mParent.clearAnimation();
|
||||
setRotation(0);
|
||||
mRing.setShowArrow(false);
|
||||
mRing.setColorIndex(0);
|
||||
mRing.resetOriginals();
|
||||
}
|
||||
|
||||
private void applyFinishTranslation(float interpolatedTime, Ring ring) {
|
||||
// shrink back down and complete a full rotation before
|
||||
// starting other circles
|
||||
// Rotation goes between [0..1].
|
||||
float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
|
||||
+ 1f);
|
||||
final float startTrim = ring.getStartingStartTrim()
|
||||
+ (ring.getStartingEndTrim() - ring.getStartingStartTrim()) * interpolatedTime;
|
||||
ring.setStartTrim(startTrim);
|
||||
final float rotation = ring.getStartingRotation()
|
||||
+ ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
|
||||
ring.setRotation(rotation);
|
||||
}
|
||||
|
||||
private void setupAnimators() {
|
||||
final Ring ring = mRing;
|
||||
final Animation animation = new Animation() {
|
||||
@Override
|
||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
if (mFinishing) {
|
||||
applyFinishTranslation(interpolatedTime, ring);
|
||||
} else {
|
||||
// The minProgressArc is calculated from 0 to create an
|
||||
// angle that
|
||||
// matches the stroke width.
|
||||
final float minProgressArc = (float) Math.toRadians(
|
||||
ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));
|
||||
final float startingEndTrim = ring.getStartingEndTrim();
|
||||
final float startingTrim = ring.getStartingStartTrim();
|
||||
final float startingRotation = ring.getStartingRotation();
|
||||
|
||||
// Offset the minProgressArc to where the endTrim is
|
||||
// located.
|
||||
final float minArc = MAX_PROGRESS_ARC - minProgressArc;
|
||||
final float endTrim = startingEndTrim + (minArc
|
||||
* START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));
|
||||
ring.setEndTrim(endTrim);
|
||||
|
||||
final float startTrim = startingTrim + (MAX_PROGRESS_ARC
|
||||
* END_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));
|
||||
ring.setStartTrim(startTrim);
|
||||
|
||||
final float rotation = startingRotation + (0.25f * interpolatedTime);
|
||||
ring.setRotation(rotation);
|
||||
|
||||
float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime)
|
||||
+ (720.0f * (mRotationCount / NUM_POINTS));
|
||||
setRotation(groupRotation);
|
||||
}
|
||||
}
|
||||
};
|
||||
animation.setRepeatCount(Animation.INFINITE);
|
||||
animation.setRepeatMode(Animation.RESTART);
|
||||
animation.setInterpolator(LINEAR_INTERPOLATOR);
|
||||
animation.setAnimationListener(new Animation.AnimationListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
mRotationCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
ring.storeOriginals();
|
||||
ring.goToNextColor();
|
||||
ring.setStartTrim(ring.getEndTrim());
|
||||
if (mFinishing) {
|
||||
// finished closing the last ring from the swipe gesture; go
|
||||
// into progress mode
|
||||
mFinishing = false;
|
||||
animation.setDuration(ANIMATION_DURATION);
|
||||
ring.setShowArrow(false);
|
||||
} else {
|
||||
mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
|
||||
}
|
||||
}
|
||||
});
|
||||
mAnimation = animation;
|
||||
}
|
||||
|
||||
private final Callback mCallback = new Callback() {
|
||||
@Override
|
||||
public void invalidateDrawable(Drawable d) {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleDrawable(Drawable d, Runnable what, long when) {
|
||||
scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unscheduleDrawable(Drawable d, Runnable what) {
|
||||
unscheduleSelf(what);
|
||||
}
|
||||
};
|
||||
|
||||
private static class Ring {
|
||||
private final RectF mTempBounds = new RectF();
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Paint mArrowPaint = new Paint();
|
||||
|
||||
private final Callback mCallback;
|
||||
|
||||
private float mStartTrim = 0.0f;
|
||||
private float mEndTrim = 0.0f;
|
||||
private float mRotation = 0.0f;
|
||||
private float mStrokeWidth = 5.0f;
|
||||
private float mStrokeInset = 2.5f;
|
||||
|
||||
private int[] mColors;
|
||||
// mColorIndex represents the offset into the available mColors that the
|
||||
// progress circle should currently display. As the progress circle is
|
||||
// animating, the mColorIndex moves by one to the next available color.
|
||||
private int mColorIndex;
|
||||
private float mStartingStartTrim;
|
||||
private float mStartingEndTrim;
|
||||
private float mStartingRotation;
|
||||
private boolean mShowArrow;
|
||||
private Path mArrow;
|
||||
private float mArrowScale;
|
||||
private double mRingCenterRadius;
|
||||
private int mArrowWidth;
|
||||
private int mArrowHeight;
|
||||
private int mAlpha;
|
||||
private final Paint mCirclePaint = new Paint();
|
||||
private int mBackgroundColor;
|
||||
|
||||
public Ring(Callback callback) {
|
||||
mCallback = callback;
|
||||
|
||||
mPaint.setStrokeCap(Paint.Cap.SQUARE);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStyle(Style.STROKE);
|
||||
|
||||
mArrowPaint.setStyle(Style.FILL);
|
||||
mArrowPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
public void setBackgroundColor(int color) {
|
||||
mBackgroundColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dimensions of the arrowhead.
|
||||
*
|
||||
* @param width Width of the hypotenuse of the arrow head
|
||||
* @param height Height of the arrow point
|
||||
*/
|
||||
public void setArrowDimensions(float width, float height) {
|
||||
mArrowWidth = (int) width;
|
||||
mArrowHeight = (int) height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the progress spinner
|
||||
*/
|
||||
public void draw(Canvas c, Rect bounds) {
|
||||
final RectF arcBounds = mTempBounds;
|
||||
arcBounds.set(bounds);
|
||||
arcBounds.inset(mStrokeInset, mStrokeInset);
|
||||
|
||||
final float startAngle = (mStartTrim + mRotation) * 360;
|
||||
final float endAngle = (mEndTrim + mRotation) * 360;
|
||||
float sweepAngle = endAngle - startAngle;
|
||||
|
||||
mPaint.setColor(mColors[mColorIndex]);
|
||||
c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
|
||||
|
||||
drawTriangle(c, startAngle, sweepAngle, bounds);
|
||||
|
||||
if (mAlpha < 255) {
|
||||
mCirclePaint.setColor(mBackgroundColor);
|
||||
mCirclePaint.setAlpha(255 - mAlpha);
|
||||
c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
|
||||
mCirclePaint);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
|
||||
if (mShowArrow) {
|
||||
if (mArrow == null) {
|
||||
mArrow = new Path();
|
||||
mArrow.setFillType(Path.FillType.EVEN_ODD);
|
||||
} else {
|
||||
mArrow.reset();
|
||||
}
|
||||
|
||||
// Adjust the position of the triangle so that it is inset as
|
||||
// much as the arc, but also centered on the arc.
|
||||
float inset = (int) mStrokeInset / 2 * mArrowScale;
|
||||
float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
|
||||
float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
|
||||
|
||||
// Update the path each time. This works around an issue in SKIA
|
||||
// where concatenating a rotation matrix to a scale matrix
|
||||
// ignored a starting negative rotation. This appears to have
|
||||
// been fixed as of API 21.
|
||||
mArrow.moveTo(0, 0);
|
||||
mArrow.lineTo(mArrowWidth * mArrowScale, 0);
|
||||
mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
|
||||
* mArrowScale));
|
||||
mArrow.offset(x - inset, y);
|
||||
mArrow.close();
|
||||
// draw a triangle
|
||||
mArrowPaint.setColor(mColors[mColorIndex]);
|
||||
c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
|
||||
bounds.exactCenterY());
|
||||
c.drawPath(mArrow, mArrowPaint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the colors the progress spinner alternates between.
|
||||
*
|
||||
* @param colors Array of integers describing the colors. Must be non-<code>null</code>.
|
||||
*/
|
||||
public void setColors(@NonNull int[] colors) {
|
||||
mColors = colors;
|
||||
// if colors are reset, make sure to reset the color index as well
|
||||
setColorIndex(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index Index into the color array of the color to display in
|
||||
* the progress spinner.
|
||||
*/
|
||||
public void setColorIndex(int index) {
|
||||
mColorIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceed to the next available ring color. This will automatically
|
||||
* wrap back to the beginning of colors.
|
||||
*/
|
||||
public void goToNextColor() {
|
||||
mColorIndex = (mColorIndex + 1) % (mColors.length);
|
||||
}
|
||||
|
||||
public void setColorFilter(ColorFilter filter) {
|
||||
mPaint.setColorFilter(filter);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alpha Set the alpha of the progress spinner and associated arrowhead.
|
||||
*/
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current alpha of the progress spinner and arrowhead.
|
||||
*/
|
||||
public int getAlpha() {
|
||||
return mAlpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param strokeWidth Set the stroke width of the progress spinner in pixels.
|
||||
*/
|
||||
public void setStrokeWidth(float strokeWidth) {
|
||||
mStrokeWidth = strokeWidth;
|
||||
mPaint.setStrokeWidth(strokeWidth);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getStrokeWidth() {
|
||||
return mStrokeWidth;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setStartTrim(float startTrim) {
|
||||
mStartTrim = startTrim;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getStartTrim() {
|
||||
return mStartTrim;
|
||||
}
|
||||
|
||||
public float getStartingStartTrim() {
|
||||
return mStartingStartTrim;
|
||||
}
|
||||
|
||||
public float getStartingEndTrim() {
|
||||
return mStartingEndTrim;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setEndTrim(float endTrim) {
|
||||
mEndTrim = endTrim;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getEndTrim() {
|
||||
return mEndTrim;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setRotation(float rotation) {
|
||||
mRotation = rotation;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getRotation() {
|
||||
return mRotation;
|
||||
}
|
||||
|
||||
public void setInsets(int width, int height) {
|
||||
final float minEdge = (float) Math.min(width, height);
|
||||
float insets;
|
||||
if (mRingCenterRadius <= 0 || minEdge < 0) {
|
||||
insets = (float) Math.ceil(mStrokeWidth / 2.0f);
|
||||
} else {
|
||||
insets = (float) (minEdge / 2.0f - mRingCenterRadius);
|
||||
}
|
||||
mStrokeInset = insets;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public float getInsets() {
|
||||
return mStrokeInset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param centerRadius Inner radius in px of the circle the progress
|
||||
* spinner arc traces.
|
||||
*/
|
||||
public void setCenterRadius(double centerRadius) {
|
||||
mRingCenterRadius = centerRadius;
|
||||
}
|
||||
|
||||
public double getCenterRadius() {
|
||||
return mRingCenterRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param show Set to true to show the arrow head on the progress spinner.
|
||||
*/
|
||||
public void setShowArrow(boolean show) {
|
||||
if (mShowArrow != show) {
|
||||
mShowArrow = show;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scale Set the scale of the arrowhead for the spinner.
|
||||
*/
|
||||
public void setArrowScale(float scale) {
|
||||
if (scale != mArrowScale) {
|
||||
mArrowScale = scale;
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The amount the progress spinner is currently rotated, between [0..1].
|
||||
*/
|
||||
public float getStartingRotation() {
|
||||
return mStartingRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the start / end trim are offset to begin with, store them so that
|
||||
* animation starts from that offset.
|
||||
*/
|
||||
public void storeOriginals() {
|
||||
mStartingStartTrim = mStartTrim;
|
||||
mStartingEndTrim = mEndTrim;
|
||||
mStartingRotation = mRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the progress spinner to default rotation, start and end angles.
|
||||
*/
|
||||
public void resetOriginals() {
|
||||
mStartingStartTrim = 0;
|
||||
mStartingEndTrim = 0;
|
||||
mStartingRotation = 0;
|
||||
setStartTrim(0);
|
||||
setEndTrim(0);
|
||||
setRotation(0);
|
||||
}
|
||||
|
||||
private void invalidateSelf() {
|
||||
mCallback.invalidateDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Squishes the interpolation curve into the second half of the animation.
|
||||
*/
|
||||
private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator {
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Squishes the interpolation curve into the first half of the animation.
|
||||
*/
|
||||
private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator {
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
return super.getInterpolation(Math.min(1, input * 2.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.widget.refreshlayout;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
|
||||
/**
|
||||
* Custom progress bar that shows a cycle of colors as widening circles that
|
||||
* overdraw each other. When finished, the bar is cleared from the inside out as
|
||||
* the main cycle continues. Before running, this can also indicate how close
|
||||
* the user is to triggering something (e.g. how far they need to pull down to
|
||||
* trigger a refresh).
|
||||
*/
|
||||
final class SwipeProgressBar {
|
||||
|
||||
// Default progress animation colors are grays.
|
||||
private final static int COLOR1 = 0xB3000000;
|
||||
private final static int COLOR2 = 0x80000000;
|
||||
private final static int COLOR3 = 0x4d000000;
|
||||
private final static int COLOR4 = 0x1a000000;
|
||||
|
||||
// The duration of the animation cycle.
|
||||
private static final int ANIMATION_DURATION_MS = 2000;
|
||||
|
||||
// The duration of the animation to clear the bar.
|
||||
private static final int FINISH_ANIMATION_DURATION_MS = 1000;
|
||||
|
||||
// Interpolator for varying the speed of the animation.
|
||||
private static final Interpolator INTERPOLATOR = BakedBezierInterpolator.getInstance();
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
private final RectF mClipRect = new RectF();
|
||||
private float mTriggerPercentage;
|
||||
private long mStartTime;
|
||||
private long mFinishTime;
|
||||
private boolean mRunning;
|
||||
|
||||
// Colors used when rendering the animation,
|
||||
private int mColor1;
|
||||
private int mColor2;
|
||||
private int mColor3;
|
||||
private int mColor4;
|
||||
private View mParent;
|
||||
|
||||
private Rect mBounds = new Rect();
|
||||
|
||||
public SwipeProgressBar(View parent) {
|
||||
mParent = parent;
|
||||
mColor1 = COLOR1;
|
||||
mColor2 = COLOR2;
|
||||
mColor3 = COLOR3;
|
||||
mColor4 = COLOR4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the four colors used in the progress animation. The first color will
|
||||
* also be the color of the bar that grows in response to a user swipe
|
||||
* gesture.
|
||||
*
|
||||
* @param color1 Integer representation of a color.
|
||||
* @param color2 Integer representation of a color.
|
||||
* @param color3 Integer representation of a color.
|
||||
* @param color4 Integer representation of a color.
|
||||
*/
|
||||
void setColorScheme(int color1, int color2, int color3, int color4) {
|
||||
mColor1 = color1;
|
||||
mColor2 = color2;
|
||||
mColor3 = color3;
|
||||
mColor4 = color4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the progress the user has made toward triggering the swipe
|
||||
* gesture. and use this value to update the percentage of the trigger that
|
||||
* is shown.
|
||||
*/
|
||||
void setTriggerPercentage(float triggerPercentage) {
|
||||
mTriggerPercentage = triggerPercentage;
|
||||
mStartTime = 0;
|
||||
ViewCompat.postInvalidateOnAnimation(
|
||||
mParent, mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start showing the progress animation.
|
||||
*/
|
||||
void start() {
|
||||
if (!mRunning) {
|
||||
mTriggerPercentage = 0;
|
||||
mStartTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
mRunning = true;
|
||||
mParent.postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop showing the progress animation.
|
||||
*/
|
||||
void stop() {
|
||||
if (mRunning) {
|
||||
mTriggerPercentage = 0;
|
||||
mFinishTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
mRunning = false;
|
||||
mParent.postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Return whether the progress animation is currently running.
|
||||
*/
|
||||
boolean isRunning() {
|
||||
return mRunning || mFinishTime > 0;
|
||||
}
|
||||
|
||||
void draw(Canvas canvas) {
|
||||
final int width = mBounds.width();
|
||||
final int height = mBounds.height();
|
||||
final int cx = width / 2;
|
||||
final int cy = height / 2;
|
||||
boolean drawTriggerWhileFinishing = false;
|
||||
int restoreCount = canvas.save();
|
||||
canvas.clipRect(mBounds);
|
||||
|
||||
if (mRunning || (mFinishTime > 0)) {
|
||||
long now = AnimationUtils.currentAnimationTimeMillis();
|
||||
long elapsed = (now - mStartTime) % ANIMATION_DURATION_MS;
|
||||
long iterations = (now - mStartTime) / ANIMATION_DURATION_MS;
|
||||
float rawProgress = (elapsed / (ANIMATION_DURATION_MS / 100f));
|
||||
|
||||
// If we're not running anymore, that means we're running through
|
||||
// the finish animation.
|
||||
if (!mRunning) {
|
||||
// If the finish animation is done, don't draw anything, and
|
||||
// don't repost.
|
||||
if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
|
||||
mFinishTime = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use a 0 opacity alpha layer to clear the animation
|
||||
// from the inside out. This layer will prevent the circles from
|
||||
// drawing within its bounds.
|
||||
long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
|
||||
float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
|
||||
float pct = (finishProgress / 100f);
|
||||
// Radius of the circle is half of the screen.
|
||||
float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
|
||||
mClipRect.set(cx - clearRadius, 0, cx + clearRadius, height);
|
||||
canvas.saveLayerAlpha(mClipRect, 0, 0);
|
||||
// Only draw the trigger if there is a space in the center of
|
||||
// this refreshing view that needs to be filled in by the
|
||||
// trigger. If the progress view is just still animating, let it
|
||||
// continue animating.
|
||||
drawTriggerWhileFinishing = true;
|
||||
}
|
||||
|
||||
// First fill in with the last color that would have finished drawing.
|
||||
if (iterations == 0) {
|
||||
canvas.drawColor(mColor1);
|
||||
} else {
|
||||
if (rawProgress >= 0 && rawProgress < 25) {
|
||||
canvas.drawColor(mColor4);
|
||||
} else if (rawProgress >= 25 && rawProgress < 50) {
|
||||
canvas.drawColor(mColor1);
|
||||
} else if (rawProgress >= 50 && rawProgress < 75) {
|
||||
canvas.drawColor(mColor2);
|
||||
} else {
|
||||
canvas.drawColor(mColor3);
|
||||
}
|
||||
}
|
||||
|
||||
// Then draw up to 4 overlapping concentric circles of varying radii, based on how far
|
||||
// along we are in the cycle.
|
||||
// progress 0-50 draw mColor2
|
||||
// progress 25-75 draw mColor3
|
||||
// progress 50-100 draw mColor4
|
||||
// progress 75 (wrap to 25) draw mColor1
|
||||
if ((rawProgress >= 0 && rawProgress <= 25)) {
|
||||
float pct = (((rawProgress + 25) * 2) / 100f);
|
||||
drawCircle(canvas, cx, cy, mColor1, pct);
|
||||
}
|
||||
if (rawProgress >= 0 && rawProgress <= 50) {
|
||||
float pct = ((rawProgress * 2) / 100f);
|
||||
drawCircle(canvas, cx, cy, mColor2, pct);
|
||||
}
|
||||
if (rawProgress >= 25 && rawProgress <= 75) {
|
||||
float pct = (((rawProgress - 25) * 2) / 100f);
|
||||
drawCircle(canvas, cx, cy, mColor3, pct);
|
||||
}
|
||||
if (rawProgress >= 50 && rawProgress <= 100) {
|
||||
float pct = (((rawProgress - 50) * 2) / 100f);
|
||||
drawCircle(canvas, cx, cy, mColor4, pct);
|
||||
}
|
||||
if ((rawProgress >= 75 && rawProgress <= 100)) {
|
||||
float pct = (((rawProgress - 75) * 2) / 100f);
|
||||
drawCircle(canvas, cx, cy, mColor1, pct);
|
||||
}
|
||||
if (mTriggerPercentage > 0 && drawTriggerWhileFinishing) {
|
||||
// There is some portion of trigger to draw. Restore the canvas,
|
||||
// then draw the trigger. Otherwise, the trigger does not appear
|
||||
// until after the bar has finished animating and appears to
|
||||
// just jump in at a larger width than expected.
|
||||
canvas.restoreToCount(restoreCount);
|
||||
restoreCount = canvas.save();
|
||||
canvas.clipRect(mBounds);
|
||||
drawTrigger(canvas, cx, cy);
|
||||
}
|
||||
// Keep running until we finish out the last cycle.
|
||||
ViewCompat.postInvalidateOnAnimation(
|
||||
mParent, mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
|
||||
} else {
|
||||
// Otherwise if we're in the middle of a trigger, draw that.
|
||||
if (mTriggerPercentage > 0 && mTriggerPercentage <= 1.0) {
|
||||
drawTrigger(canvas, cx, cy);
|
||||
}
|
||||
}
|
||||
canvas.restoreToCount(restoreCount);
|
||||
}
|
||||
|
||||
private void drawTrigger(Canvas canvas, int cx, int cy) {
|
||||
mPaint.setColor(mColor1);
|
||||
canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a circle centered in the view.
|
||||
*
|
||||
* @param canvas the canvas to draw on
|
||||
* @param cx the center x coordinate
|
||||
* @param cy the center y coordinate
|
||||
* @param color the color to draw
|
||||
* @param pct the percentage of the view that the circle should cover
|
||||
*/
|
||||
private void drawCircle(Canvas canvas, float cx, float cy, int color, float pct) {
|
||||
mPaint.setColor(color);
|
||||
canvas.save();
|
||||
canvas.translate(cx, mParent.getHeight() - cy);
|
||||
float radiusScale = INTERPOLATOR.getInterpolation(pct);
|
||||
canvas.scale(radiusScale, radiusScale);
|
||||
canvas.drawCircle(0, 0, cx, mPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawing bounds of this SwipeProgressBar.
|
||||
*/
|
||||
void setBounds(int left, int top, int right, int bottom) {
|
||||
mBounds.left = left;
|
||||
mBounds.top = top;
|
||||
mBounds.right = right;
|
||||
mBounds.bottom = bottom;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user