背景
我有一个基于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>
当您点击搜索栏中的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
,则问题永远不会出现。
这是怎么回事?!?
答案 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);
}
});