Search code examples
androidandroid-touch-event

How to receive drag events on a top-layer View, but pass through clicks?


The app has some UI that receives clicks, e.g. an EditText to receive focus, and a buttons (not in the screenshot below) that triggers an action. But the whole UI piece also needs to slide to the side to reveal Views beneath. I've implemented it as a View that contains the UI and moves on drag (visible), and a top-layer View that's invisible but collects drag input.

The code below partially works in that the visible View drags when the invisible view is dragged, and it can sense the difference between a drag and a click, but despite returning false in View.OnTouchListener.onTouch(), the Views underneath the invisible draggable View do not receive the clicks.

How do I get the clicks to pass through to Views underneath, without disturbing the working drag functionality?

import android.view.MotionEvent
import android.view.View

class ViewHorizontalSlider(dragView: View,
                           private val moveView: View,
                           private val distancePx: Int = 100) {

    private var xPos = 0f
    private var downX = 0f
    private var isOnClick = false
    private val scrollThreshold = 6

    init {
         dragView.setOnTouchListener { _, event ->
            when (event.actionMasked) {
                MotionEvent.ACTION_DOWN -> {
                    downX = event.x
                    xPos = moveView.x - event.rawX
                    isOnClick = true
                    return@setOnTouchListener true
                }
                MotionEvent.ACTION_MOVE -> {
                    var newX = event.rawX + xPos
                    if (newX > distancePx) newX = distancePx.toFloat()
                    if (newX < 0) newX = 0f
                    moveView.x = newX

                    val movement = Math.abs(downX - event.x)
                    if (isOnClick && (movement > scrollThreshold)) {
                        isOnClick = false;
                    }
                    return@setOnTouchListener true
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    if (isOnClick) {
                        return@setOnTouchListener false
                    } else {
                        return@setOnTouchListener true
                    }
                } else -> {
                    return@setOnTouchListener false
            }
            }
        }
    }

In the screenshot, the "P1" etc are EditText Views. The whole coloured panel should be draggable to the right (like P1, to reveal the Delete UI), but it should also be possible to click (on the invisible overlay draggable View) but give focus to the EditText underneath. I expected to just return false in the code above, but that appears not to be enough for the click to pass through.

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="72dp"
    >

    <ImageView
        android:id="@+id/delete"
        android:layout_width="@dimen/perspective_delete_slide"
        android:layout_height="match_parent"
        android:src="@drawable/ic_delete"
        android:scaleType="center"
        android:background="@color/black_light"
        android:contentDescription="@string/delete_perspective"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <android.support.constraint.ConstraintLayout
        android:id="@+id/container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:focusableInTouchMode="true"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:layout_marginStart="72dp"
        >

        <EditText
            android:id="@+id/name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@null"
            android:inputType="text|textCapSentences"
            android:layout_marginStart="12dp"
            android:layout_marginEnd="12dp"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/marker"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            />

        <View
            android:id="@+id/marker"
            android:layout_width="8dp"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/name"
            app:layout_constraintTop_toTopOf="parent"
            />

        <View
            android:id="@+id/draggable"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:clickable="false"
            android:focusable="false"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            />

    </android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

The UI


Solution

  • if you want menu on swipe then you need this Swipe on menu or like that Swipe on Menu 2