ProgressBar under Action Bar
Asked Answered
I

6

78

Question Summary: How can I make a ProgressBar integrated inside the ActionBar, like on the Chrome App?

Details: Look at this screenshot from Chrome:

Chrome for Android screenshot

I want to create an Action Bar just like this. Just under the Action Bar, there's a ProgressBar that fills according to page load. I've seen this example from many apps, like Feedly, but I haven't been able to create my own implementation. I tried using Android's own APIs to create it:

@Override
protected void onCreate(Bundle savedInstanceState) {
    //Request Permission to display the Progress Bar...
    this.requestWindowFeature(Window.FEATURE_PROGRESS);
            this.setWindowContentView(R.layout.activity_main)
    super.onCreate(savedInstanceState);

    this.setProgressBarIndeterminate(true);
}

But this code only causes the ProgressBar to show over the Action Bar, like so:

My App Screenshot

So, how can I make my ProgressBar appear under the Action Bar, like on the Chrome App?

Infantryman answered 18/12, 2012 at 13:24 Comment(3)
possible duplicate question of this answer : https://mcmap.net/q/160929/-how-to-add-progressbar-to-actionbarsherlockHouser
have you found the solution yet?Chrysanthemum
screenshots for my grandmaClammy
P
7

This is now a native behavior that can be obtained using SwipeRefreshLayout.

You can wrap your scrollable view with a SwipeRefreshLayout and then you just need to listen to onRefresh events:

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

    swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
    swipeLayout.setOnRefreshListener(this);
    swipeLayout.setColorScheme(android.R.color.holo_blue_bright, 
            android.R.color.holo_green_light, 
            android.R.color.holo_orange_light, 
            android.R.color.holo_red_light);
}


@Override public void onRefresh() {
    new Handler().postDelayed(new Runnable() {
        @Override public void run() {
            swipeLayout.setRefreshing(false);
        }
    }, 5000);
}

A nice and simple tutorial can be found in this blog.

Preuss answered 20/2, 2015 at 17:30 Comment(0)
S
55

I wasn't fully satisfied with the accepted answer above, so I did some extra research myself.

The trick I believe they used is that they retrieved the top view in the view hierarchy called DecorView and added the progress bar in there. That way, the progress bar displays both over the action bar AND the content area. Note that the S.D.'s answer puts the progress bar into content area and 'steals' space from actual content, what can lead to unexpected results.

Sample screenshots of this implementation:

ProgressBar

ProgressBarIndeterminate

Code

Just put this code into onCreate method of some activity and it should work:

// create new ProgressBar and style it
final ProgressBar progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
progressBar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 24));
progressBar.setProgress(65);

// retrieve the top view of our application
final FrameLayout decorView = (FrameLayout) getWindow().getDecorView();
decorView.addView(progressBar);

// Here we try to position the ProgressBar to the correct position by looking
// at the position where content area starts. But during creating time, sizes 
// of the components are not set yet, so we have to wait until the components
// has been laid out
// Also note that doing progressBar.setY(136) will not work, because of different
// screen densities and different sizes of actionBar
ViewTreeObserver observer = progressBar.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        View contentView = decorView.findViewById(android.R.id.content);
        progressBar.setY(contentView.getY() - 10);

        ViewTreeObserver observer = progressBar.getViewTreeObserver();
        observer.removeOnGlobalLayoutListener(this);
    }
});

You can play with LayoutParams' height argument to set the progressBar wider or narrower, but you might have to adjust the -10 offset.

Styling

Unfortunately, you can see the ugly gray background of the progress bar. To remove it, simply searching for the background by id and trying to hide it doesn't work. To remove the background, I had to create identical drawble of the system version and remove the background item.

TL;DR: Create file progress_horizontal_holo_no_background_light.xml and paste this drawable:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@android:id/secondaryProgress">
        <scale android:scaleWidth="100%"
               android:drawable="@drawable/progress_secondary_holo_light" />
    </item>

    <item android:id="@android:id/progress">
        <scale android:scaleWidth="100%"
               android:drawable="@drawable/progress_primary_holo_light" />
    </item>

