在 AppBarLayout 折叠之前防止 RecyclerView 滚动

7

我正在创建一个带有标题的 RecyclerView,当你向上滚动 RecyclerView 时标题会折叠。我可以通过以下布局实现这一点,使用透明的 AppBarLayoutMyCoolView 来作为标题。视差效果非常好。

然而,如果标题仍然可见并且我快速滑动 RecyclerView,RV 将缓慢滚动到顶部并有些项目将在 Toolbar 下面,直到 RV 到达视图的顶部。我已经尝试了一些 scrollFlags,但没有得到理想的结果。有没有建议如何改进 fling 体验,使项目不被截断?

查看视频,并观看它被 flinged 时的情况 --- https://www.dropbox.com/s/jppd6m7zo41k23z/20160609_151309.mp4?dl=0

<android.support.design.widget.CoordinatorLayout>

     <android.support.design.widget.AppBarLayout
         android:background="#00000000">

         <android.support.design.widget.CollapsingToolbarLayout
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

             <com.android.myapp.MyCoolView
                app:layout_collapseMode="parallax"/>

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

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

    <android.support.v7.widget.RecyclerView/>

</android.support.design.widget.CoordinatorLayout>
3个回答

5
可能的解决方案(未经测试)。向您的AppBarLayout添加一个OnOffsetChangedListener,并记住偏移值。首先,声明此字段:
private boolean shouldScroll = false;

接下来是onCreate方法:

AppBarLayout appbar = findViewById(...);
appbar.addOnOffsetChangedListener(new OnOffsetChangedListener() {
    @Override
    void onOffsetChanged(AppBarLayout appbar, int offset) {
        // Allow recycler scrolling only if we started collapsing.
        this.shouldScroll = offset != 0;
    }
});

现在,向您的RecyclerView添加一个滚动监听器。每当它尝试滚动时,如果AppBarLayout仍然展开,请将滚动还原:
RecyclerView recycler = findViewById(...);
recycler.addOnScrollListener(new OnScrollListener() {
    @Override
    void onScrolled(RecyclerView recycler, int dx, int dy) {
        // If AppBar is fully expanded, revert the scroll.
        if (!shouldScroll) {
            recycler.scrollTo(0,0);
        }
    }
});

这可能需要一些微调。我看到两个问题:

  • 如果 scrollTo() 回调 onScrolled(),可能会导致堆栈溢出。可以通过布尔值或通过添加/删除滚动监听器来解决
  • 也许你不仅想在 AppBarLayout 完全展开时防止滚动,而是更普遍地在 AppBarLayout 未折叠时防止滚动。这意味着您不必检查 offset!= 0,而是要检查 offset == appBarLayout.getTotalScrollRange()。我想。

这个很有效。偏移量始终小于零,除非完全展开。所以 offset > 0 不起作用。我做了类似于 shouldScroll = appbar.getTotalScrollRange() == Math.abs(offset) 的事情。没有遇到stackoverflow的问题,所以它似乎是有效的。 - ono
是的,我忘记了它是负数。你做得对。 - natario
1
这对我完全没有用。有人找到解决方案了吗?它让appbarlayout几乎无法使用。 - Jonathan S.

0
也许你可以像这样将 layout_behavior="@string/appbar_scrolling_view_behavior" 添加到你的 RecyclerView 中。
<android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" />

我有那个。我忘记在上面的XML中包含它了。 - ono

0
将RecyclerView包装在FrameLayout中可以解决这个问题。
您还需要将appbar_scrolling_view_behavior从RecyclerView移动到FrameLayout中,以便它可以正确地定位在AppBarLayout下方。
<android.support.design.widget.CoordinatorLayout>

    <android.support.design.widget.AppBarLayout
        android:background="#00000000">

        <android.support.design.widget.CollapsingToolbarLayout
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <com.android.myapp.MyCoolView
               app:layout_collapseMode="parallax"/>

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

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

    <!-- BEGIN SOLUTION -->
    <!-- the layout behavior needs to be set on the FrameLayout, not the RecyclerView -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        >

        <!--This RecyclerView MUST be wrapped in a FrameLayout-->
        <!--This prevents the RecyclerView from going behind the AppBarLayout-->
        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />

    </FrameLayout>
    <!-- END SOLUTION -->

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

@faezesaghafi 很抱歉这对你不起作用。自从我在多个项目上实施它以来,我已经看到它成功地工作了几次。你是否将layout_behavior移动到FrameLayout中?此外,布局的其余部分是否正确,AppBarLayout中的元素是否正确? - themichaelscott

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