Last active
March 30, 2017 22:17
-
-
Save f3401pal/5aa18553a080e5592dfbcbb39dcfbaea to your computer and use it in GitHub Desktop.
BounceCircleLoadingView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="BounceCircleLoadingView"> | |
<attr name="pointColor" format="color" /> | |
<attr name="animationDuration" format="integer" /> | |
<attr name="pointSize" format="dimension" /> | |
<attr name="interpolator" format="enum"> | |
<enum name="fastOutSlowIn" value="0"/> | |
<enum name="anticipate_overshoot" value="1"/> | |
<enum name="overshoot" value="2"/> | |
<enum name="accelerateDecelerate" value="3"/> | |
</attr> | |
</declare-styleable> | |
</resources> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.f3401pal.shb.android.ui; | |
import android.animation.Animator; | |
import android.animation.AnimatorListenerAdapter; | |
import android.animation.AnimatorSet; | |
import android.animation.ObjectAnimator; | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Color; | |
import android.graphics.drawable.GradientDrawable; | |
import android.support.annotation.Nullable; | |
import android.support.v4.view.animation.FastOutSlowInInterpolator; | |
import android.util.AttributeSet; | |
import android.util.Log; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.animation.AccelerateDecelerateInterpolator; | |
import android.view.animation.AnticipateOvershootInterpolator; | |
import android.view.animation.Interpolator; | |
import android.view.animation.OvershootInterpolator; | |
import android.widget.ImageView; | |
import android.widget.LinearLayout; | |
import com.f3401pal.shb.R; | |
public class BounceCircleLoadingView extends LinearLayout { | |
private static final String TAG = BounceCircleLoadingView.class.getSimpleName(); | |
private static final int NUM_POINTS = 3; | |
private static final int DEFAULT_DURATION = 1000; | |
private static final int DEFAULT_POINT_COLOR = Color.WHITE; | |
private enum InterpolatorValues { | |
FAST_IN_FAST_OUT(new FastOutSlowInInterpolator()), | |
ANTICIPATE_OVERSHOOT(new AnticipateOvershootInterpolator()), | |
OVERSHOOT(new OvershootInterpolator()), | |
ACCELERATE_DECELERATE(new AccelerateDecelerateInterpolator()); | |
private Interpolator mInterpolator; | |
InterpolatorValues(Interpolator interpolator) { | |
this.mInterpolator = interpolator; | |
} | |
public Interpolator getInterpolator() { | |
return mInterpolator; | |
} | |
} | |
private ImageView[] points; | |
private int mDuration; | |
private Interpolator mInterpolator; | |
private int pointSize; | |
private float shootingHeight; | |
@Nullable | |
private AnimatorSet mCircleAnimator; | |
public BounceCircleLoadingView(Context context) { | |
super(context); | |
init(context, null); | |
} | |
public BounceCircleLoadingView(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
init(context, attrs); | |
} | |
public BounceCircleLoadingView(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
init(context, attrs); | |
} | |
private void init(Context context, AttributeSet attrs) { | |
setOrientation(HORIZONTAL); | |
points = new ImageView[NUM_POINTS]; | |
for(int i = 0 ; i < NUM_POINTS ; i++) { | |
ImageView point = (ImageView) LayoutInflater.from(context).inflate(R.layout.view_bounce_circle_loading, this, false); | |
points[i] = point; | |
addView(point); | |
} | |
if (attrs != null) { | |
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BounceCircleLoadingView); | |
int pointColor = a.getColor(R.styleable.BounceCircleLoadingView_pointColor, -1); | |
pointSize = a.getDimensionPixelSize(R.styleable.BounceCircleLoadingView_pointSize, 0); | |
for(ImageView circle : points) { | |
setUpCircleColors(circle, pointColor == -1 ? | |
DEFAULT_POINT_COLOR : pointColor); | |
if (pointSize > 0) { | |
setUpCircleSize(pointSize, circle); | |
} | |
} | |
int duration = a.getInt(R.styleable.BounceCircleLoadingView_animationDuration, -1); | |
int interpolator = a.getInt(R.styleable.BounceCircleLoadingView_interpolator, -1); | |
mInterpolator = interpolator == -1 ? mInterpolator = InterpolatorValues.FAST_IN_FAST_OUT.getInterpolator() : | |
InterpolatorValues.values()[interpolator].getInterpolator(); | |
mDuration = duration == -1 || duration < 0 ? DEFAULT_DURATION : duration; | |
a.recycle(); | |
} else { | |
for(ImageView circle : points) { | |
setUpCircleColors(circle, DEFAULT_POINT_COLOR); | |
} | |
mDuration = DEFAULT_DURATION; | |
} | |
} | |
private void startAnimation(int step, final ImageView... points) { | |
if(points.length < 2) { | |
throw new RuntimeException("Cannot animate with less than 2 points"); | |
} | |
if(!isShown()) { | |
return; | |
} | |
step = step >= points.length - 1 ? 0 : step; | |
ImageView leftPoint = points[step]; | |
ImageView rightPoint = points[step + 1]; | |
// left point shooting to the right from above | |
final ObjectAnimator leftPointAnimatorX = ObjectAnimator.ofFloat(leftPoint, View.X, leftPoint.getX(), rightPoint.getX()); | |
setupAnimator(leftPointAnimatorX); | |
final ObjectAnimator leftPointAnimatorY = ObjectAnimator.ofFloat(leftPoint, View.TRANSLATION_Y, 0, -shootingHeight, 0); | |
setupAnimator(leftPointAnimatorY); | |
Log.d(TAG, "left index=" + step + ", X: " + leftPoint.getX() + " -> " + rightPoint.getX()); | |
// right point move to left horizontally | |
final ObjectAnimator rightPointAnimatorX = ObjectAnimator.ofFloat(rightPoint, View.X, rightPoint.getX(), leftPoint.getX()); | |
setupAnimator(rightPointAnimatorX); | |
Log.d(TAG, "right index=" + (step + 1) + ", X: " + rightPoint.getX() + " -> " + leftPoint.getX()); | |
final int nextStep = step + 1; | |
mCircleAnimator = new AnimatorSet(); | |
mCircleAnimator.playTogether(leftPointAnimatorX, leftPointAnimatorY, rightPointAnimatorX); | |
mCircleAnimator.addListener(new AnimatorListenerAdapter() { | |
@Override | |
public void onAnimationEnd(Animator animation) { | |
super.onAnimationEnd(animation); | |
ImageView temp = points[nextStep - 1]; | |
points[nextStep - 1] = points[nextStep]; | |
points[nextStep] = temp; | |
startAnimation(nextStep, points); | |
} | |
}); | |
mCircleAnimator.start(); | |
} | |
private void setupAnimator(Animator animator) { | |
animator.setDuration(mDuration); | |
animator.setInterpolator(mInterpolator); | |
} | |
private void setUpCircleSize(int size, ImageView circle) { | |
LayoutParams params = (LayoutParams) circle.getLayoutParams(); | |
params.width = size; | |
params.height = size; | |
circle.setLayoutParams(params); | |
} | |
private void setUpCircleColors(ImageView circle, int color) { | |
GradientDrawable gradientDrawable = (GradientDrawable) circle.getDrawable(); | |
gradientDrawable.setColor(color); | |
} | |
private void stopAnimation() { | |
if (mCircleAnimator != null) { | |
mCircleAnimator.removeAllListeners(); | |
mCircleAnimator.cancel(); | |
mCircleAnimator = null; | |
} | |
} | |
//********************************************************* | |
// Lifecycle | |
//********************************************************* | |
@Override | |
protected void onLayout(boolean changed, int l, int t, int r, int b) { | |
super.onLayout(changed, l, t, r, b); | |
if(changed) { | |
stopAnimation(); | |
shootingHeight = (getMeasuredHeight() - pointSize) >> 1; | |
startAnimation(0, points); | |
} | |
} | |
@Override | |
protected void onDetachedFromWindow() { | |
super.onDetachedFromWindow(); | |
stopAnimation(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<ImageView | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_margin="@dimen/default_view_padding" | |
android:src="@drawable/point_circle" | |
/> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment