ConstraintLayout Slide Transition animation is displaying the view's full height before the animation ends

1.7k Views Asked by At

I have the following Activity and layout:

import android.os.Bundle
import android.view.Gravity
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.transition.Slide
import androidx.transition.TransitionManager
import com.tests.playground.R


class ConstraintLayoutTransitionActivity : AppCompatActivity() {

    private val rootView: ConstraintLayout by lazy { findViewById(R.id.root) }
    private val bottomBlueView: View by lazy { findViewById(R.id.bottomBlue) }
    private val switchButton: View by lazy { findViewById(R.id.switchButton) }
    private var isBottomBlueViewShowing = true

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_constraint_layout_transition)

        switchButton.setOnClickListener(::onSwitchButtonClicked)
    }

    @Synchronized
    private fun onSwitchButtonClicked(view: View) {
        isBottomBlueViewShowing = !isBottomBlueViewShowing

        val constraintSet = ConstraintSet()
        constraintSet.clone(rootView)

        val transition = Slide()
        transition.duration = 1000L

        if (isBottomBlueViewShowing) {
            constraintSet.setVisibility(bottomBlueView.id, View.VISIBLE)
            transition.slideEdge = Gravity.BOTTOM
        } else {
            constraintSet.setVisibility(bottomBlueView.id, View.GONE)
            transition.slideEdge = Gravity.BOTTOM
        }

        TransitionManager.beginDelayedTransition(rootView, transition)
        constraintSet.applyTo(rootView)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/topRed"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#F00"
        app:layout_constraintBottom_toTopOf="@+id/bottomBlue"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/switchButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="Switch"
        app:layout_constraintBottom_toBottomOf="@+id/topRed"
        app:layout_constraintEnd_toEndOf="@+id/topRed"
        app:layout_constraintStart_toStartOf="@+id/topRed"
        app:layout_constraintTop_toTopOf="@+id/topRed" />

    <View
        android:id="@+id/bottomBlue"
        android:layout_width="0dp"
        android:layout_height="142dp"
        android:background="#00F"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

When I try to show/hide the bottomBlue, here's what happens:

Flick

As you can see,

  • when I hide the view the button goes straight down without animating (the bottomBlueView animation is ok`;
  • when I show the view the button also goes straight up without animating. But what bugs me the most is that a white area is show before the animation starts and sticks there until the end.

Is there any way to solve theses animations problems? I've already tried many ways (with ConstraintSet and Animation) but none of them work as expected.

1

There are 1 best solutions below

2
On

You can use AutoTransition for transition.

TransitionManager.beginDelayedTransition(rootView, AutoTransition())
    

Result will be like this:

AutoTransition

AutoTransition uses Fade transition for visibility changes of blue area. If you need blue area to slide you should provide custom transition. It should be ChangeBounds transition for red area and blue button and Slide transition for blue area:

private val topRed: View by lazy { findViewById(R.id.topRed) }
  
private fun onSwitchButtonClicked(view: View) {
        isBottomBlueViewShowing = !isBottomBlueViewShowing

        val transitionSet = TransitionSet()
        transitionSet.ordering = TransitionSet.ORDERING_TOGETHER

        //blue btn
        val blueBtnTransition = Slide(Gravity.BOTTOM)
        blueBtnTransition.addTarget(bottomBlueView)

        //red area + switch btn
        val topRedTransition = ChangeBounds()
        topRedTransition.addTarget(topRed)
        topRedTransition.addTarget(switchButton)

        transitionSet.addTransition(blueBtnTransition)
        transitionSet.addTransition(topRedTransition)

        val blueBtnVisibility = if (isBottomBlueViewShowing) {
            View.VISIBLE
        } else {
            View.GONE
        }

        val constraintSet = ConstraintSet()
        constraintSet.clone(rootView)
        constraintSet.setVisibility(bottomBlueView.id, blueBtnVisibility)

        TransitionManager.beginDelayedTransition(rootView, transitionSet)
        constraintSet.applyTo(rootView)
    }

Here is result:

custom_transition

As you can see there is still white area between red and blue areas when it animates. You can solve it via adding delays to transitions:

    blueBtnTransition.startDelay = if (isBottomBlueViewShowing) 0 else 100
    topRedTransition.startDelay = if (isBottomBlueViewShowing) 100 else 0

Here is result. IMHO with delays it looks much better:

with_delays