Smooth Progress Bar Animation

71.4k views Asked by At

I'm trying to implement a smooth animation for my ProgressBar, but when I increase the time (30 seconds), the animation is no longer smooth.

Example with 5 seconds:

5 seconds

Example with 30 seconds:

30 seconds

My progress background:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <padding android:top="1dp" />
            <solid android:color="#10444444" />
        </shape>
    </item>
    <item>
        <shape>
            <padding android:top="1dp" />
            <solid android:color="#20444444" />
        </shape>
    </item>
    <item>
        <shape>
            <padding android:top="1dp" />
            <solid android:color="#30444444" />
        </shape>
    </item>
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="@color/black_thirty" />
        </shape>
    </item>

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

My progress layout:

<ProgressBar
    android:id="@+id/pb_loading"
    android:layout_width="match_parent"
    android:layout_height="8dp"
    android:indeterminate="false"
    android:layout_centerInParent="true"
    android:progress="100"
    android:progressDrawable="@drawable/my_progress_bar" />

My animation method:

private void startAnimation(){
    ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.pb_loading);
    ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 100, 0);
    progressAnimator.setDuration(30000);
    progressAnimator.setInterpolator(new LinearInterpolator());
    progressAnimator.start();
}
10

There are 10 answers

3
Chris Stillwell On

Because you are using ofInt you can only move at full integers. In other words, if you have a progress bar with a width of 1000 and a progress of 0 to 100 since you are moving at an integer pace you count 1, 2, 3, 4 which translates to 10px, 20px, 30px and 40px. Which explains the jaggedness you are seeing.

To correct this you have a few options. The first is to up your integers from 0 to someBigInt This will give the animator more numbers to work with.

ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 10000, 0);

The other option is to use ofFloat which does the same thing as ofInt but uses floating points instead of integers.

ObjectAnimator progressAnimator = ObjectAnimator.ofFloat(mProgressBar, "progress", 100.0, 0.0);
0
SpiritCrusher On

Android N does provide a variant as setProgress(newProgress, true) which support animating the progress .

It will require a version check, though there is another method which you can directly use setProgressCompat(progress, true) regardless of version.

progressBar.setProgressCompat(progress, true)
0
Chetan Gupta On

here is snippet for my countdown timer using smooth animation you can modify as per your need please follow below :

private void setProgressBarValues() {
        progressBarCircle.setMax((int) (timeCountInMilliSeconds / 10));
        progressBarCircle.setProgress((int) (timeCountInMilliSeconds / 10));
        Log.e("progres", "" + (timeCountInMilliSeconds / 10));
    }

private void startCountDownTimer() {

        smoothAnimation = ObjectAnimator.ofInt(progressBarCircle, "progress", progressBarCircle.getProgress(), progressBarCircle.getMax());
        smoothAnimation.setDuration(500);
        smoothAnimation.setInterpolator(new AccelerateInterpolator());

        countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 10) {
            @Override
            public void onTick(long millisUntilFinished) {

                Log.e("getMax", "" + progressBarCircle.getMax());
                Log.e("getprogres", "" + progressBarCircle.getProgress());
                textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
                progressBarCircle.setProgress((int) (timeCountInMilliSeconds / 10 - millisUntilFinished / 10));
            }

            @Override
            public void onFinish() {

                textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
                // call to initialize the progress bar values
                setProgressBarValues();
                // hiding the reset icon
                buttonReset.setEnabled(false);
                // making edit text editable
                editTextMinute.setEnabled(true);
                // changing the timer status to stopped
                status = TimerStatus.STOPPED;
                smoothAnimation.end();
            }

        }.start();
        smoothAnimation.start();
        countDownTimer.start();
    }

Summary:

  1. setMax and setProgress

  2. setAnimation to show from progess to max values of progressbar

  3. create a timer with call back of ~10 millisec

  4. update progress in onTick i.e total - finished

0
Shwarz Andrei On

XML

       <ProgressBar
        android:id="@+id/progress_bar"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:indeterminate="false"
        android:progress="0"
        android:max="100"/>

