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

1

1

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.

Franky answered 9/3, 2021 at 12:54 Comment(0)
S
7

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

Salmonoid answered 9/3, 2021 at 14:50 Comment(2)
Thanks a bunch for your very detailed explanation. I've just tried it on an emulator and on a physical device but the result I get is just like the second gif. Do you remember if you did anything else? Thanks again ^^Franky
You need to set LinearInterpolator to blue and red transitions, only after that their bordering edge will animate exactly, without visual white gaps. But problem here is that Slide transition uses AccelerateInterpolator/DecelerateInterpolator for hide/show animation and as I've see from sources you can't change that behaviour via setInterpolator method. The best solution here is to write custom Slide transition which will support LinearInterpolator or try to hide that gap with delays, different animation durations etcSalmonoid

© 2022 - 2024 — McMap. All rights reserved.