1

I have a RecyclerView inside a CoordinatorLayout along with a FAB :

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <TextView
        android:id="@+id/empty"
        android:visibility="invisible"
        android:textSize="@dimen/title"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/margin"
        android:src="@drawable/add"
        android:layout_gravity="bottom|right"
        app:fabSize="normal"
        app:tint="@android:color/white" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

When attaching behaviour to FAB like this :

(fab.layoutParams as CoordinatorLayout.LayoutParams).behavior = ScrollAwareFABBehavior()

Then first click event is not dispatched to item views click listeners (ViewHolder.itemView.setOnClickListener()) when FAB is hidden. Here is behaviour implementation :

class ScrollAwareFABBehavior : FloatingActionButton.Behavior() {

    override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
    }

    override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
        if (dyConsumed > 0) {
            child.hide(object : OnVisibilityChangedListener() {
                override fun onHidden(fab: FloatingActionButton) {
                    super.onHidden(fab)
                    fab.visibility = View.INVISIBLE
                }
            })
        } else if (dyConsumed < 0) {
            child.show()
        }
    }
}

OnStartNestedScroll gets called but not the item click listener unless I really take care not to scroll screen from 1px when clicking recycler item.

Everything works as expected when :

  1. removing behaviour from FAB
  2. removing OnVisibilityChangedListener from behaviour implementation
  3. or removing only fab.visibility = View.INVISIBLE from behaviour impl

But without the fab.visibility = View.INVISIBLE FAB never shows up again after it has been hidden by the FabBehaviour (F.A.B Hides but Doesn't Show)...

If anyone can help me show/hide the FAB using behaviour and not have first click not delivered to item view, it would be great !

avianey
  • 5,545
  • 3
  • 37
  • 60

1 Answers1

0

Using a RecyclerView.OnScrollListener helped me solve this issue in a simpler manner :

// (fab.layoutParams as CoordinatorLayout.LayoutParams).behavior = ScrollAwareFABBehavior()
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
    var scrolling : Boolean = false
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        if (!scrolling) return
        if (dy > 0) {
            fab.hide()
        } else {
            fab.show()
        }
    }
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        scrolling = newState == RecyclerView.SCROLL_STATE_DRAGGING
    }
})
avianey
  • 5,545
  • 3
  • 37
  • 60