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?
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();
}
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();
}
}
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With