</layer-list>

Copy appropriate .png drawables from sdk/platforms/android-xx/data/res/drawable-xxx/ to your project and then in the code you can add:

progressBar.setProgressDrawable(getResources().getDrawable(R.drawable.progress_horizontal_holo_no_background_light));

Extra: Indeterminate Progress Bar

Pre-KitKat versions of indeterminate progress bars are pretty ugly and laggy. You can download new smooth progressBar called ButteryProgressBar. Just search for it on google (I cannot post any more links, because I am new here :[ ), add the class into your project and you can simply replace the previous ProgressBar with this code and have crispy indeterminate progress bar:

final ButteryProgressBar progressBar = new ButteryProgressBar(this);
progressBar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 24));

You might also need to simplify this code:

final TypedArray ta = c.obtainStyledAttributes(attrs, R.styleable.ButteryProgressBar);
try {
    mBarColor = ta.getColor(R.styleable.ButteryProgressBar_barColor,
            c.getResources().getColor(android.R.color.holo_blue_light));
    mSolidBarHeight = ta.getDimensionPixelSize(
            R.styleable.ButteryProgressBar_barHeight,
            Math.round(DEFAULT_BAR_HEIGHT_DP * mDensity));
    mSolidBarDetentWidth = ta.getDimensionPixelSize(
            R.styleable.ButteryProgressBar_detentWidth,
            Math.round(DEFAULT_DETENT_WIDTH_DP * mDensity));
} finally {
    ta.recycle();
}

to this code:

mBarColor = c.getResources().getColor(android.R.color.holo_blue_light);
mSolidBarHeight = Math.round(DEFAULT_BAR_HEIGHT_DP * mDensity);
mSolidBarDetentWidth = Math.round(DEFAULT_DETENT_WIDTH_DP * mDensity);

Hope I helped :)

Studdard answered 8/6, 2014 at 3:13 Comment(8)
The method removeGlobalOnLayoutListener(ViewTreeObserver.OnGlobalLayoutListener) from the type ViewTreeObserver is deprecatedClie
@ArjunU. what should we use instead>Expediency
@PatrickGeyer see #16190025Clie
I've tested the indeterminate progress bar (ButteryProgressBar) with ActionBarActivity but it doesn't work. How can I put a indeterminate progress bar under the ActionBar/Toolbar? This is the layout what I'm using: https://mcmap.net/q/86420/-how-do-i-use-drawerlayout-to-display-over-the-actionbar-toolbar-and-under-the-status-barWatusi
@GerritHoekstra Does the classic ProgressBar work? If it doesn't work either, it means there is a problem with my code. But if it does work, it would be a problem with the ButteryProgressBar and I cannot help you there... Anyway, can you tell me what line throws the the error? And is it runtime error or compile error? Give us more info :)Studdard
@Studdard It's not a runtime error or compile error but the ButteryProgressBar is not showing. I've debugged all values and came to the conclusion that contentView.getY() is returning 0. That's why the ButteryProgressBar is not showing. I've tried some things with getSupportActionBar().getHeight() and set that value to progressBar.setY but then the ButteryProgressBar appears in the middle of the ActionBar/Toolbar. Also tried this but then also it appears in the middle. I really appreciate any help you can provide.Watusi
@GerritHoekstra: In case the getSupportActionBar().getHeight() function works, you are almost there. The progressBar.setY() function sets the position relative to the top of the screen, including the status bar. So what you have to do is to setY(supportActionBarHeight + Math.ceil(25 * context.getResources().getDisplayMetrics().density)). The status bar is 25px high (mdpi) and scales with higher denisities (hdpi/xhdpi/...).Try it and tell me if it works.Studdard
Can anyone please specify the imports ?Illusionist
A
25

