Attached at the bottom are images to help visualise the issue.
The XML
MotionScene
for the MotionLayout
has four Transitions defined with OnSwipe mechanisms. Additionally, the fragment has two transitions defined programmatically which can only be triggered from the click of a button.
There is one panel that is swipeable from a tab on the right side of the screen. The entire panel and an additional panel below the screen are then animated with the Play icon button click inside the view swiped from the right.
Swiping the panel in and out works consistently. When I press the Play icon button in the swipeable panel, the bottom panel comes up perfectly fine. This state is show in the third image.
However, when swiping from the left side of the tab on the swipeable panel, the panel immediately closes. No animation occurs, and upon further inspection of the MotionLayout
object on the start of the animation, I see that mTransitionInstantly
is set to true.
Alternatively, if I put my finger on the tab instead of swiping from the left side, the transition works perfectly fine. I can even take my finger off after touching it and then swipe from the left side of the tab and it also works fine.
I have tried playing around with many of the settings in the MotionLayout, but to no success with this.
I have seen some previous posts about a similar issue – MotionLayout OnSwipe transition glitch when first time swipe at the opposite direction.
Their ConstraintLayout is on a much newer version than what is defined here, but inside the google issuetracker link I saw someone had this problem still in 2.1.1
. I tried many previous versions, but also no luck.
Below is the MotionScene and the Fragment code stripped down to what’s related to the MotionLayout
I needed to add the code inside the TransitionListener.onTransitionCompleted
, otherwise all swiping animations would not work at all anymore after pressing the button to do the programmatic transitions.
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ConstraintSet android:id="@+id/trackInfoClosedQueuePanelClosed">
<Constraint android:id="@id/recordsRecyclerView"
app:visibilityMode="ignore"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/trackInfoPanel"/>
<Constraint android:id="@id/trackInfoPanel"
app:visibilityMode="ignore"
android:layout_height="150dp"
app:layout_constraintTop_toTopOf="@id/trackInfoSliderBound"
app:layout_constraintBottom_toBottomOf="@id/trackInfoSliderBound"
app:layout_constraintStart_toStartOf="@id/trackInfoSliderBound"
app:layout_constraintEnd_toEndOf="@id/trackInfoSliderBound"/>
<Constraint android:id="@id/sliderBar"
android:layout_width="0dp"
android:layout_height="100dp"
android:translationZ="4dp"
app:layout_constraintWidth_percent="0.055"
android:layout_marginEnd="-16dp"
app:layout_constraintEnd_toStartOf="@id/playlistSliderBound"
app:layout_constraintTop_toTopOf="@id/recordQueueMainPanel"/>
<Constraint android:id="@id/recordQueueMainPanel"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="0.485"
android:translationZ="4dp"
android:layout_marginTop="54dp"
app:layout_constraintStart_toStartOf="@id/playlistSliderBound"
app:layout_constraintEnd_toEndOf="@id/playlistSliderBound"
app:layout_constraintTop_toTopOf="@id/playlistSliderBound"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/trackInfoOpenQueuePanelClosed">
<Constraint android:id="@id/recordsRecyclerView"
app:visibilityMode="ignore"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/trackInfoPanel"/>
<Constraint android:id="@id/trackInfoPanel"
app:visibilityMode="ignore"
android:layout_height="150dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Constraint android:id="@id/sliderBar"
android:layout_width="0dp"
android:layout_height="100dp"
android:translationZ="4dp"
app:layout_constraintWidth_percent="0.055"
android:layout_marginEnd="-16dp"
app:layout_constraintEnd_toStartOf="@id/playlistSliderBound"
app:layout_constraintTop_toTopOf="@id/recordQueueMainPanel"/>
<Constraint android:id="@id/recordQueueMainPanel"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="0.485"
android:translationZ="4dp"
android:layout_marginTop="54dp"
app:layout_constraintStart_toStartOf="@id/playlistSliderBound"
app:layout_constraintEnd_toEndOf="@id/playlistSliderBound"
app:layout_constraintTop_toTopOf="@id/playlistSliderBound"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/trackInfoClosedQueuePanelOpen">
<Constraint android:id="@id/recordsRecyclerView"
app:visibilityMode="ignore"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/trackInfoPanel"/>
<Constraint android:id="@id/trackInfoPanel"
app:visibilityMode="ignore"
android:layout_height="150dp"
app:layout_constraintTop_toTopOf="@id/trackInfoSliderBound"
app:layout_constraintBottom_toBottomOf="@id/trackInfoSliderBound"
app:layout_constraintStart_toStartOf="@id/trackInfoSliderBound"
app:layout_constraintEnd_toEndOf="@id/trackInfoSliderBound"/>
<Constraint android:id="@id/sliderBar"
android:layout_width="0dp"
android:layout_height="100dp"
android:translationZ="4dp"
app:layout_constraintWidth_percent="0.055"
android:layout_marginEnd="-16dp"
app:layout_constraintEnd_toStartOf="@id/recordQueueMainPanel"
app:layout_constraintTop_toTopOf="@id/recordQueueMainPanel"/>
<Constraint android:id="@id/recordQueueMainPanel"
android:layout_height="wrap_content"
android:translationZ="4dp"
android:layout_marginTop="54dp"
app:layout_constraintWidth_percent="0.485"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
<ConstraintSet android:id="@+id/trackInfoOpenQueuePanelOpen">
<Constraint android:id="@id/recordsRecyclerView"
app:visibilityMode="ignore"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/trackInfoPanel"/>
<Constraint android:id="@id/trackInfoPanel"
app:visibilityMode="ignore"
android:layout_height="150dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Constraint android:id="@id/sliderBar"
android:layout_width="0dp"
android:layout_height="100dp"
android:translationZ="4dp"
app:layout_constraintWidth_percent="0.055"
android:layout_marginEnd="-16dp"
app:layout_constraintEnd_toStartOf="@id/recordQueueMainPanel"
app:layout_constraintTop_toTopOf="@id/recordQueueMainPanel"/>
<Constraint android:id="@id/recordQueueMainPanel"
android:layout_height="wrap_content"
android:translationZ="4dp"
android:layout_marginTop="54dp"
app:layout_constraintWidth_percent="0.485"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</ConstraintSet>
<Transition
android:id="@+id/queueCloseToOpenWithTrackInfoClosed"
app:constraintSetStart="@id/trackInfoClosedQueuePanelClosed"
app:constraintSetEnd="@id/trackInfoClosedQueuePanelOpen">
<OnSwipe
app:touchAnchorId="@id/sliderBar"
app:touchRegionId="@id/sliderBar"
app:dragDirection="dragLeft"
app:touchAnchorSide="left"/>
</Transition>
<Transition
android:id="@+id/queueOpenToCloseWithTrackInfoClosed"
app:constraintSetStart="@id/trackInfoClosedQueuePanelOpen"
app:constraintSetEnd="@id/trackInfoClosedQueuePanelClosed">
<OnSwipe
app:touchAnchorId="@id/sliderBar"
app:touchRegionId="@id/sliderBar"
app:dragDirection="dragRight"
app:touchAnchorSide="left"/>
</Transition>
<Transition
android:id="@+id/queueCloseToOpenWithTrackInfoOpen"
app:constraintSetStart="@id/trackInfoOpenQueuePanelClosed"
app:constraintSetEnd="@id/trackInfoOpenQueuePanelOpen">
<OnSwipe
app:touchAnchorId="@id/sliderBar"
app:touchRegionId="@id/sliderBar"
app:dragDirection="dragLeft"
app:touchAnchorSide="left"/>
</Transition>
<Transition
android:id="@+id/queueOpenToCloseWithTrackInfoOpen"
app:constraintSetStart="@id/trackInfoOpenQueuePanelOpen"
app:constraintSetEnd="@id/trackInfoOpenQueuePanelClosed">
<OnSwipe
app:touchAnchorId="@id/sliderBar"
app:touchRegionId="@id/sliderBar"
app:dragDirection="dragRight"
app:touchAnchorSide="left"/>
</Transition>
</MotionScene>
class HomeFragment: Fragment() {
private lateinit var binding: FragmentHomeBinding
private val viewModel by viewModels<HomeViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentHomeBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.triggerAnimationButton.setOnClickListener {
if (homeFragmentMotionLayout.currentState == R.id.trackInfoClosedQueuePanelOpen) {
homeFragmentMotionLayout.setTransition(R.id.trackInfoClosedQueuePanelOpen, R.id.trackInfoOpenQueuePanelOpen)
homeFragmentMotionLayout.transitionToEnd()
} else if (homeFragmentMotionLayout.currentState == R.id.trackInfoOpenQueuePanelOpen) {
homeFragmentMotionLayout.setTransition(R.id.trackInfoOpenQueuePanelOpen, R.id.trackInfoClosedQueuePanelOpen)
homeFragmentMotionLayout.transitionToEnd()
}
}
homeFragmentMotionLayout.addTransitionListener(object: TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
endId: Int
) {
}
override fun onTransitionChange(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float
) {
}
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
when (currentId) {
R.id.trackInfoOpenQueuePanelOpen -> motionLayout?.setTransition(R.id.queueOpenToCloseWithTrackInfoOpen)
R.id.trackInfoOpenQueuePanelClosed -> motionLayout?.setTransition(R.id.queueCloseToOpenWithTrackInfoOpen)
R.id.trackInfoClosedQueuePanelOpen -> motionLayout?.setTransition(R.id.queueOpenToCloseWithTrackInfoClosed)
R.id.trackInfoClosedQueuePanelClosed -> motionLayout?.setTransition(R.id.queueCloseToOpenWithTrackInfoClosed)
}
}
override fun onTransitionTrigger(
motionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float
) {
}
})
}
Track and Queue closed
Track closed Queue open
Track and Queue open
Track open Queue closed
2