Android Open Source - Material Linear Progress Drawable






From Project

Back to project page Material.

License

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.

Java Source Code

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;
    }
    
  }
}




Java Source Code List

com.rey.material.ApplicationTest.java
com.rey.material.demo.ButtonFragment.java
com.rey.material.demo.MainActivity.java
com.rey.material.demo.ProgressFragment.java
com.rey.material.demo.SnackbarFragment.java
com.rey.material.demo.SwitchesFragment.java
com.rey.material.demo.TextfieldFragment.java
com.rey.material.drawable.ArrowDrawable.java
com.rey.material.drawable.BlankDrawable.java
com.rey.material.drawable.CheckBoxDrawable.java
com.rey.material.drawable.CircularProgressDrawable.java
com.rey.material.drawable.DividerDrawable.java
com.rey.material.drawable.LineMorphingDrawable.java
com.rey.material.drawable.LinearProgressDrawable.java
com.rey.material.drawable.NavigationDrawerDrawable.java
com.rey.material.drawable.RadioButtonDrawable.java
com.rey.material.drawable.RevealDrawable.java
com.rey.material.drawable.RippleDrawable.java
com.rey.material.drawable.ToolbarRippleDrawable.java
com.rey.material.util.ColorUtil.java
com.rey.material.util.ThemeUtil.java
com.rey.material.util.ViewUtil.java
com.rey.material.view.Button.java
com.rey.material.view.CheckBox.java
com.rey.material.view.CheckedTextView.java
com.rey.material.view.CompoundButton.java
com.rey.material.view.EditText.java
com.rey.material.view.FloatingActionButton.java
com.rey.material.view.ListPopupWindow.java
com.rey.material.view.ListView.java
com.rey.material.view.PopupWindow.java
com.rey.material.view.ProgressView.java
com.rey.material.view.RadioButton.java
com.rey.material.view.RippleManager.java
com.rey.material.view.SnackBar.java
com.rey.material.view.Spinner.java
com.rey.material.view.Switch.java
com.rey.material.view.TabPageIndicator.java
com.rey.material.view.TextView.java