Wednesday 10 February 2016

Setting a Custom Progress Bar using Linear Interpolator


Here we will be creating a custom progress bar with linear animation of progress.

Screenshot:



We need to create a progress bar in activity_main.xml layout file with a progressDrawable which sets the background color and progress color of ProgressBar widget.

In activity_main.xml

<ProgressBar
        android:id="@+id/progress_bar_retry"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_centerInParent="true"
        android:indeterminate="false"
        android:progress="0"
        android:progressDrawable ="@drawable/progress_bar_retry_style" />


In progress_bar_retry_style.xml
<?xml version="1.0" encoding="utf-8"?>

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@android:id/background">
        <shape>
            <solid android:color="@color/light_grey"/>
        </shape>
    </item>

    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="@color/light_red"/>
            </shape>
        </clip>
    </item>
</layer-list>


Now, we have to set maximum value of progress:


Suppose, if you set max to 100 and your phone has 1400 pixels between 1 and 100 point.
Then when you set progress value 1 to 2, it will cover 14 pixels which will not be shown smooth to user.

So, that’s why we choose max to 500, so that when you cover progress 1 to progress 2 , it will cover 2.8 pixels(1400/500), which will be smooth for users.

private void setProgress() {

  if (mRetryHandler == null) {
    mRetryHandler = new Handler();
  }

  progress++;
  count++;
  

  mRetryProgressBar.setProgress(progress);

  if (progress >= MAX_PROGRESS) {
    Toast.makeText(getActivity(), "Your timer has finished", Toast.LENGTH_SHORT).show();
    return;
  } else {
    mRetryHandler.postDelayed(mRetryRunnable, 200);
  }
}


Basically above function will give animation, but it will feel very slow, because here we are updating progress value by 1,  5 times in 1 sec, i.e. by updating progress value to 5 in 1 sec.


We should not calculate intermediate progress values(by what progress values to be incremented in 1 sec). We should use LinerInterpolator to calculate intermediate values of progress to update in a sec.

Now, we will use ObjectAnimator to set LinearInterpolator to provide smooth animation by setting linearInterpolator on ObjectAnimator instance.

mRetryObjectAnimator.setInterpolator(mInterpolator);


static  ObjectAnimator
ofInt(Object target, String propertyName, int... values)
Constructs and returns an ObjectAnimator that animates between int values.

This subclass of ValueAnimator provides support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. Appropriate set/get functions are then determined internally and the animation will call these functions as necessary to animate the property.

Parameters
targetThe object whose property is to be animated. This object should have a public method on it called setName(), where name is the value of the propertyName parameter.
propertyNameThe name of the property being animated.
valuesA set of values that the animation will animate between over time.



So, here is MainActivity.java code:



public class MainActivity extends AppCompatActivity {

    private static final int UPDATE_RETRY_PROGRESS_BAR = 1001;
    private static final int MAX_PROGRESS = 500;
    private Handler mRetryHandler;
    private ObjectAnimator mRetryObjectAnimator;
    private ProgressBar mRetryProgressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRetryProgressBar = (ProgressBar) findViewById(R.id.progress_bar_retry);
        mRetryHandler = new RetryHandler();

        int timeFromServer = 60;
        Message msg = mRetryHandler.obtainMessage(UPDATE_RETRY_PROGRESS_BAR, timeFromServer, 0);
        mRetryHandler.sendMessage(msg);

    }

    private class RetryHandler extends Handler {

        private LinearInterpolator mInterpolator;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case UPDATE_RETRY_PROGRESS_BAR:
                    int timeDuration = msg.arg1;
                    // for smooth progress bar update.
                    mRetryObjectAnimator =
                            ObjectAnimator.ofInt(mRetryProgressBar, "progress", MAX_PROGRESS);
                    mRetryObjectAnimator.setDuration(timeDuration * 1000);
                    if (mInterpolator == null) mInterpolator = new LinearInterpolator();
                    mRetryObjectAnimator.setInterpolator(mInterpolator);
                    mRetryObjectAnimator.start();

                    mRetryObjectAnimator.addListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {

                        }

                        @Override
                        public void onAnimationEnd(Animator animation) {
                            Toast.makeText(MainActivity.this, "Your timer has finished", Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void onAnimationCancel(Animator animation) {

                        }

                        @Override
                        public void onAnimationRepeat(Animator animation) {

                        }
                    });

                    break;
            }
        }
    }
}


Demo:



Reference:
http://developer.android.com/reference/android/animation/ObjectAnimator.html