Home » Android » android – How do I eliminate the delay before an LayoutTransition animation

android – How do I eliminate the delay before an LayoutTransition animation

Posted by: admin May 14, 2020 Leave a comment

Questions:

I’m trying out the new LayoutTransition class in Honeycomb. I have set an animation that slides a View into place when adding it to a ViewGroup. I noticed that there is a slight delay (around 20ms) between when a view first renders and when the LayoutTransition.APPEARING animation begins. In other words, after the view appears on screen, it hangs in the air for a moment, and then starts to animate into place. You can notice this even in the ApiDemos sample project. In the layout animation examples, there’s always a delay before a ViewGroup‘s APPEARING animation starts. I’ve even tried setting the other LayoutTransition animations to null, or finally giving them very short durations, but still the APPEARING animation is delayed. Here’s my code:

public class DebugExampleFour extends Activity {
    private int numButtons = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.debug_example_four);

        final ViewGroup frame = (ViewGroup) findViewById(R.id.frame_container);
        LayoutTransition transitioner = new LayoutTransition();

        Animator appearingAnimation = ObjectAnimator.ofFloat(null, "translationX", 600, 0);
        appearingAnimation.setDuration(45);
        appearingAnimation.setStartDelay(0);
        appearingAnimation.setInterpolator(new DecelerateInterpolator());
        appearingAnimation.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                View view = (View) ((ObjectAnimator) anim).getTarget();
                view.setTranslationX(0f);
            }
        });
        transitioner.setAnimator(LayoutTransition.APPEARING, appearingAnimation);
        Animator dummyAnimation = ObjectAnimator.ofInt(0, 1);
        dummyAnimation.setDuration(1);
        dummyAnimation.setStartDelay(0);
        transitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, dummyAnimation);
        transitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, dummyAnimation);
        transitioner.setAnimator(LayoutTransition.DISAPPEARING, dummyAnimation);

        frame.setLayoutTransition(transitioner);

        Button addButton = (Button) findViewById(R.id.addNewButton);
        addButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Button newButton = new Button(DebugExampleFour.this);
                newButton.setText("Click To Remove " + (numButtons++));
                newButton.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        frame.removeView(v);
                    }
                });
                frame.addView(newButton, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

            }
        });    
    }

}

Here’s the layout file that goes along with the example:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:orientation="vertical">
    <Button android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="Add Button"
        android:id="@+id/addNewButton" />
    <LinearLayout android:orientation="vertical"
        android:layout_width="match_parent" android:layout_height="match_parent"
        android:id="@+id/frame_container" android:animateLayoutChanges="true" />
</LinearLayout>

How do I eliminate the delay before the APPEARING animation?

How to&Answers:

Right – there’s a delay at both levels. When you’re running an Animator in the context of a LayoutTransition, you want to set the duration and startDelay on the LayoutTransition object, not on the underlying Animator (because the transition object will supply its own values for these properties to the animators that it runs).

The reason for this is that a LayoutTransition is generally going to be a sequence of animations that run on the objects inside a container. So, for example, if you want an object to ‘appear’ in a container, then the transition will first animate the other objects out of the way, then animate the new object into place. Imagine adding an item into the middle of a set of objects (as in the case you see in the ApiDemos apps); it first makes space, then fades the object in.

In the case of the original code above, you want the appearing animation to run immediately, so you should set the transition startDelay for APPEARING to 0 (as you did in the answer above).

Going the other way, DISAPPEARING animations by default have no startDelay, but CHANGE_DISAPPEARING animations have a startDelay equal to the duration of the DISAPPEARING animation, under the assumption that you first want to remove an item, then animate the other items in the container into their new places.

Since these are assumptions that don’t necessarily apply to every situation, there are duration/startDelay properties on LayoutTransition to control the behaviors according to how you need it to work in your specific cases.

Note also that if you don’t want to run one of the animation types, you should set that animation to null (see the documentation for LayoutTransition.setAnimator(int, Animator)). Setting it to your dummyAnimator will not have the same effect. For one thing, the default duration/startDelay values on the LayoutTransition will still apply, even if you supply custom Animators for these animations.

Something else to be aware of: the underlying timing mechanism (for Android, but also for most other platforms I’ve ever worked on) is going to have some minimum resolution. So you you set a duration of ‘1’, that may not result in that animation ending in 1ms. Instead, it will run for one frame, then on the next frame (generally the refresh rate of the device in a well-behaved application if the system is not bogged down) it will see that the animation should end.

Answer:

It turns out both Animator and LayoutTransition each has a start delay value. The Animator start delay is already zero, so changing that didn’t help. What is weird is that the start delay for LayoutTransition appears to be greater than zero, at least for the case of LayoutTransition.APPEARING. Here’s the working code:

public class DebugExampleFour extends Activity {
    private int numButtons = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.debug_example_four);

        final ViewGroup frame = (ViewGroup) findViewById(R.id.frame_container);
        LayoutTransition transitioner = new LayoutTransition();

        Animator appearingAnimation = ObjectAnimator.ofFloat(null, "translationX", 600, 0);
        appearingAnimation.addListener(new AnimatorListenerAdapter() {
            public void onAnimationEnd(Animator anim) {
                View view = (View) ((ObjectAnimator) anim).getTarget();
                view.setTranslationX(0f);
            }
        });
        transitioner.setAnimator(LayoutTransition.APPEARING, appearingAnimation);
        transitioner.setDuration(LayoutTransition.APPEARING, 300);
        transitioner.setStartDelay(LayoutTransition.APPEARING, 0);

        frame.setLayoutTransition(transitioner);

        Button addButton = (Button) findViewById(R.id.addNewButton);
        addButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Button newButton = new Button(DebugExampleFour.this);
                newButton.setText("Click To Remove " + (numButtons++));
                newButton.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        frame.removeView(v);
                    }
                });
                frame.addView(newButton, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

            }
        });    
    }
}