package com.lcodecore.tkrefreshlayout.header.progresslayout;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.FrameLayout;
import com.lcodecore.tkrefreshlayout.IHeaderView;
import com.lcodecore.tkrefreshlayout.OnAnimEndListener;
/**
* Created by lcodecore on 2016/11/27.
*/
public class ProgressLayout extends FrameLayout implements IHeaderView {
private int mCircleWidth;
private int mCircleHeight;
private static final int CIRCLE_DIAMETER = 40;
private static final int CIRCLE_DIAMETER_LARGE = 56;
// Maps to ProgressBar.Large style
public static final int LARGE = MaterialProgressDrawable.LARGE;
// Maps to ProgressBar default style
public static final int DEFAULT = MaterialProgressDrawable.DEFAULT;
// Default background for the progress spinner
private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA;
// Default offset in dips from the top of the view to where the progress spinner should stop
private static final int DEFAULT_CIRCLE_TARGET = 64;
private static final float MAX_PROGRESS_ANGLE = .8f;
private static final int MAX_ALPHA = 255;
private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA);
private CircleImageView mCircleView;
private MaterialProgressDrawable mProgress;
public ProgressLayout(Context context) {
this(context, null);
}
public ProgressLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final DisplayMetrics metrics = getResources().getDisplayMetrics();
mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density);
mCircleHeight = (int) (CIRCLE_DIAMETER * metrics.density);
createProgressView();
ViewCompat.setChildrenDrawingOrderEnabled(this, true);
// the absolute offset has to take into account that the circle starts at an offset
}
private void createProgressView() {
mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER / 2);
mProgress = new MaterialProgressDrawable(getContext(), this);
mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
mCircleView.setImageDrawable(mProgress);
mCircleView.setVisibility(View.GONE);
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
mCircleView.setLayoutParams(params);
addView(mCircleView);
}
/**
* Set the background color of the progress spinner disc.
*
* @param colorRes Resource id of the color.
*/
public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) {
setProgressBackgroundColorSchemeColor(getResources().getColor(colorRes));
}
/**
* Set the background color of the progress spinner disc.
*
* @param color
*/
public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
mCircleView.setBackgroundColor(color);
mProgress.setBackgroundColor(color);
}
/**
* Set the color resources 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 colorResIds
*/
public void setColorSchemeResources(@ColorRes int... colorResIds) {
final Resources res = getResources();
int[] colorRes = new int[colorResIds.length];
for (int i = 0; i < colorResIds.length; i++) {
colorRes[i] = res.getColor(colorResIds[i]);
}
setColorSchemeColors(colorRes);
}
/**
* Set the 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 colors
*/
public void setColorSchemeColors(int... colors) {
mProgress.setColorSchemeColors(colors);
}
/**
* One of DEFAULT, or LARGE.
*/
public void setSize(int size) {
if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) {
return;
}
final DisplayMetrics metrics = getResources().getDisplayMetrics();
if (size == MaterialProgressDrawable.LARGE) {
mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);
} else {
mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density);
}
// force the bounds of the progress circle inside the circle view to
// update by setting it to null before updating its size and then
// re-setting it
mCircleView.setImageDrawable(null);
mProgress.updateSizes(size);
mCircleView.setImageDrawable(mProgress);
}
@Override
public void reset() {
mCircleView.clearAnimation();
mProgress.stop();
mCircleView.setVisibility(View.GONE);
mCircleView.getBackground().setAlpha(MAX_ALPHA);
mProgress.setAlpha(MAX_ALPHA);
ViewCompat.setScaleX(mCircleView, 0);
ViewCompat.setScaleY(mCircleView, 0);
ViewCompat.setAlpha(mCircleView, 1);
}
@Override
public View getView() {
return this;
}
private boolean mIsBeingDragged = false;
@Override
public void onPullingDown(float fraction, float maxHeadHeight, float headHeight) {
if (!mIsBeingDragged) {
mIsBeingDragged = true;
mProgress.setAlpha(STARTING_PROGRESS_ALPHA);
}
if (mCircleView.getVisibility() != View.VISIBLE) {
mCircleView.setVisibility(View.VISIBLE);
}
if (fraction >= 1f) {
ViewCompat.setScaleX(mCircleView, 1f);
ViewCompat.setScaleY(mCircleView, 1f);
} else {
ViewCompat.setScaleX(mCircleView, fraction);
ViewCompat.setScaleY(mCircleView, fraction);
}
if (fraction <= 1f) {
mProgress.setAlpha((int) (STARTING_PROGRESS_ALPHA + (MAX_ALPHA - STARTING_PROGRESS_ALPHA) * fraction));
}
float adjustedPercent = (float) Math.max(fraction - .4, 0) * 5 / 3;
float strokeStart = adjustedPercent * .8f;
mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
mProgress.setArrowScale(Math.min(1f, adjustedPercent));
float rotation = (-0.25f + .4f * adjustedPercent) * .5f;
mProgress.setProgressRotation(rotation);
}
@Override
public void onPullReleasing(float fraction, float maxHeadHeight, float headHeight) {
mIsBeingDragged = false;
if (fraction >= 1f) {
ViewCompat.setScaleX(mCircleView, 1f);
ViewCompat.setScaleY(mCircleView, 1f);
} else {
ViewCompat.setScaleX(mCircleView, fraction);
ViewCompat.setScaleY(mCircleView, fraction);
}
}
@Override
public void startAnim(float maxHeadHeight, float headHeight) {
mCircleView.setVisibility(View.VISIBLE);
mCircleView.getBackground().setAlpha(MAX_ALPHA);
mProgress.setAlpha(MAX_ALPHA);
ViewCompat.setScaleX(mCircleView, 1f);
ViewCompat.setScaleY(mCircleView, 1f);
mProgress.setArrowScale(1f);
mProgress.start();
}
@Override
public void onFinish(final OnAnimEndListener animEndListener) {
mCircleView.animate().scaleX(0).scaleY(0).alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
reset();
animEndListener.onAnimEnd();
}
}).start();
}
}