問題
以下の動画のように末尾や先頭にスクロールした後に一回クリックイベントが効かなくなります。しかし、暫く待てばクリックすることができます。
これはGooglePayアプリでも発生しています。
再現環境
自分の場合はBottomSheetにFragmentとしてRecyclerViewを入れて使っていました。
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/bottom_sheet_behavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
in bottom_sheet
<androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" />
原因の特定
- スクロール後にRippleが発生していない → NestedScroll絡みの問題である。
- 親にandroid:nestedScrollingEnabled="false"を設定する → 直る(BottomSheetや、NestedScrollしたい場面では問題がある)
- スクロールの状態に合わせてisNestedScrollingEnabledを書き換えれば良いのではないかと考える。
- 下記のコードを書く
- 端までスクロールした際にSCROLL_STATE_IDLEが遅れて呼ばれていることに気がつく。更にゆっくりスクロールした場合にその時間が短いことに気がつく →見えない慣性スクロールが発生している
- 親のNestedScrollが終わっているのに(他にスクロールする物が無いのに)子のスクロールを止めていないのが原因だと判明する。
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { when (newState) { RecyclerView.SCROLL_STATE_DRAGGING -> { Log.d("TAG", "SCROLL_STATE_DRAGGING") } RecyclerView.SCROLL_STATE_SETTLING -> { Log.d("TAG", "SCROLL_STATE_SETTLING") } RecyclerView.SCROLL_STATE_IDLE -> { Log.d("TAG", "SCROLL_STATE_IDLE") } } } })
解決方法
NestedScrollしない場合
android:nestedScrollingEnabled="false"を設定する
NestedScrollする場合
今回の場合
1. 子のRecyclerViewのスクロールが末尾まで行く
2. 親のNestedScrollが開始する。
3. (ここからが今回やること)親のViewを以下に差し替え、子のスクロールを停止する
class BottomSheetContainerLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean { if (target is RecyclerView) { target.stopScroll() } return super.onStartNestedScroll(child, target, nestedScrollAxes) } }
おわりに
Googleのアプリでもなってるし、DroidKaigiのアプリでもなってるし、他にも様々なアプリで同様の問題を発見したのでかなりハマる所だなと思った。解決まで苦労した。