Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 613
Bart Burg Avatar asked Nov 12 '15 08:11

Bart Burg


3 Answers

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();
}
like image 186
Vikram Avatar answered Oct 17 '22 11:10

Vikram


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();
    }

}
like image 43
Webserveis Avatar answered Oct 17 '22 11:10

Webserveis


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();
like image 38
Anoop M Maddasseri Avatar answered Oct 17 '22 13:10

Anoop M Maddasseri