在CoordinatorLayout中禁用AppBarLayout时,RecyclerView滚动出现故障吗?

时间:2020-07-05 22:20:42

标签: java android kotlin android-recyclerview android-coordinatorlayout

背景

我有一个基于CoordinatorLayout的视图,其中有一个AppbarLayoutRecyclerView作为直接子级。 AppBarLayout包含一个搜索栏,当您向上滚动RecyclerView时,它会折叠。此视图的XML是:

<androidx.coordinatorlayout.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/search_coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/search_app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            app:elevation="0dp">

        <!-- Search bar -->
        <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/search_constraint"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:focusableInTouchMode="true"
                android:focusable="true"
                app:layout_scrollFlags="scroll|enterAlwaysCollapsed|snap">

            <!-- Abbreviated -->
            <EditText .../>

            <!-- Abbreviated -->
            <ImageView .../>

        </androidx.constraintlayout.widget.ConstraintLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <!-- List -->
    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/scroll_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

当您点击搜索栏中的EditText视图时,将启用所谓的“搜索模式”。搜索模式所做的就是在滚动AppBarLayout时禁止RecyclerView崩溃。然后,用户可以输入搜索栏-过滤RecyclerView中的项目-然后他们可以滚动列表而不会折叠搜索栏。我加入了EditText onFocus事件以执行此操作:

searchField.setOnFocusChangeListener { _, hasFocus ->
    if (hasFocus) {
        // When user taps the search bar, enable search mode
        enableSearchMode()
    }
}

enableSearchMode代码是:

private fun enableSearchMode() {
    ...

    itemRecyclerView.isNestedScrollingEnabled = false

    ...
}

问题

此设置似乎在大多数情况下都能完美运行。随机地-大约有1%的时间-当您触摸EditText启用搜索模式时,出现了问题,并且我无法再有效滚动RecyclerView了。就像它停留在列表的顶部。如果您尝试滚动到列表的底部,则滚动会突然跳动,并且通常会跳回到列表的顶部。禁用搜索模式后,问题就消失了。

// Disable search mode
itemRecyclerView.isNestedScrollingEnabled = true

尽管进行了大量测试,但我仍无法始终如一地重现该问题或确定导致该问题的行动进展。它只是随机发生,好像CoordinatorLayout中正在发生某种竞争状况一样。

我在应用程序中剥离了太多代码以隔离问题,以至于我确信将isNestedScrollingEnabled设置为false时,问题就发生了。也就是说,我还尝试了一种替代方法,即在AppBarLayout滚动时禁止RecyclerView移动,这将覆盖here所述的AppBarLayout的行为。奇怪的是,这导致了同样的问题。如果我既不通过这种方式也不通过将AppBarLayout设置为false来禁用isNestedScrollingEnabled,则问题永远不会出现。

这是怎么回事?!?

1 个答案:

答案 0 :(得分:1)

这是怎么回事?!?

实际上,将isNestedScrollingEnabled设置为false会中断您作为{strong>滚动孩子的itemRecyclerView和作为其父母AppBarLayout之间的通信>。正常行为是itemRecyclerView通知其父级AppBarLayout滚动进度,父级应该对此做出反应,方法是在给定滚动进度和所有其他内容的情况下计算其折叠高度
我发现在某处将isNestedScrollingEnabled设置为false会导致RecyclerView 不回收其视图。我不能确切地说出是否正确,但如果是这样,我认为这是造成这种故障的原因。

我想提出的解决方案是以编程方式将scroll_flags更改为NO_SCROLL,以使AppBarLayout在其子滚动视图的滚动中不会做出反应/滚动。 /> 虽然,它在Java中,但是下面的代码片段应该可以帮助您。

            // get a reference for your constraint layout
            ConstraintLayout constraintLayout = findViewById(R.id.search_constraint);
            // get the layout params object to change the scroll flags programmatically
            final AppBarLayout.LayoutParams layoutParams = (AppBarLayout.LayoutParams) constraintLayout.getLayoutParams();
    
            // flags variable to switch between
            final int noScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL;
            final int defaultScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL 
                | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
                | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP;

        // now we will set appropriate scroll flags according to the focus
        searchField.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                layoutParams.setScrollFlags(hasFocus ? noScrollFlags : defaultScrollFlags);
            }
        });
相关问题