Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MotionLayout: How to Limit the 'OnSwipe' event on a specific view instead of 'OnSwipe' on whole screen

I have a motionlayout with the following 'layoutDescription' (scenes file)

    <Transition
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">

    <OnSwipe
        motion:touchAnchorId="@+id/button"
        motion:touchAnchorSide="left"
        motion:dragDirection="dragLeft" />

</Transition>
<ConstraintSet android:id="@+id/start">
    <!--    ConstraintSet for starting -->
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <!--    ConstraintSet for end -->
</ConstraintSet>

What I expect from here is if user drag the 'button' towards left side then it should perform the transition but what actually happening is when user drag left(swipe left) from any where in the screen this transition getting executed!

How to limit the animation work only when dragging the specified view?

(Im using "constraintlayout:2.0.0-beta2" )

like image 509
Niroshan Avatar asked Jan 01 '23 19:01

Niroshan


2 Answers

Actually for onSwipe you can use touchRegionId and targetId for OnClick

like image 66
Ruslan Leshchenko Avatar answered Jan 13 '23 20:01

Ruslan Leshchenko


Updated answer : with the ConstraintLayout 2.0.0 alpha-5 you can simply use

app:touchRegionId

in your "OnSwipe" for the same behaviour. Also can use 'app:targetId' for "OnClick" usages.

Thanks @RuslanLeshchenko


Original answer :

I managed to do this finally by extending MotionLayout and override onTouchEvent to forward the event to required view.

Create the custom layout by extending MotionLayout

class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {

private var viewToDetectTouch1 :View? = null
private var viewToDetectTouchRes :Int = -1

private val viewRect = Rect()
private var touchStarted = false

init {

    setTransitionListener(object : MotionLayout.TransitionListener {
        override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {

        }

        override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {

        }

        override fun onTransitionChange(p0: MotionLayout, p1: Int, p2: Int, p3: Float) {
        }

        override fun onTransitionCompleted(p0: MotionLayout, p1: Int) {
            touchStarted = false
        }
    })

    context.theme.obtainStyledAttributes(
            attributeSet,
            R.styleable.SingleViewTouchableMotionLayout,
            0, 0).apply {

        try {
            viewToDetectTouchRes = getResourceId(
                    R.styleable.SingleViewTouchableMotionLayout_viewToDetectTouch, 0)
        } finally {
            recycle()
        }
    }

}

override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    viewToDetectTouch1 =  (parent as View).findViewById<View>(viewToDetectTouchRes)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.actionMasked) {
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
            touchStarted = false
            return super.onTouchEvent(event)
        }
    }
    if (!touchStarted) {
        viewToDetectTouch1?.getHitRect(viewRect)
        touchStarted = viewRect.contains(event.x.toInt(), event.y.toInt())
    }
    return touchStarted && super.onTouchEvent(event)
}

}

And add the custom attribute which will be used to declare the View that gonna receives the events to trigger the Transition/animation. Add these styleable to your attrs.xml in your res folder (res/values/attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SingleViewTouchableMotionLayout">
        <attr name="viewToDetectTouch" format="reference" />
    </declare-styleable>
</resources>

Thats it!! This way we can archive MotionLayout OnSwipe on a specified view.

Layout XML file now will look like this :

 <your_package.SingleViewTouchableMotionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/motion_file"

    app:viewToDetectTouch="@+id/triggerView"

        >
    <!-- Notice that 'app:viewToDetectTouch' hold the id of the  view we interested -->


    <!-- View we interested  -->
    <View

        android:id="@+id/triggerView"

        android:layout_width="50dp"
        android:layout_height="50dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:background="@color/colorBlack"
        />

    </your_package.SingleViewTouchableMotionLayout>
like image 31
Niroshan Avatar answered Jan 13 '23 21:01

Niroshan