Error parameter specified as non-null is null when use swipe touch listener in recyclerView - Android
Asked Answered
Q

2

7

I am using from bellow class for detect swipe in recyclerView:

open class OnSwipeTouchListener(context: Context) : View.OnTouchListener {
    var context: Context? = context

    private val gestureDetector = GestureDetector(context, GestureListener())

    fun onTouchs(event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event)
    }

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {

        private val SWIPE_THRESHOLD = 100
        private val SWIPE_VELOCITY_THRESHOLD = 100

        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
            onTouchs(e)
            return true
        }


        override fun onFling(@NonNull e1: MotionEvent,
                             @NonNull e2: MotionEvent,
                             @NonNull velocityX: Float,
                             @NonNull velocityY: Float): Boolean {
            val result = false
            try {
                val diffY = e2.y - e1.y
                val diffX = e2.x - e1.x
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            //onSwipeRight()
                        } else {
                            onSwipeLeft()
                        }
                    }
                } else {
                    // onTouch(e);
                }
            } catch (exception: Exception) {
                exception.printStackTrace()
                Crashlytics.logException(exception)
            }

            return result
        }
    }

    //    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(@NonNull v: View, @NonNull event: MotionEvent): Boolean {
        v.performClick()
        return gestureDetector.onTouchEvent(event)
    }

    //open fun onSwipeRight() {}

    open fun onSwipeLeft() {}
}

But when I scrolling to end list get me bellow crash when I arrive to end list:

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter e1
        at xx.xx.xx.tools.OnSwipeTouchListener$GestureListener.onFling(OnSwipeTouchListener.kt)
        at android.view.GestureDetector.onTouchEvent(GestureDetector.java:667)
        at xx.xx.xx.tools.OnSwipeTouchListener.onTouch(OnSwipeTouchListener.kt:65)

And here is my SwipeController.kt:

internal class SwipeController(private val swipeControllerInter: SwipeControllerInter) : ItemTouchHelper.Callback() {
    private var swipeBack = false

    interface SwipeControllerInter {
        fun left()

//        fun right()
    }

    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
        return makeMovementFlags(0, LEFT or RIGHT)
    }

    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        return false
    }

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

    }

    override fun convertToAbsoluteDirection(flags: Int, layoutDirection: Int): Int {
        if (swipeBack) {
            swipeBack = false
            return 0
        }
        return super.convertToAbsoluteDirection(flags, layoutDirection)
    }

    override fun onChildDraw(c: Canvas,
                             recyclerView: RecyclerView,
                             viewHolder: RecyclerView.ViewHolder,
                             dX: Float, dY: Float,
                             actionState: Int, isCurrentlyActive: Boolean) {

        if (actionState == ACTION_STATE_SWIPE) {
            if (dX > 0) {
                // then swiping right.
                //swipeControllerInter.right()
            } else if (dX < 0) {
                // then swiping left.
                swipeControllerInter.left()
            }
            // If dX == 0 then at at start position.
        } else {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
        }

        if (actionState == ACTION_STATE_SWIPE) {
            setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun setTouchListener(c: Canvas,
                                 recyclerView: RecyclerView,
                                 viewHolder: RecyclerView.ViewHolder,
                                 dX: Float, dY: Float,
                                 actionState: Int,
                                 isCurrentlyActive: Boolean) {

        recyclerView.setOnTouchListener { v, event ->
            swipeBack = event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_UP
            false
        }
    }
}

And I am using in my MainFragment.kt:

val swipeController = SwipeController(this)
val itemTouchhelper = ItemTouchHelper(swipeController)
itemTouchhelper.attachToRecyclerView(recycler_view)

recycler_view.setOnTouchListener(object : OnSwipeTouchListener(activity!!) {
    override fun onSwipeLeft() {
        val intent = Intent(activity, MainActivityMou::class.java)
        intent.putExtra("ONE", 1)
        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
        startActivity(intent)
        activity!!.finish()
    }
})
Quasi answered 26/12, 2018 at 9:39 Comment(0)
Q
1

Resolved my problem, I changed onTouch to:

override fun onTouch(v: View, event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_MOVE) {
        v.performClick()
        return gestureDetector.onTouchEvent(event)
    } else {
        return false
    }
}

OR:

override fun onFling(e1: MotionEvent,
                     e2: MotionEvent,
                     velocityX: Float,
                     velocityY: Float): Boolean {
            val result = false
        if(e1 != null && e2 != null){
            try {
                val diffY = e2.y - e1.y
                val diffX = e2.x - e1.x
                if (Math.abs(diffX) > Math.abs(diffY)) {
                    if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                        if (diffX > 0) {
                            //onSwipeRight()
                        } else {
                            onSwipeLeft()
                        }
                    }
                } else {
                    // onTouch(e);
                }
            } catch (exception: Exception) {
                exception.printStackTrace()
                Crashlytics.logException(exception)
            }
          }
            return result
        }
    }
Quasi answered 26/12, 2018 at 10:26 Comment(2)
About the onFling callback, I think you've forgotten to add "?" for the "MotionEvent" parameters. Also, this raises the question: Why would any of them be null? In which case does it occur? The documentation doesn't say anything about it: developer.android.com/reference/android/view/… . Reported here about missing this information: issuetracker.google.com/issues/206855618Ministration
Your code seems a bit backwards. ACTION_MOVE should only return onTouchEvent and v.performClick() should be in the else portion of that conditionMiserable
D
0

No extra class requires for swipe event. As this can be done using ItemouchHelper class.

private void rightAndLeftSwipe() {
        ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
            @Override
            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                super.clearView(recyclerView, viewHolder);
            }
 @Override
            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                int postion = viewHolder.getAdapterPosition();

              /*----------Right swipe -----------*/
                if (direction == ItemTouchHelper.RIGHT) {
                  //todo perform your action
                }
                /*--------Left swipe--------*/
                if (direction == ItemTouchHelper.LEFT) {
                   //perform your action for left swipe
                }
            }

        };
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        itemTouchHelper.attachToRecyclerView(binding.rvRecycler);
    }

Above code will perform the right and left swipe like tinder application. If you need to a button below when swiping a item, then let me know.

Deviationism answered 26/12, 2018 at 11:24 Comment(2)
Only one problem may occur. Items will move.Cardcarrying
Have you triied the solution? Is it moving? If yes, then what was the issue?Deviationism

© 2022 - 2024 — McMap. All rights reserved.