Search code examples
androidkotlinandroid-recyclerviewandroid-viewpager2itemtouchhelper

How to use ItemTouchHelper.SimpleCallback with ViewPager2?


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


Solution

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