Last active
August 20, 2017 02:28
-
-
Save gnumilanix/8dc73956b8d46caa8423341553bb0d2f to your computer and use it in GitHub Desktop.
Implementation of FrameLayout that can show progress with in the entire layout
This file contains 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="ProgressLayout"> | |
<attr name="progress" format="integer" /> | |
<attr name="progressColor" format="color" /> | |
<attr name="progressCorners" format="dimension" /> | |
<attr name="progressDuration" format="integer" /> | |
</declare-styleable> | |
</resources> |
This file contains 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
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