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

Java 在CoordinatorLayout中禁用AppBarLayout时出现RecyclerView滚动故障?,java,android,kotlin,android-recyclerview,android-coordinatorlayout,Java,Android,Kotlin,Android Recyclerview,Android Coordinatorlayout,背景 我有一个基于CoordinatorLayout的视图,它有一个AppbarLayout和RecyclerView作为直接子视图。AppBarLayout包含一个搜索栏,当您向上滚动RecyclerView时,该搜索栏将折叠。此视图的XML为: <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/androi

背景

我有一个基于
CoordinatorLayout
的视图,它有一个
AppbarLayout
RecyclerView
作为直接子视图。
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>
启用搜索模式
代码为:

private fun enableSearchMode() {
    ...

    itemRecyclerView.isNestedScrollingEnabled = false

    ...
}
问题

这个设置似乎工作得很好。。。大多数时候。随机-可能有1%的时间-当您触摸
EditText
以启用搜索模式时,出现问题,我无法再有效地滚动
RecyclerView
。就好像它被卡在了列表的顶部。如果您试图滚动到列表的底部,滚动会突然移动,通常会跳回到列表的顶部。一旦禁用搜索模式,问题就会消失

// Disable search mode
itemRecyclerView.isNestedScrollingEnabled = true
尽管进行了大量的测试,但我无法始终如一地重现这个问题,也无法确定行动的进展会导致它。这只是随机发生的,好像在
协调器布局中发生了某种竞争条件

我已经在我的应用程序中删除了太多的代码来隔离问题,我相信当我将
isNestedScrollingEnabled
设置为false时,问题就会发生。也就是说,我还尝试了另一种方法,在滚动
RecyclerView
时禁用
AppBarLayout
移动,这是覆盖
AppBarLayout
的行为,如前所述。奇怪的是,这导致了同样的问题。如果我没有通过这种方式或通过设置
isNestedScrollingEnabled
false禁用
AppBarLayout
,则问题永远不会出现

这里发生了什么

这里发生了什么

isNestedScrollingEnabled
设置为false实际上会中断
itemRecyclerView
as滚动子项
AppBarLayout
as其父项之间的通信。正常的行为是
itemRecyclerView
通知它的父级
AppBarLayout
它的滚动进度,父级应该通过计算它的折叠高度,给出任何滚动进度和所有其他内容来对其作出反应
我发现将
isNestedScrollingEnabled
设置为false会导致
RecyclerView
不循环其视图。我不能确切地说这是否是真的,但如果是真的,我想这就是那个小故障的原因

我想提出的解决方案是以编程方式将
scroll\u标志更改为
NO\u 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);
            }
        });

如果我希望将
AppBarLayout
折叠,但同时禁用,那么情况如何?当前,如果我在应用程序栏已经折叠时将标志更改为
NO_SCROLL
,它会立即完全展开,而不会出现动画,即使我希望它被折叠。因此,即使
AppBarLayout
被折叠,编辑文本仍然可见?当
AppBarLayout
被折叠时,我能看一下您的UI吗?很抱歉,不清楚。当
AppBarLayout
折叠时,应隐藏
EditText
。从UI透视图上仍然可见的是
RecyclerView
。也就是说,如果我调用
appBarLayout.setExpanded(false)
EditText
将设置动画并折叠,并且不再可见。但是,一旦它被折叠,如果我像你提到的那样将标志设置为
NO_SCROLL
,那么
AppBarLayout
就不可能保持折叠状态。当我设置标志时,它立即返回到其完全展开的高度。我只提到使用
NO_SCROLL
标志来防止
AppBarLayout
searchField
有焦点时被
折叠,我想这就是你在问题中提出的问题。如果我的答案确实回答了你的问题,那么你应该接受答案,并根据SO规则为此问题创建一个新线程。对不起,如果我听起来很粗鲁。另外,如果
AppBarLayout
已折叠,并且您不希望
AppBarLayout
对滚动做出反应,那么您的
搜索字段将永远看不到。所以,用户将无法过滤结果。我想那会是个糟糕的用户体验。如果我错了,请纠正我。
            // 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);
            }
        });