Skip to content

Instantly share code, notes, and snippets.

@gnumilanix
Last active August 20, 2017 02:28
Show Gist options
  • Save gnumilanix/8dc73956b8d46caa8423341553bb0d2f to your computer and use it in GitHub Desktop.
Save gnumilanix/8dc73956b8d46caa8423341553bb0d2f to your computer and use it in GitHub Desktop.
Implementation of FrameLayout that can show progress with in the entire layout
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProgressLayout">
<attr name="progress" format="integer" />
<attr name="progressColor" format="color" />
<attr name="progressCorners" format="dimension" />
<attr name="progressDuration" format="integer" />
</declare-styleable>
</resources>
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Property;
import android.widget.FrameLayout;
import com.milanix.view.R;
/**
* Implementation of {@link FrameLayout} that can show progress with in the layout
*
* @author milan
*/
public class ProgressLayout extends FrameLayout {
private final TypeEvaluator<Float> typeEvaluator = (fraction, startValue, endValue) -> fraction * 100;
private final Property<ProgressLayout, Float> property = Property.of(ProgressLayout.class, Float.class, "progress");
private final AnimatorListenerAdapter progressListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setEnabled(false);
}
@Override
public void onAnimationEnd(Animator animation) {
setProgress(100);
}
@Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
};
private ObjectAnimator progressAnimator;
private int progressColor;
private Path clipPath;
private Paint progressBar;
private RectF progressRect;
private int cornerRadius;
private float progress;
private long progressDuration;
private OnProgressChangeListener progressChangeListener;
private OnProgressCompleteListener progressCompleteListener;
public ProgressLayout(Context context) {
this(context, null);
}
public ProgressLayout(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public ProgressLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setAttributes(context, attrs);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public ProgressLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setAttributes(context, attrs);
init();
}
private void setAttributes(Context context, AttributeSet attrs) {
if (attrs != null) {
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressLayout);
progressColor = array.getColor(R.styleable.ProgressLayout_progressColor, 0);
cornerRadius = array.getDimensionPixelSize(R.styleable.ProgressLayout_progressCorners, 0);
progress = array.getInt(R.styleable.ProgressLayout_progress, 0);
progressDuration = array.getInt(R.styleable.ProgressLayout_progressDuration, 0);
array.recycle();
}
}
private void init() {
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, null);
clipPath = new Path();
progressBar = new Paint();
progressBar.setColor(progressColor);
progressBar.setStyle(Paint.Style.FILL);
progressBar.setAntiAlias(true);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startProgress();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
cancelProgress();
}
@Override
protected void dispatchDraw(Canvas canvas) {
progressRect.right = progress * getWidth() / 100;
canvas.clipPath(clipPath);
canvas.drawRect(progressRect, progressBar);
super.dispatchDraw(canvas);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
final RectF clipRect = new RectF(0, 0, w, h);
progressRect = new RectF(clipRect);
clipPath.reset();
clipPath.addRoundRect(clipRect, cornerRadius, cornerRadius, Path.Direction.CW);
}
/**
* Sets total progress duration
*
* @param progressDuration to set
*/
public void setProgressDuration(@IntRange(from = 100) int progressDuration) {
this.progressDuration = progressDuration;
startProgress();
}
/**
* Cancels the progress
*/
public void cancelProgress() {
setEnabled(true);
cancelProgressAnimation();
}
/**
* Cancels ongoing progress animation
*/
private void cancelProgressAnimation() {
if (null != progressAnimator) {
progressAnimator.cancel();
}
}
/**
* Starts the progress
*/
public void startProgress() {
if (!isInEditMode()) {
cancelProgress();
startProgressAnimation();
}
}
/**
* Starts progress animation
*/
private void startProgressAnimation() {
progressAnimator = ObjectAnimator.ofObject(this, property, typeEvaluator, 100f);
progressAnimator.setDuration(progressDuration);
progressAnimator.addListener(progressListener);
progressAnimator.addUpdateListener(animation -> notifyProgressUpdate());
progressAnimator.start();
}
/**
* Notifies about the progress
*/
private void notifyProgressUpdate() {
if (null != progressChangeListener) {
progressChangeListener.onProgressChange(ProgressLayout.this, progress);
}
}
/**
* Returns current progress
*
* @return current progress
*/
public float getProgress() {
return progress;
}
/**
* Sets current progress
*
* @param progress to set
*/
public void setProgress(@FloatRange(from = 0f, to = 100f) float progress) {
this.progress = progress;
invalidate();
if (progress == 100) {
setEnabled(true);
notifyProgressComplete();
}
}
/**
* Notifies about progress completion
*/
private void notifyProgressComplete() {
if (null != progressCompleteListener) {
progressCompleteListener.onComplete(this);
}
}
/**
* Sets progress change listener
*
* @param progressChangeListener to set
*/
public void setProgressChangeListener(OnProgressChangeListener progressChangeListener) {
this.progressChangeListener = progressChangeListener;
}
/**
* Sets progress complete listener
*
* @param progressCompleteListener to set
*/
public void setProgressCompleteListener(OnProgressCompleteListener progressCompleteListener) {
this.progressCompleteListener = progressCompleteListener;
}
/**
* Interface to be implemented for listening to progress updates in this view
*/
public interface OnProgressChangeListener {
/**
* Invoked when progress has changed
*
* @param view current progress view
* @param progress that was updated to
*/
void onProgressChange(ProgressLayout view, float progress);
}
/**
* Interface to be implemented for listening to completion updates in this view
*/
public interface OnProgressCompleteListener {
/**
* Invoked when progress has completed
*
* @param view current progress view
*/
void onComplete(ProgressLayout view);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment