如何重置由CoordinatorLayout控制的工具栏位置?

55

我正在开发的应用程序包括一个导航抽屉,该抽屉已在 Activity 中实现。该 Activity 的布局如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.v4.widget.DrawerLayout
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <include
            android:id="@+id/appbar"
            layout="@layout/appbar" />

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

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/header_drawer"
        app:menu="@menu/menu_nav">
    </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>

</FrameLayout>
这是一个非常常见的模式,唯一经常更改的是容器布局内的Fragment。
如果任何的Fragment具有滚动元素,则在滚动时,CoordinatorLayout将愉快地进行位置平移,包括Toolbar / AppBarLayout。
真正的问题在于,当Fragment被替换时,Toolbar的位置保持不变,即使Toolbar被隐藏,它也会保持不变,这并不是预期的结果。
结果如下:
这个:
得到卡住了:
如何为这种情况重置Toolbar的位置?
编辑: 可能存在一个错误,AppBarLayout偏移更改侦听器仅在重新启动应用程序(按返回按钮并打开应用程序)时调用,并在强烈的挥舞后停止再次调用。

1
你找到解决方案了吗? - mthandr
还没有,有位好心的人在这里回答了我的问题,但他在我讨论之前就删除了他的帖子。他说他使用setTranslationY()来操作AppBarLayout而不是重置它,并提到这应该可以解决问题,但对我来说并没有用,我仍在寻找答案。一旦我找到答案,我一定会发布解决方案。 - razzledazzle
已修复。忘记在工具栏中添加 app:layout_scrollFlags="scroll" - Froyo
1
还有一件事。即使在recyclerview中没有任何条目,工具栏仍然会滚动。有任何想法如何解决? - Froyo
@Froyo 让你的 Activity 或 Fragment 实现 AppBarLayout.OnOffsetChangedListener 接口,然后添加 mAppBar.addOnOffsetChangedListener(this);@Override public void onOffsetChanged(AppBarLayout appBarLayout, int offset) { if(recycler.canScrollVertically(1)) { appBarLayout.setTranslationY(0); } } 代码块。基本上,检查你的滚动视图是否可以滚动并锁定 AppBarLayout。 - razzledazzle
显示剩余2条评论
9个回答

48

要重置滚动状态,只需获取AppBarLayout.Behavior对象。

CoordinatorLayout coordinator = (CoordinatorLayout) findViewById(R.id.coordinator);
AppBarLayout appbar = (AppBarLayout) findViewById(R.id.appbar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appbar.getLayoutParams();
AppBarLayout.Behavior behavior = (AppBarLayout.Behavior) params.getBehavior();       

并手动调用 onNestedPreScroll 方法:

int[] consumed = new int[2];
behavior.onNestedPreScroll(coordinator, appbar, null, 0, -1000, consumed);

如果您希望通过动画平滑地重置,请尝试调用onNestedFling

behavior.onNestedFling(coordinator, appbar, null, 0, -1000, true);

1
对我来说,只有通过设置正的“velocityY”值才能使折叠的AppBarLayout正常工作。使用负的“velocityY”值的上述代码没有效果。我在NestedScrollViewonTouchListener中的ACTION_UP操作后调用它。我想实现快照行为-始终折叠或展开AppBarLayout - theb1uro
谢谢您的回答,这是一个很好的解决方案。我在抽屉打开回调函数中使用了onNestedFling(),效果非常不错。 - razzledazzle
我有和@theb1uro完全相同的经验。只有在'velocityY'值为正时,才能使其折叠。负的'velocityY'值无效... - Tomas
1
params.getBehavior() 返回 null。在 xml 中,layout_behaviour 标签设置在兄弟 NestedScrollView 上,而不是 AppBarLayout 上。请帮忙! - User31689
在CoordinatorLayout内与ViewPager+TabLayout很好地配合使用。 - Nati Dykstein
显示剩余2条评论

40

首先,在您的 MainActivity 中获取 AppBarLayout 引用,然后在被替换的片段的暂停状态中使用下面的方法来展开工具栏:

MainActivity.appbar.setExpanded(true,true);

关闭工具栏的方法:

MainActivity.appbar.setExpanded(false,true);
第二个参数用于平滑地滚动工具栏。

这并不涵盖当您想要锁定AppBarLayout的用例。 - razzledazzle
1
AppBarLayout没有setExpanded方法? - Muhammad Babar
3
更新你的依赖库,兄弟。 - Pkmmte

14

将您的支持库更新到v23,然后您就可以使用:

appBarLayout.setExpanded(true/false);

public void setExpanded (boolean expanded)

设置该AppBarLayout是否展开,若已经布局则进行动画。

与AppBarLayout的滚动一样,此方法依赖于该布局是CoordinatorLayout的直接子项。

expanded 若为true则布局应完全展开,若为false则应完全折叠。


1
这无法将AppBarLayout锁定在原位。但是对于常规情况,它确实有效。 - razzledazzle

3

啊,这绝对是事实,我在源代码中看到了WeakReferences,但从未想过它们会如此容易地被GCed。这也解释了为什么将监听器作为onResume()下的字段引用比在onCreate()期间初始化匿名类更好。像类一样实现监听器是一个保证的解决方案,正如链接中所提到的。非常感谢您的输入! - razzledazzle
根据那篇帖子,问题已经被解决了。 - Michael

2

在片段更改之前,我使用这些代码。

scrollingElement.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
scrollingElement.dispatchNestedPreScroll(0, -Integer.MAX_VALUE, null, null);
scrollingElement.stopNestedScroll();

3
你能加一些解释吗? - Peanut
这个想法似乎来自于startNestedScroll()文档,详见 https://developer.android.com/reference/android/view/View.html#startNestedScroll%28int%29。 - Dan Dar3

2
根据文档所说:

AppBarLayout支持
void setExpanded (boolean expanded, boolean animate);
设置此AppBarLayout是否展开。

  • expanded boolean: 如果布局应完全展开,则为true,如果应完全折叠,则为false
  • animate boolean: 是否要动画到新状态
因此,首先需要...
AppBarLayout appBarLayout = findViewById(R.id.appBarLayout);

然后扩展布局。
appBarLayout.setExpanded(true, true); // with animation
appBarLayout.setExpanded(true, false); // without animation  

折叠布局。
appBarLayout.setExpanded(false, true); // with animation
appBarLayout.setExpanded(false, false); // without animation 

2

android.support.design.widget.CoordinatorLayout 中的 FrameLayout 替换或包装为 android.support.v4.widget.NestedScrollView,这样就可以自动完成工作,而无需进行其他任何操作……所以它看起来像这样:

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <include
        android:id="@+id/appbar"
        layout="@layout/appbar" />

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

0
// Get the reference for appbar from layout
AppBarLayout appbar = rootView.findViewById(R.id.appbar);
//boolean expanded, boolean animate
appbar.setExpanded(true, true);

0

我曾经遇到过类似的问题。当我动态调整布局大小时,我的布局要么放置得比需要的位置更高(就像这里一样),要么放置得更低。这是一个在FrameLayout中以编程方式替换的Fragment。 我所做的是:在改变FrameLayout大小时重新创建我的Fragment。

myFragment = new MyFragment();
getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_layout, myFragment)
                .commit();

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