10

I'm using ViewPager2 in my App, it has three pages and each page show different contents, the issue is that it's too sensible that the ViewPager will change the pages even if someone is scrolling down and for mistake moves the finger a bit to right or left.

So how could i make the ViewPager less sensitive? The ViewPager2 is created in Activity

Here is my code where i initialize the ViewPager:

private lateinit var viewPager: ViewPager2
private lateinit var adapterView: ViewPagerAdapter

private fun initViewPager() {
    adapterView.addFragment(ElencoFragment())
    adapterView.addFragment(TestataFragment())
    adapterView.addFragment(CorpoFragment())
    adapterView.notifyDataSetChanged()

    viewPager.adapter = adapterView
    ...
}


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewPager = findViewById(R.id.pager)
    adapterView = ViewPagerAdapter(supportFragmentManager, lifecycle)
    ...
}

My Adapter:

 class ViewPagerAdapter
        (manager: FragmentManager, lifecycle: Lifecycle) :
        FragmentStateAdapter(manager, lifecycle) {

        private var fragmentList: ArrayList<Fragment> = ArrayList()

        override fun getItemCount(): Int {
            return fragmentList.size
        }

        override fun createFragment(position: Int): Fragment {
            return fragmentList[position]
        }

        fun addFragment(fragment: Fragment) {
            fragmentList.add(fragment)
        }
    }
NiceToMytyuk
  • 3,644
  • 3
  • 39
  • 100
  • `ViewPagerAdapter.addFragment()` is an anti-pattern that will inevitably lead to crashes. Please use `createFragment()` method as intended – EpicPandaForce Apr 30 '21 at 13:41
  • Possible workaround: Use a `TabLayout` and disallow swiping to navigate between the pages and only allow it by using the tabs. I'm not too sure about the user experience if you want to add "resistance" to the swiping action. – JensV May 04 '21 at 07:42

4 Answers4

12

To reduce the sensitivity of the ViewPager's horizontal scroll, try this extension

fun ViewPager2.reduceDragSensitivity(f: Int = 4) {
    val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
    recyclerViewField.isAccessible = true
    val recyclerView = recyclerViewField.get(this) as RecyclerView

    val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
    touchSlopField.isAccessible = true
    val touchSlop = touchSlopField.get(recyclerView) as Int
    touchSlopField.set(recyclerView, touchSlop*f)       // "8" was obtained experimentally
}

Source

c-an
  • 3,543
  • 5
  • 35
  • 82
Navjot
  • 1,202
  • 1
  • 11
  • 24
  • 2
    how can we have this implemented with java? – Tiny Sonhh Feb 23 '22 at 04:21
  • 1
    `public void reduceDragSensitivity(int f) throws NoSuchFieldException, IllegalAccessException { Field recyclerViewField = ViewPager2.class.getDeclaredField("mRecyclerView"); recyclerViewField.setAccessible(true); RecyclerView recyclerView = (RecyclerView) recyclerViewField.get(this); Field touchSlopField = RecyclerView.class.getDeclaredField("mTouchSlop"); touchSlopField.setAccessible(true); int touchSlop = (int) touchSlopField.get(recyclerView); touchSlopField.set(recyclerView, touchSlop*f); }` – CODAR747 Apr 26 '23 at 10:14
2

This should be the java code for the kotlin code of @Navjot. Something like sensitivity = 3; or sensitivity = 4; works fine for me.

See soure and other solutions

private void reduceDragSensitivity(int sensitivity) {
    try {
        Field ff = ViewPager2.class.getDeclaredField("mRecyclerView") ;
        ff.setAccessible(true);
        RecyclerView recyclerView =  (RecyclerView) ff.get(viewPager);
        Field touchSlopField = RecyclerView.class.getDeclaredField("mTouchSlop") ;
        touchSlopField.setAccessible(true);
        int touchSlop = (int) touchSlopField.get(recyclerView);
        touchSlopField.set(recyclerView,touchSlop * sensitivity);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
Tobi Z
  • 29
  • 2
-1

this works perfectly in my approach i stored the fragments as an arrayList

inside Activity

val listOfFragment = arrayListOf<Fragment>(
                F1(),
                F2(),
                F3(),
           
            )
            val adapter = TabsPagerAdapter(
                listOfFragment,
                requireActivity().supportFragmentManager,
                requireActivity(),
                0,
                lifecycle

            )

            viewPager1.adapter = adapter           

adapter

class TabsPagerAdapter(val array: ArrayList<Fragment>,
                       val manager: FragmentManager,
                       val context: Context,
                       private val numTabs: Int,
                       lifecycle: Lifecycle): FragmentStateAdapter(manager, lifecycle){
    override fun createFragment(position: Int): Fragment {
        return array[position]
    }

    override fun getItemCount(): Int {
        return array.size
     }
    private fun getReadableTabPosition(position: Int) = position + 1

}

NOTE: the sensitivity might depends on the machine that you are currently running on try switching another devices

Ananiya Jemberu
  • 1,366
  • 1
  • 9
  • 14
-1

Other solutions use reflection to accessing recyclerView.

In fact that property is accesible, and reflection is no needed here.

Using Kotlin we can do:

   runCatching {
        val touchSlopField = RecyclerView::class.java.getDeclaredField(TOUCH_SLOP_FIELD)
        touchSlopField.isAccessible = true
        val touchSlop = touchSlopField.get(recyclerView) as Int
        touchSlopField.set(recyclerView, touchSlop * SENSITIVE_SCROLL)
    }.onFailure(ZaraLogger::Log)
Yago Rey
  • 209
  • 2
  • 8