How to implement onClick, onLongClick and onTouch to one View

1k views Asked by At

I have a View (it's ViewHolder in RecyclerView but I think it wouldn't make any difference) which implements onClick(), onLongClick(), and onTouch(). The problem I faced is that longClick is never executed. I read about it in other questions but I can't figure out how to do it and when exactly I should return true \ false in onTouch(). Here is my code;

Function bind() in ViewHolder class which implements View.OnTouchListener:

binding.background.setOnClickListener {
    Log.d("Click")
}

binding.background.setOnLongClickListener {
    Log.d("Long Click")
}

binding.background.setOnTouchListener(this) // here I see warning "Custom view `ConstraintLayout` has setOnTouchListener called on it but does not override performClick"

Override onTouch()

override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
    return when (event!!.action)
    {
        MotionEvent.ACTION_DOWN ->
        {
            x1 = event.x
            true
        }
            MotionEvent.ACTION_UP ->
        {
            x2 = event.x
            return if (abs(x2 - x1) > MIN_DISTANCE)
            {
                Log.d("Was swiped")
                true
            }
            else
            {
                Log.d("Wasn't swiped")
                v?.performClick()
                false
            }
        }
        else -> false
    }
}

This function just detects slide on the view. What should I change in onTouch() to make longClick working? Also, is there any simpler way to detect slide on the RecyclerView item?

2

There are 2 answers

0
iknow On BEST ANSWER

If someone ever will have such a problem I solved it in this way:
(It is recycler view item which can be moved left / right or scrolled)

ViewHolder fields:

private var xStart = 0F
private var lastY = 0F
private var yStart = 0F
private val handler: Handler = Handler()
private var isLongClickCanceled = false
private var wasLongClicked = false
private var startScrolling = false
private var status = 0
...
private const val LONG_CLICK_TIME = 700L
private const val CLICK_DISTANCE = 75
private const val PANEL_SIZE = 125

Touch function

override fun onTouch(v: View?, event: MotionEvent?): Boolean
{
    if (v != null && event != null)
    {
        v.parent.requestDisallowInterceptTouchEvent(true)
        when (event.action)
        {
            MotionEvent.ACTION_DOWN ->
            {
                Log.d("Down")
                xStart = event.x
                yStart = event.y
                isLongClickCanceled = false
                wasLongClicked = false
                startScrolling = false
                handler.postDelayed({ //long click
                    wasLongClicked = true
                    val leftPanel = (v as ViewGroup).getChildAt(0)
                    val rightPanel = v.getChildAt(1)
                    val paramsLP = leftPanel.layoutParams
                    val paramsRP = rightPanel.layoutParams
                    paramsLP.width = 1
                    paramsRP.width = 1
                    leftPanel.layoutParams = paramsLP
                    rightPanel.layoutParams = paramsRP
                    v.performLongClick()
                }, LONG_CLICK_TIME)
                xStart = event.x
            }
            MotionEvent.ACTION_UP ->
            {
                Log.d("Up")
                handler.removeCallbacksAndMessages(null)
                if (!startScrolling && !isLongClickCanceled && !wasLongClicked)
                {
                    if ((event.eventTime - event.downTime) < LONG_CLICK_TIME) //click
                    {
                        Log.d("Status $status")
                        if (status == 0)
                        {
                            v.performClick()
                        }
                        else
                        {
                            val leftPanel = (v as ViewGroup).getChildAt(0)
                            val rightPanel = v.getChildAt(1)
                            val paramsLP = leftPanel.layoutParams
                            val paramsRP = rightPanel.layoutParams
                            paramsLP.width = 1
                            paramsRP.width = 1
                            leftPanel.layoutParams = paramsLP
                            rightPanel.layoutParams = paramsRP
                            status = 0
                            selectCar(binding.car!!.carID, false)
                        }
                    }
                }
                else
                {
                    val leftPanel = (v as ViewGroup).getChildAt(0)
                    val rightPanel = v.getChildAt(1)
                    val paramsLP = leftPanel.layoutParams
                    val paramsRP = rightPanel.layoutParams
                    when
                    {
                        status > (PANEL_SIZE / 2) ->
                        {
                            paramsLP.width = PANEL_SIZE
                            paramsRP.width = 1
                            selectCar(binding.car!!.carID, false)
                        }
                        status < -(PANEL_SIZE / 2) ->
                        {
                            paramsRP.width = PANEL_SIZE
                            paramsLP.width = 1
                            selectCar(binding.car!!.carID, true)
                        }
                        else ->
                        {
                            paramsLP.width = 1
                            paramsRP.width = 1
                            status = 0
                            selectCar(binding.car!!.carID, false)
                        }
                    }
                    leftPanel.layoutParams = paramsLP
                    rightPanel.layoutParams = paramsRP
                }
            }
            MotionEvent.ACTION_MOVE ->
            {
                if (startScrolling)
                {
                    scroll((lastY - event.rawY).toInt())
                    lastY = event.rawY
                }
                else
                {
                    if (!wasLongClicked)
                    {
                        if (isLongClickCanceled)
                        {
                            val deltaX = (event.x - xStart).toInt()
                            if (abs(deltaX) > 75)
                            {
                                val leftPanel = (v as ViewGroup).getChildAt(0)
                                val rightPanel = v.getChildAt(1)
                                val paramsLP = leftPanel.layoutParams
                                val paramsRP = rightPanel.layoutParams
                                if (deltaX > 0)
                                {
                                    status = (deltaX - CLICK_DISTANCE)
                                    paramsLP.width = min(status, PANEL_SIZE)
                                    paramsRP.width = 1
                                }
                                else if (deltaX < 0)
                                {
                                    status = (deltaX + CLICK_DISTANCE)
                                    paramsRP.width = min(-status, PANEL_SIZE)
                                    paramsLP.width = 1
                                }
                                leftPanel.layoutParams = paramsLP
                                rightPanel.layoutParams = paramsRP
                            }
                        }
                        else if (abs(yStart - event.y) > CLICK_DISTANCE)
                        {
                            lastY = event.rawY
                            startScrolling = true
                            handler.removeCallbacksAndMessages(null)
                        }
                        else if (!isLongClickCanceled && abs(xStart - event.x) >= CLICK_DISTANCE)
                        {
                            isLongClickCanceled = true
                            handler.removeCallbacksAndMessages(null)
                        }
                    }
                }
            }
        }
    }
    return true
}

Scroll function which is passed to ViewHolder

{ dy -> binding.recViewCar.scrollBy(0, dy) }

This code can execute Click, LongClick, and Touching view. When user make LongClick, move action wouldn't be executed. After holding a finger on the screen and moving it more than CLICK_DISTANCE Click and LongClick wouldn't be executed

0
Seshadri Pera On

Try using with GestureDetector from view onTouchEvent like below

final GestureDetector longPressGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
        Log.d(TAG, "onLongPress -> long press detected");
    }
});
public boolean onTouchEvent(MotionEvent event) {
    return longPressGestureDetector.onTouchEvent(event); //Will handle gesture API calls. The Gesture Detector can be used for Swipe actions as well.
};