RecyclerView触摸与点击冲突:onFling中e1参数空指针异常
问题描述
原本RecyclerView通过GestureDetector的onFling方法实现上滑缩放功能,运行正常。为RecyclerView Item添加点击事件(通过接口在Activity中实现)后,上滑操作触发空指针异常:
java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter e1
异常堆栈指向com.example.simmone.utils.SwipeServices$GestureListener.onFling方法。
相关代码如下:
SwipeService.kt
class SwipeServices : View.OnTouchListener { enum class SwipeDirection { bottomToTop } private var rootLayout: ViewGroup? = null private var layoutToShowHide: ViewGroup? = null private var gestureDetector: GestureDetector? = null private var swipeDirections: MutableList<SwipeDirection>? = null fun initialize(rootLayout: ViewGroup, layoutToShowHide:ViewGroup?, swipeDirections: MutableList<SwipeDirection>,maxSwipeDistance: Int = 1) { val gestureListener = GestureListener() gestureDetector = GestureDetector(rootLayout.context, gestureListener) this.rootLayout = rootLayout this.layoutToShowHide = layoutToShowHide this.swipeDirections = swipeDirections gestureListener.MAX_SWIPE_DISTANCE = maxSwipeDistance this.rootLayout!!.setOnTouchListener(this) } @SuppressLint("ClickableViewAccessibility") override fun onTouch(v: View?, event: MotionEvent): Boolean { return gestureDetector?.onTouchEvent(event)!! } inner class GestureListener : GestureDetector.SimpleOnGestureListener() { var MAX_SWIPE_DISTANCE = 1 private val SWIPE_VELOCITY_THRESHOLD = 1 override fun onDown(e: MotionEvent): Boolean { return true } override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { var result = false try { val diffY = e2.y - e1.y if (abs(diffY) > MAX_SWIPE_DISTANCE && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY<0) { onSwipeBottomToTop() } } result = true } catch (exception: Exception) { exception.printStackTrace() } return result } } fun cancel() { layoutToShowHide?.animate()?.scaleX(1f)?.scaleY(1f)?.setDuration(500)?.start(); } fun onSwipeBottomToTop() { layoutToShowHide?.animate()?.scaleX(.7f)?.scaleY(.7f)?.setDuration(500)?.start(); } }
Activity类
class SwappingAppsActivity : AppCompatActivity(), SwappingAppsAdapter.OnItemClickListener{ private lateinit var swappingAppsBinding: ActivitySwappingAppsBinding private var swappingAppsAdapter: SwappingAppsAdapter? = null private lateinit var selectedApp: SwappingItems override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) swappingAppsBinding = ActivitySwappingAppsBinding.inflate(layoutInflater) setContentView(swappingAppsBinding.root) val swappingAppViewModel = ViewModelProvider(this)[SwappingAppViewModel::class.java] swappingAppViewModel.generateSwapItems() swappingAppViewModel.swapList.observe(this@SwappingAppsActivity) { swappingAppsAdapter = SwappingAppsAdapter(this@SwappingAppsActivity, it) swappingAppsBinding.rvSwappingApps.layoutManager = LinearLayoutManager( this@SwappingAppsActivity, LinearLayoutManager.HORIZONTAL, false ) swappingAppsBinding.rvSwappingApps.adapter = swappingAppsAdapter } val movingElement = swappingAppsBinding.rvSwappingApps val swipeServices = SwipeServices() //调用滑动服务的部分 swipeServices.initialize(rootLayout =movingElement,movingElement, arrayListOf(SwipeServices.SwipeDirection.bottomToTop), 50) } override fun onItemClick(view: View, swappingItems: SwappingItems) { if (swappingAppsAdapter?.getItemList()?.indexOf(selectedApp)==2){ val movingElement = swappingAppsBinding.rvSwappingApps SwipeServices().cancel() } } }
解决方案
问题原因
onFling参数非空声明冲突:Kotlin中GestureListener的onFling方法参数e1被声明为非空,但Android系统在触摸事件被Item点击事件拦截的场景下,会传递null的e1,触发Kotlin非空参数检查,导致空指针异常。SwipeServices实例未复用:在onItemClick中新建未初始化的SwipeServices实例调用cancel(),属于无效操作,同时也是潜在的资源浪费问题。selectedApp未初始化风险:onItemClick中直接使用selectedApp,但未提前赋值,可能触发未初始化异常。
修复步骤及代码
1. 修改SwipeService.kt的onFling方法
将e1、e2改为可空参数,并添加空值判断:
inner class GestureListener : GestureDetector.SimpleOnGestureListener() { var MAX_SWIPE_DISTANCE = 1 private val SWIPE_VELOCITY_THRESHOLD = 1 override fun onDown(e: MotionEvent): Boolean { return true } // 修改参数为可空类型,添加空值校验 override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean { var result = false try { // 先判断e1和e2不为null,避免空指针 if (e1 == null || e2 == null) { return result } val diffY = e2.y - e1.y if (abs(diffY) > MAX_SWIPE_DISTANCE && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY < 0) { onSwipeBottomToTop() } } result = true } catch (exception: Exception) { exception.printStackTrace() } return result } }
同时优化onTouch方法的空安全处理:
@SuppressLint("ClickableViewAccessibility") override fun onTouch(v: View?, event: MotionEvent): Boolean { return gestureDetector?.onTouchEvent(event) ?: false }
2. 修改SwappingAppsActivity.kt,复用SwipeServices实例并初始化selectedApp
class SwappingAppsActivity : AppCompatActivity(), SwappingAppsAdapter.OnItemClickListener{ private lateinit var swappingAppsBinding: ActivitySwappingAppsBinding private var swappingAppsAdapter: SwappingAppsAdapter? = null private lateinit var selectedApp: SwappingItems // 保存初始化后的SwipeServices实例 private lateinit var swipeServices: SwipeServices override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) swappingAppsBinding = ActivitySwappingAppsBinding.inflate(layoutInflater) setContentView(swappingAppsBinding.root) val swappingAppViewModel = ViewModelProvider(this)[SwappingAppViewModel::class.java] swappingAppViewModel.generateSwapItems() swappingAppViewModel.swapList.observe(this@SwappingAppsActivity) { swappingAppsAdapter = SwappingAppsAdapter(this@SwappingAppsActivity, it) swappingAppsBinding.rvSwappingApps.layoutManager = LinearLayoutManager( this@SwappingAppsActivity, LinearLayoutManager.HORIZONTAL, false ) swappingAppsBinding.rvSwappingApps.adapter = swappingAppsAdapter } val movingElement = swappingAppsBinding.rvSwappingApps swipeServices = SwipeServices() //调用滑动服务的部分 swipeServices.initialize(rootLayout = movingElement, layoutToShowHide = movingElement, arrayListOf(SwipeServices.SwipeDirection.bottomToTop), 50) } override fun onItemClick(view: View, swappingItems: SwappingItems) { // 先赋值selectedApp,避免未初始化异常 selectedApp = swappingItems if (swappingAppsAdapter?.getItemList()?.indexOf(selectedApp) == 2){ // 复用已初始化的SwipeServices实例 swipeServices.cancel() } } }
内容的提问来源于stack exchange,提问作者Mohamed Rimshad
相关产品推荐
相关产品推荐

