Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Animating Constraint Layouts

I want to animate two ConstraintLayouts - one being the child of another.

I know that animations only work on direct childs of an ConstraintLayout, so this is my layout:

<android.support.constraint.ConstraintLayout 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"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.constraint.ConstraintLayout
        android:id="@+id/cl"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#393939"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:visibility="visible">

        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Move me!"
            android:textColor="#fff"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.8" />
    </android.support.constraint.ConstraintLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Animate" />
</android.support.constraint.ConstraintLayout>

Which has a ConstraintLayout as root layout and an inner ConstraintLayout which has a background color and a TextView. The inner layout is set to gone in the beginning.

I want to fade in the inner ConstraintLayout (set its visibility to View.VISIBLE) and also move the TextView up (set its vertical Bias to a lower number). Since I can't animate inner Child views, I create 4 Constraint Sets - csA for the visibility animation of the inner ConstraintLayout and csB for the animation to move the TextView upwards.

class MainActivity : AppCompatActivity() {
    val csA1 = ConstraintSet()
    val csA2 = ConstraintSet()

    val csB1 = ConstraintSet()
    val csB2 = ConstraintSet()

    lateinit var btn: Button
    lateinit var cl: ConstraintLayout
    lateinit var root: ConstraintLayout

    var switch = false


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        root = findViewById(R.id.root_layout)
        val tv: TextView = findViewById(R.id.textview)
        cl = findViewById(R.id.cl)
        btn = findViewById(R.id.button)

        btn.setOnClickListener { toggle() }

        csA1.clone(root)
        csA2.clone(csA1)
        csA2.setVisibility(R.id.cl, View.VISIBLE)

        csB1.clone(cl)
        csB2.clone(csB1)
        csB2.setVerticalBias(R.id.textview, 0.3f)

    }

    fun toggle() {
        TransitionManager.beginDelayedTransition(root)
        val cs = if (!switch) csA2 else csA1
        cs.applyTo(root)
        val vsB = if (!switch) csB2 else csB1

        TransitionManager.beginDelayedTransition(cl)
        vsB.applyTo(cl)

        switch = !switch
        btn.text = "switch: $switch"
    }
}

The first button press may work - but on the second press, the layout does not get hidden, and the view stays where it is. The text gets cut to the first letter, full text is shown on the next click. When the trigger is then set from true to false, the background blinks to white for a really short time, then goes to grey again.

There is nothing else in that project. If I disable one of the animations, the other is working without any problem.

What I have tried:

  • Delaying vsB via Handler & postDelayed as well as setting a custom transition with a startDelay with the duration of the visibility animation.
   
    Handler().postDelayed({
        TransitionManager.beginDelayedTransition(cl)
        vsB.applyTo(cl)
    }, 450)
  • With the handler and a delay of 450 ms (I did not test the minimum limit of this) - the animation does work. It does not lag, the layout gets shown and hidden properly on every button press and the textview moves upwards. But that's not quite my requirement. I need to animate them simultaneously, or even better: with a delay of 50ms on the upwards movement.

  • With the handler and a delay of 1 ms, the layout is shown on first press, the TextView is already in its final position (no movement).

  • Custom transition with a startDelay, which reacts like the version without anything: TextView.text gets cut to the first letter, background blinks to white and goes back to gray. The transitions to not seem to have any effect, not even with a greater startDelay (duration + 200, for example).

   
    fun toggle() {
        val duration = 200L
        val transitionA = AutoTransition()
        transitionA.duration = duration

        val transitionB = AutoTransition()
        transitionB.startDelay = duration

        TransitionManager.beginDelayedTransition(root, transitionA)
        val cs = if (!switch) csA2 else csA1
        cs.applyTo(root)

        val vsB = if (!switch) csB2 else csB1

        TransitionManager.beginDelayedTransition(cl,transitionB)
        vsB.applyTo(cl)

        switch = !switch
        btn.text = "switch: $switch"
    }

I tested this on an emulator on API 27 and a Samsung S6 on API 24. Other technical info:

   
    compileSdkVersion 27

    minSdkVersion 21
    targetSdkVersion 27

    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'


    Android Studio Version 3.1.2
    Android SDK Tools 26.1.1 

Is the simultaneous animation of two ConstraintLayouts even possible?

like image 218
yennsarah Avatar asked May 22 '18 14:05

yennsarah


1 Answers

Since I still can't comment (not enough rep), I'll write it here, but read it like a comment.
You are thinking of ConstraintSets wrong.
If you just want to set some stuff visible and animate it to another location, you need to make two .xml's. They need to have the same views inside (no matter if visible or not) and then apply them via TransitionManager and ConstraintSets.
Simple example without code:

You have a ConstraintLayout xml with one ImageView in it. In the first xml it's set to be at the top of the Layout, in the second you have set it to the bottom. When you now clone these layouts and apply one to another (on a button click probably), then it will animate the ImageView from the top to the bottom.

Have another look at the official doc and try it like that. If you understand it once, it's actually quite easy. If you have further questions or want me to give you a better example, just ask.

like image 77
IntersectGlasses Avatar answered Oct 20 '22 11:10

IntersectGlasses