Android的SupportActionBar在显示/隐藏时没有动画效果

9

我看到很多关于如何禁用Android ActionBar动画的问题,但我恰恰相反,我希望有动画效果,而且应该默认就有,对吧?

我认为问题在于我的自定义工具栏:

<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>

我在活动中设置如下:

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

工具栏可以正常工作,但是当我调用以下任一方法时没有动画效果:
protected void hideActionBar(){
    ActionBar ab = getSupportActionBar();
    if (ab.isShowing()) {
        ab.hide();
    }

}

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

这是什么原因呢?

这可能是一次尝试:您可能需要显式启用动画。尝试 getSupportActionBar().setShowHideAnimationEnabled(true)。请注意,动画显示和隐藏仅适用于API 14及以上版本。 - Vikram
我之前尝试过,但它不起作用。无论如何,它应该默认为动画。在这个实现中,我有一些阻止动画的东西,但它在哪里? - Bart Burg
@Bart Burg,你在使用什么 - ActionBar 还是 ToolBar - Vladimir Markeev
@VladimirMarkeev,工具栏,虽然我已经设置它:setSupportActionBar(toolbar); - Bart Burg
@Bart Burg,尝试像Anoop M回复的那样将动画应用于ToolBar,请参考此链接 - https://dev59.com/018d5IYBdhLWcg3wsT4b - Vladimir Markeev
5个回答

20
请忽略我之前的评论。我对此进行了一些研究,发现我之前的建议毫无意义。
当您调用setSupportActionBar(Toolbar)时,会发生以下情况:
public void setSupportActionBar(Toolbar toolbar) {
    ....
    ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity) mContext).getTitle(),
            mAppCompatWindowCallback);
    mActionBar = tbab;
    ....
}
因此,随后对 getSupportActionBar() 的调用将返回 ToolbarActionBar 的实例。看一下这个类如何实现我们感兴趣的特性: setShowHideAnimationEnabled(boolean): 正如您指出的那样,该方法不会产生任何差异。
@Override
public void setShowHideAnimationEnabled(boolean enabled) {
    // This space for rent; no-op.
}

show(): 仅使 ActionBar 可见 - 不支持动画效果。

@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(): 只是让 ActionBar 再次可见 - 没有动画支持。

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

show()hide() 中的注释提示开发者应该提供动画过渡效果。也许可以像这样:

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

mToolbar.animate() 部分是凭记忆编写的 - 语法可能不正确 :(。您还可以添加 .alpha(0(隐藏期间)或1(显示期间)) 以使过渡效果更好。

实现:

现在应该清楚的是,getSupportActionBar().show() & hide() 不关心您对 Toolbar 进行了什么操作。此外,Toolbar 应被视为 Activity 中的任何其他 View。考虑到这些要点,问题归结为 - 我们如何 动画地 隐藏(然后显示)一个 View。由于我们需要 Activity 内容随着隐藏(或显示)的 Toolbar 滑动,因此我建议使用以下实现。请注意,这只是一个基本的例行程序。您肯定可以对此进行微调,或者提出完全不同(读作更好的)动画转换:

// 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();
}
在您的评论中,提到了I do see an animation now but the space still gets reserved for the toolbar until ab.hide() happens。对我来说,这意味着您正在使用AppBarLayout来托管Toolbar。 如果不是,请告诉我,我们会想出其他办法。
最后,对这些方法的调用将根据以下方式进行分派:
if (getSupportActionBar().isShowing()) {
    hideActionBar();
} else {
    showActionBar();
}

感谢您进行的广泛研究。我现在看到了动画,但是直到执行ab.hide()之前,空间仍然会被保留给工具栏。这将使内容在动画之后跳起来,而不是与动画一起。 - Bart Burg
@BartBurg 感谢您的编辑。我已经在上面添加了更多信息和示例代码。看看这是否可以让您按照自己的想法继续前进。 - Vikram

10

如果你想使用默认动画隐藏或显示,请尝试以下方法:

在父级toolbar中添加android:animateLayoutChanges="true"。在这种情况下,应该在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>

代码切换隐藏和显示

final ActionBar actionBar = getSupportActionBar();

if (actionBar != null) {

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

}

8
您可以在XML中使用以下代码,作为工具栏父元素,在调用隐藏/显示时进行动画处理。
android:animateLayoutChanges="true"

OR this

Hiding

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

显示

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

0

我建议你尝试cheesesquare示例。这是一个可以折叠和展开工具栏的布局。

<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>

0
如果您在活动中使用CoordinatorLayout,请使用以下代码:
<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" />

app:layout_scrollFlags="scroll|enterAlways" 这一行代码会使得我们的工具栏在用户向下滚动列表时从屏幕上滚动出去,而当用户开始向上滚动时,工具栏将再次显示出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接