ViewPager2+FragmentStateAdapter嵌套滚动异常:滑动ImageSlider触发页面切换
我用ViewPager2、BottomNavigationView和Fragment搭建了Activity布局,首个Fragment中包含一个ImageSlider。但滑动这个ImageSlider时,整个ViewPager2会直接切换到第二个Fragment。换成普通ViewPager搭配FragmentPagerAdapter后功能正常,可FragmentPagerAdapter已经被弃用了,这是什么原因?
提供的代码片段
布局代码
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"/>
Activity代码
val adapter = ViewPagerAdapter(this) viewPager.adapter = adapter
ViewPagerAdapter代码
const val FRAGMENT_COUNT = 4; class ViewPagerAdapter(activity: FragmentActivity): FragmentStateAdapter(activity){ override fun getItemCount() = FRAGMENT_COUNT override fun createFragment(position: Int): Fragment { return when(position) { 0 -> HomeFragment() 1 -> DepartmentFragment() 2 -> ConnectFragment() 3 -> SocialFragment() else -> HomeFragment() } } }
问题原因与解决方案
这本质是横向滑动事件冲突导致的:ViewPager2基于RecyclerView实现,它会优先拦截横向滑动事件来完成页面切换;而你的ImageSlider同样是横向滑动组件,当你滑动它时,ViewPager2抢先捕获了滑动事件,于是触发了页面切换。旧ViewPager的事件拦截逻辑和ViewPager2不同,对嵌套滑动的兼容性更好,所以不会出现这个问题。
下面是几种可行的解决方法:
方案一:让ImageSlider优先处理触摸事件
在HomeFragment中,给ImageSlider添加触摸监听,通知父布局(ViewPager2)在滑动ImageSlider时不要拦截事件:imageSlider.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { // 按下时禁止父布局拦截事件 v.parent.requestDisallowInterceptTouchEvent(true) } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // 松开/取消时恢复父布局的拦截能力 v.parent.requestDisallowInterceptTouchEvent(false) } } // 让ImageSlider自行处理触摸事件 v.onTouchEvent(event) }方案二:自定义ViewPager2拦截逻辑
如果方案一无效,可以自定义ViewPager2,判断触摸点是否在ImageSlider范围内,再决定是否拦截事件:class CustomViewPager2(context: Context, attrs: AttributeSet?) : ViewPager2(context, attrs) { private val imageSliderRect = Rect() // 传入ImageSlider实例,用于获取其屏幕位置 fun bindImageSlider(slider: View) { slider.getGlobalVisibleRect(imageSliderRect) } override fun onInterceptTouchEvent(event: MotionEvent): Boolean { // 触摸点在ImageSlider范围内时,不拦截事件 if (imageSliderRect.contains(event.rawX.toInt(), event.rawY.toInt())) { return false } return super.onInterceptTouchEvent(event) } }之后把布局中的
ViewPager2替换为这个自定义类,在HomeFragment初始化完成后,把ImageSlider实例传给bindImageSlider方法即可。方案三:检查ImageSlider的配置参数
如果你使用的是第三方ImageSlider库,不妨查看它的文档,是否有处理嵌套滑动的配置项。比如部分基于RecyclerView的Slider,可以设置setNestedScrollingEnabled(false)来避免冲突;有些库还提供了是否允许横向滑动的开关,确保它能正确响应自身的滑动事件。
内容的提问来源于stack exchange,提问作者Unaisul Hadi

