Back to project page Material.
The source code is released under:
Apache License
If you think the Android project Material listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.rey.material.drawable; // w w w . j ava2s .c o m import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathEffect; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.SystemClock; import android.util.AttributeSet; import android.util.TypedValue; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import com.rey.material.R; import com.rey.material.util.ColorUtil; import com.rey.material.util.ThemeUtil; import com.rey.material.util.ViewUtil; import com.rey.material.view.ProgressView; public class LinearProgressDrawable extends Drawable implements Animatable { private long mLastUpdateTime; private long mLastProgressStateTime; private long mLastRunStateTime; private int mProgressState; private static final int PROGRESS_STATE_STRETCH = 0; private static final int PROGRESS_STATE_KEEP_STRETCH = 1; private static final int PROGRESS_STATE_SHRINK = 2; private static final int PROGRESS_STATE_KEEP_SHRINK = 3; private int mRunState = RUN_STATE_STOPPED; private static final int RUN_STATE_STOPPED = 0; private static final int RUN_STATE_STARTING = 1; private static final int RUN_STATE_STARTED = 2; private static final int RUN_STATE_RUNNING = 3; private static final int RUN_STATE_STOPPING = 4; public static final int ALIGN_TOP = 0; public static final int ALIGN_CENTER = 1; public static final int ALIGN_BOTTOM = 2; private Paint mPaint; private float mStartLine; private float mLineWidth; private int mStrokeColorIndex; private float mAnimTime; private Path mPath; private DashPathEffect mPathEffect; private float mProgressPercent; private float mSecondaryProgressPercent; private int mMaxLineWidth; private float mMaxLineWidthPercent; private int mMinLineWidth; private float mMinLineWidthPercent; private int mStrokeSize; private int mVerticalAlign; private int[] mStrokeColors; private int mStrokeSecondaryColor; private boolean mReverse; private int mTravelDuration; private int mTransformDuration; private int mKeepDuration; private int mInAnimationDuration; private int mOutAnimationDuration; private int mProgressMode; private Interpolator mTransformInterpolator; private LinearProgressDrawable(float progressPercent, float secondaryProgressPercent, int maxLineWidth, float maxLineWidthPercent, int minLineWidth, float minLineWidthPercent, int strokeSize, int verticalAlign, int[] strokeColors, int strokeSecondaryColor, boolean reverse, int travelDuration, int transformDuration, int keepDuration, Interpolator transformInterpolator, int progressMode, int inAnimDuration, int outAnimDuration){ setProgress(progressPercent); setSecondaryProgress(secondaryProgressPercent); mMaxLineWidth = maxLineWidth; mMaxLineWidthPercent = maxLineWidthPercent; mMinLineWidth = minLineWidth; mMinLineWidthPercent = minLineWidthPercent; mStrokeSize = strokeSize; mVerticalAlign = verticalAlign; mStrokeColors = strokeColors; mStrokeSecondaryColor = strokeSecondaryColor; mReverse = reverse; mTravelDuration = travelDuration; mTransformDuration = transformDuration; mKeepDuration = keepDuration; mTransformInterpolator = transformInterpolator; mProgressMode = progressMode; mInAnimationDuration = inAnimDuration; mOutAnimationDuration = outAnimDuration; mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeJoin(Paint.Join.ROUND); mPath = new Path(); } @Override public void draw(Canvas canvas) { switch (mProgressMode) { case ProgressView.MODE_DETERMINATE: drawDeterminate(canvas); break; case ProgressView.MODE_INDETERMINATE: drawIndeterminate(canvas); break; case ProgressView.MODE_BUFFER: drawBuffer(canvas); break; case ProgressView.MODE_QUERY: drawQuery(canvas); break; } } private void drawLinePath(Canvas canvas, float x1, float y1, float x2, float y2, Paint paint){ mPath.reset(); mPath.moveTo(x1, y1); mPath.lineTo(x2, y2); canvas.drawPath(mPath, paint); } private void drawDeterminate(Canvas canvas){ Rect bounds = getBounds(); int width = bounds.width(); float size = 0f; if(mRunState == RUN_STATE_STARTING) size = (float)mStrokeSize * Math.min(mInAnimationDuration, (SystemClock.uptimeMillis() - mLastRunStateTime)) / mInAnimationDuration; else if(mRunState == RUN_STATE_STOPPING) size = (float)mStrokeSize * Math.max(0, (mOutAnimationDuration - SystemClock.uptimeMillis() + mLastRunStateTime)) / mOutAnimationDuration; else if(mRunState != RUN_STATE_STOPPED) size = mStrokeSize; if(size > 0){ float y = 0; float lineWidth = width * mProgressPercent; switch (mVerticalAlign) { case ALIGN_TOP: y = size / 2; break; case ALIGN_CENTER: y = bounds.height() / 2f; break; case ALIGN_BOTTOM: y = bounds.height() - size / 2; break; } mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.STROKE); if(mProgressPercent != 1f){ mPaint.setColor(mStrokeSecondaryColor); if(mReverse) canvas.drawLine(0, y, width - lineWidth, y, mPaint); else canvas.drawLine(lineWidth, y, width, y, mPaint); } if(mProgressPercent != 0f){ mPaint.setColor(mStrokeColors[0]); if(mReverse) drawLinePath(canvas, width - lineWidth, y, width, y, mPaint); else drawLinePath(canvas, 0, y, lineWidth, y, mPaint); } } } private int getIndeterminateStrokeColor(){ if(mProgressState != PROGRESS_STATE_KEEP_SHRINK || mStrokeColors.length == 1) return mStrokeColors[mStrokeColorIndex]; float value = Math.max(0f, Math.min(1f, (float)(SystemClock.uptimeMillis() - mLastProgressStateTime) / mKeepDuration)); int prev_index = mStrokeColorIndex == 0 ? mStrokeColors.length - 1 : mStrokeColorIndex - 1; return ColorUtil.getMiddleColor(mStrokeColors[prev_index], mStrokeColors[mStrokeColorIndex], value); } private void drawIndeterminate(Canvas canvas){ Rect bounds = getBounds(); int width = bounds.width(); float size = 0f; if(mRunState == RUN_STATE_STARTING) size = (float)mStrokeSize * Math.min(mInAnimationDuration, (SystemClock.uptimeMillis() - mLastRunStateTime)) / mInAnimationDuration; else if(mRunState == RUN_STATE_STOPPING) size = (float)mStrokeSize * Math.max(0, (mOutAnimationDuration - SystemClock.uptimeMillis() + mLastRunStateTime)) / mOutAnimationDuration; else if(mRunState != RUN_STATE_STOPPED) size = mStrokeSize; if(size > 0){ float y = 0; switch (mVerticalAlign) { case ALIGN_TOP: y = size / 2; break; case ALIGN_CENTER: y = bounds.height() / 2f; break; case ALIGN_BOTTOM: y = bounds.height() - size / 2; break; } mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.STROKE); float endLine = offset(mStartLine, mLineWidth, width); if(mReverse){ if(endLine <= mStartLine){ mPaint.setColor(mStrokeSecondaryColor); if(endLine > 0) canvas.drawLine(0, y, endLine, y, mPaint); if(mStartLine < width) canvas.drawLine(mStartLine, y, width, y, mPaint); mPaint.setColor(getIndeterminateStrokeColor()); drawLinePath(canvas, endLine, y, mStartLine, y, mPaint); } else{ mPaint.setColor(mStrokeSecondaryColor); canvas.drawLine(mStartLine, y, endLine, y, mPaint); mPaint.setColor(getIndeterminateStrokeColor()); mPath.reset(); if(mStartLine > 0){ mPath.moveTo(0, y); mPath.lineTo(mStartLine, y); } if(endLine < width){ mPath.moveTo(endLine, y); mPath.lineTo(width, y); } canvas.drawPath(mPath, mPaint); } } else{ if(endLine >= mStartLine){ mPaint.setColor(mStrokeSecondaryColor); if(mStartLine > 0) canvas.drawLine(0, y, mStartLine, y, mPaint); if(endLine < width) canvas.drawLine(endLine, y, width, y, mPaint); mPaint.setColor(getIndeterminateStrokeColor()); drawLinePath(canvas, mStartLine, y, endLine, y, mPaint); } else{ mPaint.setColor(mStrokeSecondaryColor); canvas.drawLine(endLine, y, mStartLine, y, mPaint); mPaint.setColor(getIndeterminateStrokeColor()); mPath.reset(); if(endLine > 0){ mPath.moveTo(0, y); mPath.lineTo(endLine, y); } if(mStartLine < width){ mPath.moveTo(mStartLine, y); mPath.lineTo(width, y); } canvas.drawPath(mPath, mPaint); } } } } private PathEffect getPathEffect(){ if(mPathEffect == null) mPathEffect = new DashPathEffect(new float[]{0.1f, mStrokeSize * 2}, 0f); return mPathEffect; } private void drawBuffer(Canvas canvas){ Rect bounds = getBounds(); int width = bounds.width(); float size = 0f; if(mRunState == RUN_STATE_STARTING) size = (float)mStrokeSize * Math.min(mInAnimationDuration, (SystemClock.uptimeMillis() - mLastRunStateTime)) / mInAnimationDuration; else if(mRunState == RUN_STATE_STOPPING) size = (float)mStrokeSize * Math.max(0, (mOutAnimationDuration - SystemClock.uptimeMillis() + mLastRunStateTime)) / mOutAnimationDuration; else if(mRunState != RUN_STATE_STOPPED) size = mStrokeSize; if(size > 0){ float y = 0; float lineWidth = width * mProgressPercent; float secondaryLineWidth = width * mSecondaryProgressPercent; switch (mVerticalAlign) { case ALIGN_TOP: y = size / 2; break; case ALIGN_CENTER: y = bounds.height() / 2f; break; case ALIGN_BOTTOM: y = bounds.height() - size / 2; break; } mPaint.setStyle(Paint.Style.STROKE); if(mProgressPercent != 1f){ mPaint.setStrokeWidth(size); mPaint.setColor(mStrokeSecondaryColor); mPaint.setPathEffect(null); if(mReverse) drawLinePath(canvas, width - secondaryLineWidth, y, width - lineWidth, y, mPaint); else drawLinePath(canvas, secondaryLineWidth, y, lineWidth, y, mPaint); mPaint.setStrokeWidth(mLineWidth); mPaint.setPathEffect(getPathEffect()); float offset = mStrokeSize * 2 - mStartLine; if(mReverse) drawLinePath(canvas, -offset, y, width - secondaryLineWidth, y, mPaint); else drawLinePath(canvas, width + offset, y, secondaryLineWidth, y, mPaint); } if(mProgressPercent != 0f){ mPaint.setStrokeWidth(size); mPaint.setColor(mStrokeColors[0]); mPaint.setPathEffect(null); if(mReverse) drawLinePath(canvas, width - lineWidth, y, width, y, mPaint); else drawLinePath(canvas, 0, y, lineWidth, y, mPaint); } } } private int getQueryStrokeColor(){ return ColorUtil.getColor(mStrokeColors[0], mAnimTime); } private void drawQuery(Canvas canvas){ Rect bounds = getBounds(); int width = bounds.width(); float size = 0f; if(mRunState == RUN_STATE_STARTING) size = (float)mStrokeSize * Math.min(mInAnimationDuration, (SystemClock.uptimeMillis() - mLastRunStateTime)) / mInAnimationDuration; else if(mRunState == RUN_STATE_STOPPING) size = (float)mStrokeSize * Math.max(0, (mOutAnimationDuration - SystemClock.uptimeMillis() + mLastRunStateTime)) / mOutAnimationDuration; else if(mRunState != RUN_STATE_STOPPED) size = mStrokeSize; if(size > 0){ float y = 0; switch (mVerticalAlign) { case ALIGN_TOP: y = size / 2; break; case ALIGN_CENTER: y = bounds.height() / 2f; break; case ALIGN_BOTTOM: y = bounds.height() - size / 2; break; } mPaint.setStrokeWidth(size); mPaint.setStyle(Paint.Style.STROKE); if(mProgressPercent != 1f){ mPaint.setColor(mStrokeSecondaryColor); canvas.drawLine(0, y, width, y, mPaint); if(mAnimTime < 1f){ float endLine = Math.max(0, Math.min(width, mStartLine + mLineWidth)); mPaint.setColor(getQueryStrokeColor()); drawLinePath(canvas, mStartLine, y, endLine, y, mPaint); } } if(mProgressPercent != 0f){ float lineWidth = width * mProgressPercent; mPaint.setColor(mStrokeColors[0]); if(mReverse) drawLinePath(canvas, width - lineWidth, y, width, y, mPaint); else drawLinePath(canvas, 0, y, lineWidth, y, mPaint); } } } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter cf) { mPaint.setColorFilter(cf); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } public int getProgressMode(){ return mProgressMode; } public float getProgress(){ return mProgressPercent; } public float getSecondaryProgress(){ return mSecondaryProgressPercent; } public void setProgress(float percent){ percent = Math.min(1f, Math.max(0f, percent)); if(mProgressPercent != percent){ mProgressPercent = percent; if(isRunning()) invalidateSelf(); else if(mProgressPercent != 0f) start(); } } public void setSecondaryProgress(float percent){ percent = Math.min(1f, Math.max(0f, percent)); if(mSecondaryProgressPercent != percent){ mSecondaryProgressPercent = percent; if(isRunning()) invalidateSelf(); else if(mSecondaryProgressPercent != 0f) start(); } } //Animation: based on http://cyrilmottier.com/2012/11/27/actionbar-on-the-move/ private void resetAnimation(){ mLastUpdateTime = SystemClock.uptimeMillis(); mLastProgressStateTime = mLastUpdateTime; if(mProgressMode == ProgressView.MODE_INDETERMINATE){ mStartLine = mReverse ? getBounds().width() : 0; mStrokeColorIndex = 0; mLineWidth = mReverse ? -mMinLineWidth : mMinLineWidth; mProgressState = PROGRESS_STATE_STRETCH; } else if(mProgressMode == ProgressView.MODE_BUFFER){ mStartLine = 0; } else if(mProgressMode == ProgressView.MODE_QUERY){ mStartLine = !mReverse ? getBounds().width() : 0; mStrokeColorIndex = 0; mLineWidth = !mReverse ? -mMaxLineWidth : mMaxLineWidth; } } @Override public void start() { start(mInAnimationDuration > 0); } @Override public void stop() { stop(mOutAnimationDuration > 0); } private void start(boolean withAnimation){ if(isRunning()) return; if(withAnimation){ mRunState = RUN_STATE_STARTING; mLastRunStateTime = SystemClock.uptimeMillis(); } resetAnimation(); scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } private void stop(boolean withAnimation){ if(!isRunning()) return; if(withAnimation){ mLastRunStateTime = SystemClock.uptimeMillis(); if(mRunState == RUN_STATE_STARTED){ scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } mRunState = RUN_STATE_STOPPING; } else{ mRunState = RUN_STATE_STOPPED; unscheduleSelf(mUpdater); invalidateSelf(); } } @Override public boolean isRunning() { return mRunState != RUN_STATE_STOPPED; } @Override public void scheduleSelf(Runnable what, long when) { if(mRunState == RUN_STATE_STOPPED) mRunState = mInAnimationDuration > 0 ? RUN_STATE_STARTING : RUN_STATE_RUNNING; super.scheduleSelf(what, when); } private final Runnable mUpdater = new Runnable() { @Override public void run() { update(); } }; private void update(){ switch (mProgressMode) { case ProgressView.MODE_DETERMINATE: updateDeterminate(); break; case ProgressView.MODE_INDETERMINATE: updateIndeterminate(); break; case ProgressView.MODE_BUFFER: updateBuffer(); break; case ProgressView.MODE_QUERY: updateQuery(); break; } } private void updateDeterminate(){ long curTime = SystemClock.uptimeMillis(); if(mRunState == RUN_STATE_STARTING){ if(curTime - mLastRunStateTime > mInAnimationDuration){ mRunState = RUN_STATE_STARTED; return; } } else if(mRunState == RUN_STATE_STOPPING){ if(curTime - mLastRunStateTime > mOutAnimationDuration){ stop(false); return; } } if(isRunning()) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } private float offset(float pos, float offset, float max){ pos += offset; if(pos > max) return pos - max; if(pos < 0) return max + pos; return pos; } private void updateIndeterminate(){ Rect bounds = getBounds(); int width = bounds.width(); long curTime = SystemClock.uptimeMillis(); float travelOffset = (float)(curTime - mLastUpdateTime) * width / mTravelDuration; if(mReverse) travelOffset = -travelOffset; mLastUpdateTime = curTime; switch (mProgressState) { case PROGRESS_STATE_STRETCH: if(mTransformDuration <= 0){ mLineWidth = mMinLineWidth == 0 ? width * mMinLineWidthPercent : mMinLineWidth; if(mReverse) mLineWidth = -mLineWidth; mStartLine = offset(mStartLine, travelOffset, width); mProgressState = PROGRESS_STATE_KEEP_STRETCH; mLastProgressStateTime = curTime; } else{ float value = (curTime - mLastProgressStateTime) / (float)mTransformDuration; float maxWidth = mMaxLineWidth == 0 ? width * mMaxLineWidthPercent : mMaxLineWidth; float minWidth = mMinLineWidth == 0 ? width * mMinLineWidthPercent : mMinLineWidth; mStartLine = offset(mStartLine, travelOffset, width); mLineWidth = mTransformInterpolator.getInterpolation(value) * (maxWidth - minWidth) + minWidth; if(mReverse) mLineWidth = -mLineWidth; if(value > 1f){ mLineWidth = mReverse ? -maxWidth : maxWidth; mProgressState = PROGRESS_STATE_KEEP_STRETCH; mLastProgressStateTime = curTime; } } break; case PROGRESS_STATE_KEEP_STRETCH: mStartLine = offset(mStartLine, travelOffset, width); if(curTime - mLastProgressStateTime > mKeepDuration){ mProgressState = PROGRESS_STATE_SHRINK; mLastProgressStateTime = curTime; } break; case PROGRESS_STATE_SHRINK: if(mTransformDuration <= 0){ mLineWidth = mMinLineWidth == 0 ? width * mMinLineWidthPercent : mMinLineWidth; if(mReverse) mLineWidth = -mLineWidth; mStartLine = offset(mStartLine, travelOffset, width); mProgressState = PROGRESS_STATE_KEEP_SHRINK; mLastProgressStateTime = curTime; mStrokeColorIndex = (mStrokeColorIndex + 1) % mStrokeColors.length; } else{ float value = (curTime - mLastProgressStateTime) / (float)mTransformDuration; float maxWidth = mMaxLineWidth == 0 ? width * mMaxLineWidthPercent : mMaxLineWidth; float minWidth = mMinLineWidth == 0 ? width * mMinLineWidthPercent : mMinLineWidth; float newLineWidth = (1f - mTransformInterpolator.getInterpolation(value)) * (maxWidth - minWidth) + minWidth; if(mReverse) newLineWidth = -newLineWidth; mStartLine = offset(mStartLine, travelOffset + mLineWidth - newLineWidth, width); mLineWidth = newLineWidth; if(value > 1f){ mLineWidth = mReverse ? -minWidth : minWidth; mProgressState = PROGRESS_STATE_KEEP_SHRINK; mLastProgressStateTime = curTime; mStrokeColorIndex = (mStrokeColorIndex + 1) % mStrokeColors.length; } } break; case PROGRESS_STATE_KEEP_SHRINK: mStartLine = offset(mStartLine, travelOffset, width); if(curTime - mLastProgressStateTime > mKeepDuration){ mProgressState = PROGRESS_STATE_STRETCH; mLastProgressStateTime = curTime; } break; } if(mRunState == RUN_STATE_STARTING){ if(curTime - mLastRunStateTime > mInAnimationDuration) mRunState = RUN_STATE_RUNNING; } else if(mRunState == RUN_STATE_STOPPING){ if(curTime - mLastRunStateTime > mOutAnimationDuration){ stop(false); return; } } if (isRunning()) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } private void updateBuffer(){ long curTime = SystemClock.uptimeMillis(); float maxDistance = mStrokeSize * 2; mStartLine += maxDistance * (float)(curTime - mLastUpdateTime) / mTravelDuration; while(mStartLine > maxDistance) mStartLine -= maxDistance; mLastUpdateTime = curTime; switch (mProgressState) { case PROGRESS_STATE_STRETCH: if(mTransformDuration <= 0){ mProgressState = PROGRESS_STATE_KEEP_STRETCH; mLastProgressStateTime = curTime; } else{ float value = (curTime - mLastProgressStateTime) / (float)mTransformDuration; mLineWidth = mTransformInterpolator.getInterpolation(value) * mStrokeSize; if(value > 1f){ mLineWidth = mStrokeSize; mProgressState = PROGRESS_STATE_KEEP_STRETCH; mLastProgressStateTime = curTime; } } break; case PROGRESS_STATE_KEEP_STRETCH: if(curTime - mLastProgressStateTime > mKeepDuration){ mProgressState = PROGRESS_STATE_SHRINK; mLastProgressStateTime = curTime; } break; case PROGRESS_STATE_SHRINK: if(mTransformDuration <= 0){ mProgressState = PROGRESS_STATE_KEEP_SHRINK; mLastProgressStateTime = curTime; } else{ float value = (curTime - mLastProgressStateTime) / (float)mTransformDuration; mLineWidth = (1f - mTransformInterpolator.getInterpolation(value)) * mStrokeSize; if(value > 1f){ mLineWidth = 0; mProgressState = PROGRESS_STATE_KEEP_SHRINK; mLastProgressStateTime = curTime; } } break; case PROGRESS_STATE_KEEP_SHRINK: if(curTime - mLastProgressStateTime > mKeepDuration){ mProgressState = PROGRESS_STATE_STRETCH; mLastProgressStateTime = curTime; } break; } if(mRunState == RUN_STATE_STARTING){ if(curTime - mLastRunStateTime > mInAnimationDuration) mRunState = RUN_STATE_RUNNING; } else if(mRunState == RUN_STATE_STOPPING){ if(curTime - mLastRunStateTime > mOutAnimationDuration){ stop(false); return; } } if(isRunning()) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); invalidateSelf(); } private void updateQuery(){ long curTime = SystemClock.uptimeMillis(); mAnimTime = (float)(curTime - mLastProgressStateTime) / mTravelDuration; boolean requestUpdate = mRunState == RUN_STATE_STOPPING || mProgressPercent == 0 || mAnimTime < 1f; if(mAnimTime > 1f){ mLastProgressStateTime = Math.round(curTime - (mAnimTime - 1f) * mTravelDuration); mAnimTime -= 1f; } if(requestUpdate && mRunState != RUN_STATE_STOPPING){ Rect bounds = getBounds(); int width = bounds.width(); float maxWidth = mMaxLineWidth == 0 ? width * mMaxLineWidthPercent : mMaxLineWidth; float minWidth = mMinLineWidth == 0 ? width * mMinLineWidthPercent : mMinLineWidth; mLineWidth = mTransformInterpolator.getInterpolation(mAnimTime) * (minWidth - maxWidth) + maxWidth; if(mReverse) mLineWidth = -mLineWidth; mStartLine = mReverse ? mTransformInterpolator.getInterpolation(mAnimTime) * (width + minWidth) : ((1f - mTransformInterpolator.getInterpolation(mAnimTime)) * (width + minWidth) - minWidth); } if(mRunState == RUN_STATE_STARTING){ if(curTime - mLastRunStateTime > mInAnimationDuration) mRunState = RUN_STATE_RUNNING; } else if(mRunState == RUN_STATE_STOPPING){ if(curTime - mLastRunStateTime > mOutAnimationDuration){ stop(false); return; } } if (isRunning()){ if(requestUpdate) scheduleSelf(mUpdater, SystemClock.uptimeMillis() + ViewUtil.FRAME_DURATION); else if(mRunState == RUN_STATE_RUNNING) mRunState = RUN_STATE_STARTED; } invalidateSelf(); } public static class Builder{ private float mProgressPercent = 0; private float mSecondaryProgressPercent = 0; private int mMaxLineWidth; private float mMaxLineWidthPercent; private int mMinLineWidth; private float mMinLineWidthPercent; private int mStrokeSize = 8; private int mVerticalAlign = LinearProgressDrawable.ALIGN_BOTTOM; private int[] mStrokeColors; private int mStrokeSecondaryColor; private boolean mReverse = false; private int mTravelDuration = 1000; private int mTransformDuration = 800; private int mKeepDuration = 200; private Interpolator mTransformInterpolator; private int mProgressMode = ProgressView.MODE_INDETERMINATE; private int mInAnimationDuration = 400; private int mOutAnimationDuration = 400; public Builder(){} public Builder(Context context, AttributeSet attrs, int defStyle){ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LinearProgressDrawable, 0, defStyle); int resId; progressPercent(a.getFloat(R.styleable.LinearProgressDrawable_pv_progress, 0)); secondaryProgressPercent(a.getFloat(R.styleable.LinearProgressDrawable_pv_secondaryProgress, 0)); TypedValue value = a.peekValue(R.styleable.LinearProgressDrawable_lpd_maxLineWidth); if(value == null) maxLineWidth(0.75f); else if(value.type == TypedValue.TYPE_FRACTION) maxLineWidth(a.getFraction(R.styleable.LinearProgressDrawable_lpd_maxLineWidth, 1, 1, 0.75f)); else maxLineWidth(a.getDimensionPixelSize(R.styleable.LinearProgressDrawable_lpd_maxLineWidth, 0)); value = a.peekValue(R.styleable.LinearProgressDrawable_lpd_minLineWidth); if(value == null) minLineWidth(0.25f); else if(value.type == TypedValue.TYPE_FRACTION) minLineWidth(a.getFraction(R.styleable.LinearProgressDrawable_lpd_minLineWidth, 1, 1, 0.25f)); else minLineWidth(a.getDimensionPixelSize(R.styleable.LinearProgressDrawable_lpd_minLineWidth, 0)); strokeSize(a.getDimensionPixelSize(R.styleable.LinearProgressDrawable_lpd_strokeSize, ThemeUtil.dpToPx(context, 4))); verticalAlign(a.getInteger(R.styleable.LinearProgressDrawable_lpd_verticalAlign, LinearProgressDrawable.ALIGN_BOTTOM)); strokeColors(a.getColor(R.styleable.LinearProgressDrawable_lpd_strokeColor, ThemeUtil.colorPrimary(context, 0xFF000000))); if((resId = a.getResourceId(R.styleable.LinearProgressDrawable_lpd_strokeColors, 0)) != 0){ TypedArray ta = context.getResources().obtainTypedArray(resId); int[] colors = new int[ta.length()]; for(int j = 0; j < ta.length(); j++) colors[j] = ta.getColor(j, 0); ta.recycle(); strokeColors(colors); } strokeSecondaryColor(a.getColor(R.styleable.LinearProgressDrawable_lpd_strokeSecondaryColor, 0)); reverse(a.getBoolean(R.styleable.LinearProgressDrawable_lpd_reverse, false)); travelDuration(a.getInteger(R.styleable.LinearProgressDrawable_lpd_travelDuration, context.getResources().getInteger(android.R.integer.config_longAnimTime))); transformDuration(a.getInteger(R.styleable.LinearProgressDrawable_lpd_transformDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime))); keepDuration(a.getInteger(R.styleable.LinearProgressDrawable_lpd_keepDuration, context.getResources().getInteger(android.R.integer.config_shortAnimTime))); if((resId = a.getResourceId(R.styleable.LinearProgressDrawable_lpd_transformInterpolator, 0)) != 0) transformInterpolator(AnimationUtils.loadInterpolator(context, resId)); progressMode(a.getInteger(R.styleable.LinearProgressDrawable_pv_progressMode, ProgressView.MODE_INDETERMINATE)); inAnimDuration(a.getInteger(R.styleable.LinearProgressDrawable_lpd_inAnimDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime))); outAnimDuration(a.getInteger(R.styleable.LinearProgressDrawable_lpd_outAnimDuration, context.getResources().getInteger(android.R.integer.config_mediumAnimTime))); a.recycle(); } public LinearProgressDrawable build(){ if(mStrokeColors == null) mStrokeColors = new int[]{0xFF0099FF}; if(mTransformInterpolator == null) mTransformInterpolator = new DecelerateInterpolator(); return new LinearProgressDrawable(mProgressPercent, mSecondaryProgressPercent, mMaxLineWidth, mMaxLineWidthPercent, mMinLineWidth, mMinLineWidthPercent, mStrokeSize, mVerticalAlign, mStrokeColors, mStrokeSecondaryColor, mReverse, mTravelDuration, mTransformDuration, mKeepDuration, mTransformInterpolator, mProgressMode, mInAnimationDuration, mOutAnimationDuration); } public Builder secondaryProgressPercent(float percent){ mSecondaryProgressPercent = percent; return this; } public Builder progressPercent(float percent){ mProgressPercent = percent; return this; } public Builder maxLineWidth(int width){ mMaxLineWidth = width; return this; } public Builder maxLineWidth(float percent){ mMaxLineWidthPercent = Math.max(0f, Math.min(1f, percent)); mMaxLineWidth = 0; return this; } public Builder minLineWidth(int width){ mMinLineWidth = width; return this; } public Builder minLineWidth(float percent){ mMinLineWidthPercent = Math.max(0f, Math.min(1f, percent)); mMinLineWidth = 0; return this; } public Builder strokeSize(int strokeSize){ mStrokeSize = strokeSize; return this; } public Builder verticalAlign(int align){ mVerticalAlign = align; return this; } public Builder strokeColors(int... strokeColors){ mStrokeColors = strokeColors; return this; } public Builder strokeSecondaryColor(int color){ mStrokeSecondaryColor = color; return this; } public Builder reverse(boolean reverse){ mReverse = reverse; return this; } public Builder reverse(){ return reverse(true); } public Builder travelDuration(int duration){ mTravelDuration = duration; return this; } public Builder transformDuration(int duration){ mTransformDuration = duration; return this; } public Builder keepDuration(int duration){ mKeepDuration = duration; return this; } public Builder transformInterpolator(Interpolator interpolator){ mTransformInterpolator = interpolator; return this; } public Builder progressMode(int mode){ mProgressMode = mode; return this; } public Builder inAnimDuration(int duration){ mInAnimationDuration = duration; return this; } public Builder outAnimDuration(int duration){ mOutAnimationDuration = duration; return this; } } }