12

The problem

I would like to make a progress bar like in this image:

this image

The desired behavior is:

  • When user clicks the button, the dark gray progress bar appears and starts to get incremented at a constant pace. I want to be able to determine how long it will take for it to completely fill (like 2 seconds).
  • If the request arrives BEFORE the progress bar has reached 100%, the progress bar should go straight to 100% (not within a frame, but it should grow very quickly, like it happens in the windows file explorer, when it's loading something and finished when the progress bar is not at the end yet).
  • If the request doesn't arrive before the bar reaches 100%, let it reach 100%. If at 100% (i.e. past 2 seconds) the request has not arrived yet, a timeout happens (I will make a snackbar appear; don't worry about this, I know how to do it, it's just for clarification).

What I found so far

I found this article that sort of exemplifies what I want. Are there any issues with that approach? Also, that example does not show how to customize the progress bar (in my case, i want it with gray color at the top of a view with curved corners). But what I would mostly like to know is if the approach from that link is a good one.

My guesses

My main concerns are about the "quick filling" that happens when the request has arrived but the progress bar is not at 100% yet (which I presume will be the majority of the cases; it's very unlikely that the request arrives precisely at 100%). Taking what is said in the link, I was thinking that, when the request arrives, the method doSomeTasks should return the progress bar status + 1 within a small interval. So, in example, if it's at 55, it would return 56, 57, 58...Until 100 and the returns would have a interval of, say, 0.5 second between them. This way, I think I could simulate the progress bar quickly growing to 100%.

Any help is greatly appreciated, thanks!

FabioR
  • 696
  • 9
  • 18
  • 1
    Could you overlay a clickable TextView over a ProgressBar? (Though the "newer "progress bars are actually spinners, so maybe not) – OneCricketeer Nov 03 '16 at 18:38
  • I think so, I was thinking about using a FrameLayout and putting the TextView on top of the progress bar – FabioR Nov 03 '16 at 18:40

1 Answers1

26

Create a frame layout with textview and progress bar:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ProgressBar
        android:id="@+id/progress_bar"
        android:progressDrawable="@drawable/progress_bar_states"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:indeterminate="false"
        style="?android:attr/progressBarStyleHorizontal" />

    <TextView
        android:id="@+id/text_view_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Enter"
        android:textColor="@android:color/white"
        android:padding="6dp"
        android:textSize="16sp"
        android:textStyle="bold"
        android:gravity="center"/>

</FrameLayout>

You need to create a progressDrawable file.

File res/drawable/progress_bar_states.xml declares the colors of the different states:

<?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>
            <gradient
                android:startColor="#777777"
                android:centerColor="#333333"
                android:centerY="0.75"
                android:endColor="#222222"
                android:angle="270" />
        </shape>
    </item>

    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <gradient
                    android:startColor="#234"
                    android:centerColor="#234"
                    android:centerY="0.75"
                    android:endColor="#a24"
                    android:angle="270" />
            </shape>
        </clip>
    </item>

    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <gradient
                    android:startColor="#999999"
                    android:centerColor="#777777"
                    android:centerY="0.75"
                    android:endColor="#555555"
                    android:angle="270" />
            </shape>
        </clip>
    </item>
</layer-list>

Then, create the logic of your button/progressbar:

final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
final ObjectAnimator objectAnimator = ObjectAnimator.ofInt(progressBar, "progress", progressBar.getProgress(), 100).setDuration(2000);
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        int progress = (int) valueAnimator.getAnimatedValue();
        progressBar.setProgress(progress);
    }
});

TextView btn = (TextView) findViewById(R.id.text_view_button);
btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        objectAnimator.start();
    }
});

Basically, after the Textview is clicked, the ObjectAnimator is going to increment the ProgressBar for 2 seconds until it completes.

If you want to accelerate the progress call this method:

private void completeFast(final ProgressBar progressBar) {
    final ObjectAnimator objectAnimator = ObjectAnimator.ofInt(progressBar, "progress",
                    progressBar.getProgress(), progressBar.getMax());

    objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            int progress = (int) valueAnimator.getAnimatedValue();
            progressBar.setProgress(progress);
        }
    });
}

It will complete the progress in 0.3s

Maybe you want to change the colors of res/drawable/progress_bar_states.xml. But that's pretty much it =]

Result: enter image description here

Paula
  • 59
  • 2
  • 6
Leandro Borges Ferreira
  • 12,422
  • 10
  • 53
  • 73
  • Wow, that's brilliant! Thanks for answering! I will try it and let you know! Already upvoted you, if it works, I'll mark it as correct answer. Thanks again for such effort to describe it so fully! – FabioR Nov 04 '16 at 02:41
  • Thanks =]. I edited the answer. Change the: ObjectAnimator.ofInt(progressBar, "progress", progressBar.getProgress(), 100); For: ObjectAnimator.ofInt(progressBar, "progress", progressBar.getProgress(), progressBar.getMax()); – Leandro Borges Ferreira Nov 04 '16 at 09:30
  • I haven't tried it yet, I will try it today and let you know! : ) – FabioR Nov 08 '16 at 14:40
  • 1
    A question: what are exactly the `background`, `secondaryProgress` and `progress` states? Is `background` the button in its default configuration and `progress` the growing bar? What would be the `secondaryProgress` one? Thanks! – FabioR Nov 08 '16 at 18:29
  • I understood the backgrounds part, but I couldn't make the `completeFast` method work : S. It seems that it cannot interrupt the `objectAnimator.start()` with that method – FabioR Nov 09 '16 at 03:20
  • Try to cancel the previous animation, and then call complete fast. – Leandro Borges Ferreira Nov 09 '16 at 04:03
  • Works like a charm! Thank you very much for the answer! – FabioR Nov 09 '16 at 13:12