How to use ItemTouchHelper.SimpleCallback with ViewPager2?

1.4k Views Asked by At

I have created a image slider using ViewPager2, I need a functionality that when I swipe up the image should be removed. I already worked with RecyclerView. So I remember I can use ItemTouchHelper.SimpleCallback for swipe to remove functionality. But attachToRecyclerView method requires a RecyclerView not a ViewPager2 even though viewpager2 uses RecyclerView adapter.

Kotlin:

ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

    }
}).attachToRecyclerView(binding.viewPager)

viewpager2 slider

2

There are 2 best solutions below

2
On BEST ANSWER

The RecyclerView of the ViewPager2 is not accessible by default, but you can enforce its accessibility using reflection.

In that you need to access the RecyclerView by reflecting its declared field name using getDeclaredField(), and for RecyclerView it is: mRecyclerView (you can check it in the ViewPager2 class)

Then use setAccessible() to make this field accessible in order to allow using it for the ItemTouchHelper.

Here is an extension function to return the ViewPager2 ReyclerView:

fun ViewPager2.getRecyclerView(): RecyclerView? {
    try {
        val field = ViewPager2::class.java.getDeclaredField("mRecyclerView")
        field.isAccessible = true
        return field.get(this) as RecyclerView
    } catch (e: NoSuchFieldException) {
        e.printStackTrace()
    } catch (e: IllegalAccessException) {
        e.printStackTrace()
    }
    return null
}

And you can use it like:

ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

    }
}).attachToRecyclerView(binding.viewPager.getRecyclerView())

Preview:

UPDATE:

Thanks to @SimpleAndroid answer, there is a nice way for obtaining the RecyclerView:

viewPager.children.find { it is RecyclerView }?.let {
    ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP) {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            return false
        }

        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

        }
    }).attachToRecyclerView(it as RecyclerView)
}
0
On

Here is another way to get RecyclerView from ViewPager2. I assume you have an adapter named is PictureItemAdapter:

class PictureItemAdapter(private val recyclerViewAttached: (RecyclerView) -> Unit) : RecyclerView.Adapter<PictureItemViewHolder>() {

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        recyclerViewAttached(recyclerView)
    }
    ...
}

And you will receive the Recycler view from the recyclerViewAttached closure

val pictureItemAdapter = PictureItemAdapter{ recyclerView ->
    ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP) {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            return false
        }

        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

        }
    }).attachToRecyclerView(recyclerView)
}
binding.viewPager.adapter = pictureItemAdapter

Note: Both RecyclerViewAdapter and FragmentStateAdapter have a method onAttachedToRecyclerView.