I am trying to set a touch listener to a cardView (which is also swipeable because of the motion layout) the problem is that if i return true in the OnTouchListener when the event.action == ACTION_DOWN (so i can receive the callback when the user lifts his finger from the cardView) the swiping animation on this cardView does not work. I need to execute my logic when the user lifts his finger because if i do my logic on the ACTION_DOWN, then in the case when the user swipes the cardView my logic is also executed (i want the logic to be executed only when the user taps on the cardView).
Here is the xml layout:
<androidx.constraintlayout.motion.widget.MotionLayout
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"
app:motionDebug="SHOW_ALL"
android:id="@+id/matchingMotionLayout"
android:layout_width="match_parent"
app:layoutDescription="@xml/matching_scene"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardTwo"
android:layout_width="320dp"
android:layout_height="480dp"
app:cardBackgroundColor="@color/design_default_color_secondary"
app:cardCornerRadius="24dp"
app:layout_constraintBottom_toTopOf="@id/dislikeButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/matchingWithTitle"
app:layout_constraintVertical_bias="0.4">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/bottomImage"
android:layout_height="0dp"
android:layout_width="match_parent"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="@id/bottomInterestPointName"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/bottomInterestPointName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="1dp"
android:gravity="center_horizontal"
android:paddingVertical="24dp"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Restaurant Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cardOne"
android:layout_width="320dp"
android:layout_height="480dp"
app:layout_constraintBottom_toTopOf="@id/dislikeButton"
app:cardBackgroundColor="@color/md_theme_light_primary"
app:cardCornerRadius="24dp"
app:layout_constraintTop_toBottomOf="@id/matchingWithTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="0.4">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cardOneLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/topImage"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:layout_width="match_parent"
app:layout_constraintBottom_toTopOf="@id/topInterestPointName"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/topInterestPointName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:elevation="1dp"
android:gravity="center_horizontal"
android:paddingVertical="24dp"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Restaurant Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.jhn.restaurantpicker.ui.core.TestCardView>
...
</com.jhn.restaurantpicker.ui.core.TestMotionLayout>
This is my motion scene transitions:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
app:defaultDuration="500"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
android:id="@+id/startToLeft3"
app:constraintSetEnd="@+id/unlike"
app:constraintSetStart="@+id/start">
<OnSwipe
app:dragDirection="dragLeft"
app:touchAnchorId="@id/cardOne"
app:touchAnchorSide="left"
app:touchRegionId="@id/cardOne" />
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:keyPositionType="pathRelative"
app:motionTarget="@id/cardOne"
app:percentY="0.3" />
<KeyPosition
app:framePosition="50"
app:keyPositionType="pathRelative"
app:motionTarget="@id/topInterestPointName"
app:percentY="0.3" />
</KeyFrameSet>
</Transition>
<Transition
android:id="@+id/startToRight3"
app:constraintSetEnd="@+id/like"
app:constraintSetStart="@+id/start">
<OnSwipe
app:dragDirection="dragRight"
app:touchAnchorId="@id/cardOne"
app:touchAnchorSide="right"
app:touchRegionId="@id/cardOne" />
<KeyFrameSet>
<KeyPosition
app:framePosition="50"
app:keyPositionType="pathRelative"
app:motionTarget="@id/cardOne"
app:percentY="-0.3" />
<KeyPosition
app:framePosition="50"
app:keyPositionType="pathRelative"
app:motionTarget="@id/topInterestPointName"
app:percentY="-0.3" />
</KeyFrameSet>
</Transition>
...
This is the transition listener in the fragment:
matchingMotionLayout.setTransitionListener(object : TransitionAdapter() {
override fun onTransitionCompleted(motionLayout: MotionLayout, currentId: Int) {
when (currentId) {
R.id.offScreenUnlike -> {
if (vm.shouldResetScene) {
motionLayout.progress = 0f
motionLayout.setTransition(R.id.start, R.id.start)
}
vm.unlike()
}
R.id.offScreenLike -> {
if (vm.shouldResetScene) {
motionLayout.progress = 0f
motionLayout.setTransition(R.id.start, R.id.start)
}
vm.like()
}
R.id.start -> {
motionLayout.progress = 0f
motionLayout.setTransition(R.id.start, R.id.start)
}
}
super.onTransitionCompleted(motionLayout, currentId)
}
})
Solution which worked for me: So after trying different methods for 1 week straight i finally managed to find something which worked.
class ClickableMotionLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr) {
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
return event?.action == MotionEvent.ACTION_MOVE
}
}
cardOne.setOnTouchListener(imageSeekListener)
In the touchListener i call the onTouchEvent of the motionLayout by passing the event received by the touchListener and when the action is ACTION_DOWN i return true.
private var imageSeekListener: View.OnTouchListener? = View.OnTouchListener { v, event ->
binding.matchingMotionLayout.onTouchEvent(event)
if (event.action == MotionEvent.ACTION_DOWN) {
return@OnTouchListener true
}
if (event.action == MotionEvent.ACTION_UP) {
when (getTouchSide(v, event)) {
TouchSide.LEFT -> vm.peekPreviousPhoto()
TouchSide.RIGHT -> vm.peekNextPhoto()
}
}
return@OnTouchListener false
}
Now i can click on the card to peek the photos, and swipe on it to display the other card without triggering the click. Hope it helps someone.