JAVA

@BindView(R.id.progress_bar) ProgressBar progressBar;
ObjectAnimator.ofInt(progressBar, "progress", 79).start();

79

  • any number between 0 and 100 for this example
0
Manmohan On

You can make custom clas like this :

public class CustomProgressBar extends ProgressBar {
private static final long DEFAULT_DELAY = 500;
private static final long DEFAULT_DURATION = 1000;

public CustomProgressBar(Context context) {
    super(context);
}

public CustomProgressBar(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

public synchronized void setProgress(float progress) {
    super.setProgress((int) progress);
}

@Override
public synchronized void setProgress(int progress) {
    super.setProgress(progress);
}

public void startLcpProgressAnim(int progressTo) {
   startLcpProgressAnim(DEFAULT_DELAY, progressTo);
}

public void startLcpProgressAnim(long delay, int progressTo) {
   startLcpProgressAnim(DEFAULT_DURATION, delay, progressTo);
}

public void startLcpProgressAnim(long duration, long delay, float progressTo) {
    ObjectAnimator animation = ObjectAnimator.
            ofFloat(this, "progress",
                    (float)this.getProgress(), progressTo);
    animation.setDuration(duration);
    animation.setStartDelay(delay);
    animation.start();
}
}
0
zivko On

You can not use the ofFloat because the ProgressBar's progress attribute doesn't accept float values, only integer ones. That is why your ProgressBar stopped progressing after going with that solution.

As the others have said it, the correct way to do what you want is to set android:max to some big integer.

Sorry for reviving the thread but I feel like this had to be said.

3
Pavel Kataykin On

If you change progress value each time by 1 (for example from 45 to 46) you won't see the animation. You'd better change progress by 100 points (or maybe other), for this you just need to multiply your max value by 100 and each progress value to 100 too. For example:

    private void setProgressMax(ProgressBar pb, int max) {
        pb.setMax(max * 100);
    }

    private void setProgressAnimate(ProgressBar pb, int progressTo) 
    {
        ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress", pb.getProgress(), progressTo * 100);
        animation.setDuration( 500);
        animation.setAutoCancel( true);
        animation.setInterpolator( new DecelerateInterpolator());
        animation.start();
    }
1
Hawkins On

Just set android:max="1000" and do ObjectAnimator progressAnimator = ObjectAnimator.ofInt(mProgressBar, "progress", 1000, 0);

in this case you will animate on 1/1000 by each step which in 10 time smoothly when default 100 percent scale. and it looks much better

0
Arslan Maqbool On

Use the library may its help you give the time in which you want to fill the progress bar its very smooth with no lag but you little bit customize to use this.

Just add the dependency to your build.gradle:

compile 'com.carlosmuvi.segmentedprogressbar:library:0.2'

Next, add it to your layout

  <com.carlosmuvi.segmentedprogressbar.SegmentedProgressBar
      android:id="@+id/segmented_progressbar"
      android:layout_width="match_parent"
      android:layout_height="5dp"/>

Finally, customize it programatically and play it!

segmentedProgressBar = (SegmentedProgressBar) findViewById(R.id.segmented_progressbar);

// number of segments in your bar
segmentedProgressBar.setSegmentCount(7); 

//empty segment color
segmentedProgressBar.setContainerColor(Color.BLUE); 
//fill segment color
segmentedProgressBar.setFillColor(Color.GREEN); 

//play next segment specifying its duration
segmentedProgressBar.playSegment(5000);

//pause segment
segmentedProgressBar.pause();

//set filled segments directly
segmentedProgressBar.setCompletedSegments(3);

GoToLibrary

0
android developer On

If you have Android N and above, you can use :

progressBar.setProgress(newProgress, true)

Docs:

Sets the current progress to the specified value, optionally animating the visual position between the current and target values.

Animation does not affect the result of getProgress(), which will return the target value immediately after this method is called.

https://developer.android.com/reference/android/widget/ProgressBar.html#setProgress(int,%20boolean)