Bart Burg
Bart Burg

Reputation: 4924

Android SupportActionBar not animating on show / hide

I find a lot of questions about how to disable the animation of an Android ActionBar. I have exactly the opposite problem. I do want the animation but it should work out of the box right?

I think the problem lies with my custom toolbar:

<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar_parent"
    android:layout_width="match_parent"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
        android:layout_width="match_parent" android:layout_height="@dimen/menu_height"
        android:minHeight="@dimen/menu_height"
        android:background="@color/backgroundColor" app:popupTheme="@style/AppTheme.PopupOverlay"
        android:theme="@style/ToolbarColoredBackArrow"/>

</android.support.design.widget.AppBarLayout>

Which I set in mij activity like so:

 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 setSupportActionBar(toolbar);

The toolbar works fine, but no animation when I call either of these methods:

protected void hideActionBar(){
    ActionBar ab = getSupportActionBar();
    if (ab.isShowing()) {
        ab.hide();
    }

}

protected void showActionBar(){
    ActionBar ab = getSupportActionBar();
    if (!ab.isShowing()) {
        ab.show();
    }
}

What is the reason of this?

Upvotes: 9

Views: 13624

Answers (5)

Codelaby
Codelaby

Reputation: 2883

If you hide or show with default animation try this:

Add android:animateLayoutChanges="true" in your parent toolbar In this case in AppBarLayout

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay"
    android:animateLayoutChanges="true">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay"

        />

</android.support.design.widget.AppBarLayout>

Code for toggle hide and show

final ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {

    if (actionBar.isShowing()) {
        actionBar.hide();
    } else {
        actionBar.show();
    }

}

Upvotes: 10

Jaydev
Jaydev

Reputation: 1812

If you are using a CoordinatorLayout in your activity, use this:

<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    app:layout_scrollFlags="scroll|enterAlways" />

The app:layout_scrollFlags="scroll|enterAlways" line will cause our Toolbar to scroll off the screen when the user scrolls down the list and as soon as he starts scrolling up the Toolbar will show up again.

Upvotes: 0

Vikram
Vikram

Reputation: 51571

Please discard my comment above. I did some research on this and my suggestion above is of no consequence.

When you call setSupportActionBar(Toolbar), the following happens:

public void setSupportActionBar(Toolbar toolbar) {
    ....
    ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity) mContext).getTitle(),
            mAppCompatWindowCallback);
    mActionBar = tbab;
    ....
}

So, subsequent calls to getSupportActionBar() return an instance of ToolbarActionBar. Take a look at how this class implements the feature we're interested in:

setShowHideAnimationEnabled(boolean): As noted by you, this method makes no difference.

@Override
public void setShowHideAnimationEnabled(boolean enabled) {
    // This space for rent; no-op.
}

show(): only makes the actionbar visible - no animation support.

@Override
public void show() {
    // TODO: Consider a better transition for this.
    // Right now use no automatic transition so that the app can supply one if desired.
    mDecorToolbar.setVisibility(View.VISIBLE);
}

hide(): only makes the actionbar visible - again, no animation support.

@Override
public void hide() {
    // TODO: Consider a better transition for this.
    // Right now use no automatic transition so that the app can supply one if desired.
    mDecorToolbar.setVisibility(View.GONE);
}

The comments in show() and hide() hint that the developer should provide the animated transition. Perhaps, something like this:

protected void hideActionBar(){
    final ActionBar ab = getSupportActionBar();
    if (ab != null && ab.isShowing()) {
        if(mToolbar != null) {
            mToolbar.animate().translationY(-112).setDuration(600L)
                    .withEndAction(new Runnable() {
                        @Override
                        public void run() {
                            ab.hide();
                        }
                    }).start();
        } else {
            ab.hide();
        }
    }
}

protected void showActionBar(){
    ActionBar ab = getSupportActionBar();
    if (ab != null && !ab.isShowing()) {
        ab.show();
        if(mToolbar != null) {
            mToolbar.animate().translationY(0).setDuration(600L).start();
        }
    }
}

The mToolbar.animate()..... part was written from memory - syntax may not be correct :(. You can also add .alpha(0(during hide) or 1(during show)) to make the transition look better.

Implementation:

What should be clear by now is that getSupportActionBar().show() & hide() don't care what you do with your Toolbar along with these method calls. Moreover, the Toolbar is to be treated as any other View inside your Activity. Keeping these points in mind, the problem boils down to - how do we animate hiding (& later, showing) of a View. Since we need the Activity content to slide with the hiding (or showing) Toolbar, I suggest the following implementation. Note that this is just a basic run-of-the-mill routine. You can surely fine tune this, or come up a totally different (read better) animated transition:

// holds the original Toolbar height.
// this can also be obtained via (an)other method(s)
int mToolbarHeight, mAnimDuration = 600/* milliseconds */;

ValueAnimator mVaActionBar;

void hideActionBar() {
    // initialize `mToolbarHeight`
    if (mToolbarHeight == 0) {
        mToolbarHeight = mToolbar.getHeight();
    }

    if (mVaActionBar != null && mVaActionBar.isRunning()) {
        // we are already animating a transition - block here
        return;
    }

    // animate `Toolbar's` height to zero.
    mVaActionBar = ValueAnimator.ofInt(mToolbarHeight , 0);
    mVaActionBar.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // update LayoutParams
            ((AppBarLayout.LayoutParams)mToolbar.getLayoutParams()).height
                    = (Integer)animation.getAnimatedValue();
            mToolbar.requestLayout();
        }
    });

    mVaActionBar.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);

            if (getSupportActionBar() != null) { // sanity check
                getSupportActionBar().hide();
            }
        }
    });

    mVaActionBar.setDuration(mAnimDuration);
    mVaActionBar.start();
}

void showActionBar() {
    if (mVaActionBar != null && mVaActionBar.isRunning()) {
        // we are already animating a transition - block here
        return;
    }

    // restore `Toolbar's` height
    mVaActionBar = ValueAnimator.ofInt(0 , mToolbarHeight);
    mVaActionBar.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // update LayoutParams
            ((AppBarLayout.LayoutParams)mToolbar.getLayoutParams()).height
                    = (Integer)animation.getAnimatedValue();
            mToolbar.requestLayout();
        }
    });

    mVaActionBar.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);

            if (getSupportActionBar() != null) { // sanity check
                getSupportActionBar().show();
            }
        }
    });

    mVaActionBar.setDuration(mAnimDuration);
    mVaActionBar.start();
}

In your comment, you mentioned I do see an animation now but the space still gets reserved for the toolbar until ab.hide() happens. To me, this implies that you are using AppBarLayout to host the Toolbar. If this is not so, let me know and we'll figure something out.

Lastly, calls to these methods will be dispatched based on:

if (getSupportActionBar().isShowing()) {
    hideActionBar();
} else {
    showActionBar();
}

Upvotes: 20

Mohammad Hossein Gerami
Mohammad Hossein Gerami

Reputation: 1388

I suggested you try cheesesquare sample. this is a layout that collapsing & expanding toolbar.

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:layout_scrollFlags="scroll|enterAlways|snap" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_done" />

</android.support.design.widget.CoordinatorLayout>

Upvotes: 0

Anoop M Maddasseri
Anoop M Maddasseri

Reputation: 10549

You can use the below code in the xml ie, for the toolbar parent for animating it when you call hide/show.

android:animateLayoutChanges="true"

OR this

Hiding

toolbarParent.animate().translationY(-toolbarHeight).setInterpolator(new AccelerateInterpolator(2)).start();

Showing

toolbarParent.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();

Upvotes: 9

Related Questions