Search code examples
androidkotlinonclicklistenerontouchlisteneronlongclicklistener

How to implement onClick, onLongClick and onTouch to one View


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?


Solution

  • 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