Extend Activities from following class to have a ProgressBar at the top(below ActionBar) of each, and a getProgressBar() method:

Parent class:

public abstract class ProgressActivity extends Activity {
    private ProgressBar mProgressBar;

    @Override
    public void setContentView(View view) {
        init().addView(view);
    }

    @Override
    public void setContentView(int layoutResID) {
        getLayoutInflater().inflate(layoutResID,init(),true);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        init().addView(view,params);
    }

    private ViewGroup init(){
        super.setContentView(R.layout.progress);
        mProgressBar = (ProgressBar) findViewById(R.id.activity_bar);
        return (ViewGroup) findViewById(R.id.activity_frame);
    }

    protected ProgressBar getProgressBar(){
        return mProgressBar;
    }
}

Layout (progress.xml):

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
    android:layout_width="match_parent" android:layout_height="match_parent">
<ProgressBar android:id="@+id/activity_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="-8dp"
    style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
    />
  <FrameLayout android:id="@+id/activity_frame"
      android:layout_width="match_parent"
      android:layout_height="fill_parent"
      />
</LinearLayout>
Amah answered 25/2, 2013 at 18:19 Comment(3)
It works... for the most part. The ProgressBar does look separate from the actionbar. I had to add the android:layout_marginTop="-8dp" to the ProgressBar in order to simulate the "shadow" effect. But for this method, it is probably more efficient to add it to the main layout itself. Yet now it works as intended. Thanks for the hack.Infantryman
@Infantryman Yes, You can add it to main layout if you are just using it for one activity. If you want multiple activities to have this feature, you can just extend the class shown above.Amah
Its work for me, but the progress bar appear under the tab host, how so i can make it above the tabhost and under action bar?Heyde
P
7

This is now a native behavior that can be obtained using SwipeRefreshLayout.

You can wrap your scrollable view with a SwipeRefreshLayout and then you just need to listen to onRefresh events:

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

    swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
    swipeLayout.setOnRefreshListener(this);
    swipeLayout.setColorScheme(android.R.color.holo_blue_bright, 
            android.R.color.holo_green_light, 
            android.R.color.holo_orange_light, 
            android.R.color.holo_red_light);
}


@Override public void onRefresh() {
    new Handler().postDelayed(new Runnable() {
        @Override public void run() {
            swipeLayout.setRefreshing(false);
        }
    }, 5000);
}

A nice and simple tutorial can be found in this blog.

Preuss answered 20/2, 2015 at 17:30 Comment(0)
C
4

I have compiled code from this thread as well as other threads on StackOverflow and created a project that can be used to implement ButteryProgessbar as well as "pull down to refresh". https://github.com/pauldmps/Gmail-like-Pull-Down-to-Refresh

Feel free to use the code in your application.

A big thanks to you guys.

Constantia answered 26/7, 2014 at 17:20 Comment(0)
P
3

Please use below code. just use one default style in progress bar as style="?android:attr/progressBarStyleHorizontal"

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
    <ProgressBar 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        android:indeterminate="true"

        />

</RelativeLayout>
Pierette answered 12/5, 2014 at 12:47 Comment(0)
I
0

Well , I did something similar for one of my pet apps and am not sure if that's the only way or the best way to do it , but it definitely works.

To begin with , use an overlay action bar and then set the background drawable to "null" so that the overlay action bar is transparent.

Now in you activity layout , set the top margin for your root view to "actionbar height" You can do it like this.

<RelativeLayout
    ...
    android:layout_marginTop="?android:attr/actionBarSize" />

Now the action is not going to hide any of your activity content.

The next thing you have to do now is - add a header view on top of your root view with height same as the action bar. Set a background color to it. This would now be the color of your action bar and since the action bar aligns perfectly on top pf this header view.

Now you can easily put a progress bar in the header view and align it to the bottom of the header view. To the user this would look as if the progress bar is on the action bar itself , just like chrome ..

Isidraisidro answered 6/4, 2014 at 20:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.