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