Property animation frame

Property animations can use ViewPropertyAnimator, ObjectAnimator, and ValueAnimator. It’s actually a progressive relationship: from left to right, it gets harder to use and more flexible. But the performance is the same, because the internal implementation of ViewPropertyAnimator and ObjectAnimator is actually ValueAnimator, and ObjectAnimator is a subclass of ValueAnimator. There is no difference in performance between the three. The only difference is ease of use and flexibility of function. So in the actual use of the choice, as long as follow a principle: try to use simple. ObjectAnimator does not need to be implemented with view.animate (). ObjectAnimator does not need to be implemented with ValueAnimator.

The property animation requires the object to provide the set method of the property, and the property animation calls the set method several times according to the initial and final values of the property. The value passed to the set method is different each time, precisely because it gets closer and closer to the final value over time. If the initial value is not passed during animation, then the get method is also provided because the system needs to get the initial value of the property.

The working principle can be summarized in three steps:

  1. If the initial value of the property is not set during animation creation, the Android system will obtain the initial value of the property through the get method. (ObjectAnimator)
  2. As the animation plays, the property animation framework uses the percentage of time elapsed to get the percentage change in the property value (that is, through the time interpolator), and then uses the percentage change in the property value to get the changed property value (that is, through the type estimator). (ObjectAnimator,ValueAnimator)
  3. The set method of the property sets the value of the changed property to the object and animates the change. (ObjectAnimator)
FILE LOCATION: res/animator/filename.xml The filename will be used as the resource ID. COMPILED RESOURCE DATATYPE: Resource pointer to a ValueAnimator, ObjectAnimator, or AnimatorSet. RESOURCE REFERENCE: In Java: Filename In XML: @[package:]animator/filename file location: res /animator/ filename. XML filename used as the resource ID. Compile resource data type: resource pointer to ValueAnimator, ObjectAnimator, or AnimatorSet. Resource references: in Java: r.nimator. filename in XML: @ Package :animator/ filenameCopy the code

Grammar;

<set
  android:ordering=["together"|"sequentially"] >

  <! <set> for the AnimatorSet class -->
  <! - android: ordering = [" animation son of a set of animation broadcast at the same time (default) "|" child animation in the animation set in accordance with the order, writing play "] -- >
  
  <! <objectAnimator> objectAnimator <objectAnimator>
  <! --<animator> tag corresponds to ValueAnimator class -->
  
    <objectAnimator
        android:propertyName="string"// The name of the property on which the animation worksandroid:duration="int"// Animation period, the default is300msandroid:valueFrom="float | int | color"// The initial value of the propertyandroid:valueTo="float | int | color"// The end value of the attributeandroid:startOffset="int"/ / callstartMethod after how many milliseconds to delay the start of playback animationandroid:repeatCount="int"// The number of iterations of the animation,- 1Is an infinite loop. The default is0Is played only once.android:repeatMode=["repeat"|"reverse"] // Animation repeat mode;repeat(Repeat),reverse(Repeat in reverse).android:valueType=["intType"|"floatType"] / / saidandroid:propertyNameThe type of the specified property, which defaults tofloatType. You don't need to specify the color attribute, />

    <animator
        android:duration="int"// Animation period, the default is300msandroid:valueFrom="float | int | color"// The initial value of the propertyandroid:valueTo="float | int | color"// The end value of the attributeandroid:startOffset="int"/ / callstartMethod after how many milliseconds to delay the start of playback animationandroid:repeatCount="int"// The number of iterations of the animation,- 1Is an infinite loop. The default is0Is played only once.android:repeatMode=["repeat"|"reverse"] // Animation repeat mode;repeat(Repeat),reverse(Repeat in reverse).android:valueType=["intType"|"floatType"] / / saidandroid:propertyNameThe type of the specified property, which defaults tofloatType. You don't need to specify the color attribute, />

    <set>.</set>
</set>
Copy the code

The animate () sample

Note: Two methods in the corresponding ViewPropertyAnimator;

  • The first one: for example translationX(100) means to animate the translationX value of the View to a gradient of 100. The View will move 100 pixels to the right. ViewPropertyAnimator does not support duplication
  • Second: for example, translationXBy(100) means animating the translationX value of the View by a gradient of 100. The View will move 100 pixels to the right. If the animation is executed again, it will be executed from where it ended, moving 100 pixels to the right again.

    /** * view.animate () * ViewPropertyAnimator does not support repetition */
    public void viewAnimate(View view){
        ViewPropertyAnimator vpa = img.animate();
        vpa.translationX(-300);
        Path interpolatorPath = new Path();
        // Run at a uniform speed of 25% at "animation completion: Time completion = 1:1"
        interpolatorPath.lineTo(0.25 f.0.25 f);
        // Then instantly jump to 150% animation completion
        interpolatorPath.moveTo(0.25 f.1.5 f);
        // Back up at a constant speed to the target
        interpolatorPath.lineTo(1.1);
        vpa.setInterpolator(PathInterpolatorCompat.create(interpolatorPath));
        vpa.setDuration(2000);
        vpa.withStartAction(new Runnable() {
            @Override
            public void run(a) {
                // A one-time listening method
                Toast.makeText(CustomSwitchingActivity.this."Animation starts",Toast.LENGTH_SHORT).show(); }}); vpa.withEndAction(new Runnable() {
            @Override
            public void run(a) {
                Toast.makeText(CustomSwitchingActivity.this."End of animation",Toast.LENGTH_SHORT).show();
                // A one-time listening method
                // Is called only when the animation ends normally, and not executed when the animation is canceled}}); }Copy the code

ObjectAnimator sample

A subclass of ValueAnimator because most of the start and end values of properties are obtained dynamically while the program is running, it is recommended to create code dynamically.

public class CustomSwitchingActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView iv;
	
    /** * Use the property animation ObjectAnimator * to achieve the four effects of tween animation */
    private TextView tvAlpha,tvScale,tvTranslate,tvRotate;

    /** * self-encapsulation * provides get and set methods */
    private TextView tvWrapper;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        overridePendingTransition(R.anim.custom_in,0);
        setContentView(R.layout.activity_custom_switching);
        initView();
        initData();
    }

    private void initData(a) {
        iv.setImageResource(R.drawable.ic_launcher_background);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.tv_custom_alpha:
                ObjectAnimator ob = ObjectAnimator.ofFloat(iv,"alpha".1.0.1).setDuration(2000);
                ob.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                            @Override
                            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                                float value = (Float) valueAnimator.getAnimatedValue();
                                Log.e("TAG", value + "");
                                if (value == 1){
                                    Toast.makeText(CustomSwitchingActivity.this."End of execution",Toast.LENGTH_SHORT).show(); }}});// Delay execution after 1000ms, need to call before start()
                ob.setStartDelay(1000);
                ob.start();
                break;
            case R.id.tv_custom_scale:
                final AnimatorSet as = new AnimatorSet();
                iv.setPivotX(iv.getWidth()/2);
                iv.setPivotY(iv.getHeight()/2);
                as.playTogether(
                        ObjectAnimator.ofFloat(iv,"scaleX".1.0).setDuration(2000),
                        ObjectAnimator.ofFloat(iv,"scaleY".1.0).setDuration(2000));// Add listening events
                as.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {
                        // when the animation starts
                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
                        // when the animation ends
                        Toast.makeText(CustomSwitchingActivity.this."End of execution, reset animation",Toast.LENGTH_SHORT).show();
                        // Reset the animation
                        as.removeListener(this);
                        as.setDuration(0);
                        as.setInterpolator(new ReverseInterpolator());
                        as.start();
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {
                        // called when the animation is cancelled

                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {
                        // called when the animation is repeated}}); as.start();break;
            case R.id.tv_custom_translate:
                final AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(
                        ObjectAnimator.ofFloat(iv,"translationX".20.100).setDuration(2000),
                        ObjectAnimator.ofFloat(iv,"translationY".20.100).setDuration(2000));// Another way to set up a listener, in which the listener method can be optionally overridden
                animatorSet.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        Toast.makeText(CustomSwitchingActivity.this."End of execution",Toast.LENGTH_SHORT).show();
                        // Reset the animation
                        animatorSet.removeListener(this);
                        animatorSet.setDuration(0);
                        animatorSet.setInterpolator(newReverseInterpolator()); animatorSet.start(); }}); animatorSet.start();break;
            case R.id.tv_custom_rotate:
                iv.setPivotX(iv.getWidth()/2);
                iv.setPivotY(iv.getHeight()/2);
                ObjectAnimator.ofFloat(iv,"rotation".0.360).setDuration(2000).start();
                break;
            case R.id.tv_custom_wrapper:
// Property animation cannot be used or property animation does not work:
// 1. This field does not have set and get methods
// 2. The set method only changes the value of the object property, but does not animate the change
// Solution:
// 1. Add get and set methods to this field if you have permission, such as in a custom View.
// 2. Use a wrapper class to wrap the corresponding class of the field and indirectly provide the get and set methods for the field.
// 3. Use ValueAnimator to monitor the animation process and implement attribute changes by yourself
ValueAnimator itself does not operate on any objects, that is, using it directly does not have any animation effects.
// It can animate a value, and then we can listen to it animate and modify the property values of our object as we animate it.

                // Change the ImageView width in the second way.
                ViewWrapper vw = new ViewWrapper(iv);
                ObjectAnimator.ofInt(vw,"width".20).setDuration(2000).start();

                // Change the ImageView width in the third way.
// performAnimate(iv, iv.getWidth(), 20);
                break;
                default:
                    break; }}private void performAnimate(final View target, final int start, final int end) {
// For this ValueAnimator, it will change a number from 1 to 100 in 2000ms, and then every frame of the animation will call onAnimationUpdate,
// In this method, we can get the current value (1-100), based on the proportion of the current value (current value /100),
// We can calculate what the width of the appearance should be, for example, after half time, the current value is 50, the ratio is 0.5,
// Assuming the starting width is 100px and the final width is 500px, the added width should also account for half of the total added width.
// The total width is 500-100=400, so the width should be increased by 400*0.5=200.
// Then the current width should be the initial width + increased width (100+200=300).
// The above calculation is very simple. It is actually an internal implementation of the integer valuator.
        ValueAnimator valueAnimator = ValueAnimator.ofInt(1.100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            // Hold an IntEvaluator object for use in the following valuation
            private IntEvaluator mEvaluator = new IntEvaluator();

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                // Get the current animation progress value, integer between 1 and 100
                int currentValue = (Integer)animator.getAnimatedValue();

                // Calculate the ratio of current progress to the entire animation process, floating point, 0-1
                float fraction = currentValue / 100f;

                // Here I am lazy, but why not use it when there is one (rational)
                // Call the integer estimator directly to scale the width and set it to Buttontarget.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end); target.requestLayout(); }}); valueAnimator.setDuration(2000).start();
    }

    private void initView(a) {
        iv=findViewById(R.id.iv_custom_anim);
        tvAlpha=findViewById(R.id.tv_custom_alpha);
        tvScale=findViewById(R.id.tv_custom_scale);
        tvTranslate=findViewById(R.id.tv_custom_translate);
        tvRotate=findViewById(R.id.tv_custom_rotate);

        tvWrapper=findViewById(R.id.tv_custom_wrapper);

        tvAlpha.setOnClickListener(this);
        tvScale.setOnClickListener(this);
        tvTranslate.setOnClickListener(this);
        tvRotate.setOnClickListener(this);
        tvWrapper.setOnClickListener(this); }}Copy the code
/** * Property animation cannot be used or property animation does not work: * 1. This field does not have set and get methods * 2. The set method of this property only changes the value of the property of the object, but does not animate the change. Add get and set methods to this field if you have permission, such as in a custom View. * 2. Use a wrapper class to encapsulate the corresponding class of the field and indirectly provide the get and set methods for the field. * ValueAnimator does not work on any object, that is, it has no animation effect. * It can animate a value, and then we can listen to the animation and modify the property values of our object during the animation, which is equivalent to our object being animated. * * To change the width of the ImageView, use the second method. The View wrapper class provides get and set methods

public class ViewWrapper {

    private View view;

    public ViewWrapper(View view) {
        this.view = view;
    }

    public int getHeight(a){
        return view.getLayoutParams().height;
    }

    public void setHeight(int height){ view.getLayoutParams().height = height; view.requestLayout(); }}Copy the code

Common scenarios:

Changing multiple properties in the same animation

Unlike View.animate (), which can be chained, ObjectAnimator uses PropertyValuesHolder to change multiple properties simultaneously in a single animation.

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX".0.1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY".0.1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha".0.1);
// PropertyValuesHolder is a batch store of property values.
// Use ofPropertyValuesHolder() to put into the Animator.
ObjectAnimator animator3 = ObjectAnimator.ofPropertyValuesHolder(ivHolder, holder1, holder2, holder3);
animator3.setDuration(2000);
animator3.start();
Copy the code

AnimatorSet Multiple animations executed together

Interpolator if you share a series of Settings such as start time, end time, Interpolator, etc. within the same animation, the animation will not be executed sequentially.

    ObjectAnimator animator4 = ObjectAnimator.ofFloat(ivHolder, "translationX".0, -200);
    animator4.setDuration(1000);
    ObjectAnimator animator5 = ObjectAnimator.ofFloat(ivHolder, "alpha".0.1);
    animator5.setDuration(1000);
    ObjectAnimator animator6 = ObjectAnimator.ofFloat(ivHolder, "translationX", -200.200);
    ObjectAnimator animator7 = ObjectAnimator.ofFloat(ivHolder, "rotation".0.1080);
    animator7.setDuration(1000);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(animator4).before(animator5); // Go to 4 and then 5
    animatorSet.playTogether(animator5,animator6, animator7); // 5,6 and 7 start at the same time
	animatorSet.start();
	
    // The two animations are executed in sequence
      animatorSet.playSequentially(animator4, animator5);
    // Both animations are executed simultaneously
      animatorSet.playTogether(animator1, animator2);
    / / use AnimatorSet. Play (animatorA) with/before/after (animatorB)
    // to precisely configure the relationships between animators
    // animatorSet.play(animator1).with(animator2);
    // animatorSet.play(animator1).before(animator2);
    // animatorSet.play(animator1).after(animator2);
Copy the code

PropertyValuesHolders. OfKeyframe () split the same properties

PropertyValuesHolder separates the same animation property into multiple stages by setting a Keyframe. For example, you can increase a progress to 100% and then “bounce” back.

 // Start at 0%
 Keyframe keyframe1 = Keyframe.ofFloat(0.0);
 // The animation is 100% complete after 50% of the time
 Keyframe keyframe2 = Keyframe.ofFloat(0.5 f.100);
 // When the time is 100%, the animation's completion regress to 80%, i.e., it bounces 20%
 Keyframe keyframe3 = Keyframe.ofFloat(1.80);
 PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
 
 ObjectAnimator animator8 = ObjectAnimator.ofPropertyValuesHolder(viewKey, holder);
 animator8.setInterpolator(new FastOutSlowInInterpolator());
 animator8.setDuration(2000);
 animator8.start();
Copy the code

ValueAnimator sample

The ValueAnimator class is just a number generator. It only has the second step of the above principle. ValueAnimator generates regular numbers following the steps in Step 2 of how the property animation framework works. ValueAnimator is not commonly used because its functionality is so basic. In effect, ValueAnimator is an ObjectAnimator that cannot specify the version of the target object. ObjectAnimator automatically calls setter methods of the target object to update the value of the target property and, in many cases, to change the UI of the target object. ValueAnimator, on the other hand, uses gradients to change a single piece of data that does not belong to an object. It is up to you to decide what to do after the data is updated. The least functional and inconvenient, but sometimes the least restrictive and most flexible. For example, if you are animating a third-party control and there is no setter method for the property to be updated, you can use ValueAnimator to update the property value in its onUpdate() and manually call invalidate().

public class CustomSwitchingActivity extends AppCompatActivity {

    /** * properties ValueAnimator * implement search box show and hide */
    private EditText etSearch;
    private Button btSearch;
    private int etWidth;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_switching);
        initView();
        initData();
    }

    private void initData(a) {
        etSearch.post(new Runnable() {
            @Override
            public void run(a) { etWidth=etSearch.getWidth(); }}); }private void initView(a) {
        btSearch = findViewById(R.id.bt_custom_search);
        etSearch = findViewById(R.id.et_custom_search);
    }

    /** * controls the display of the search bar */
    public void search(View view){
        btSearch.setEnabled(false);
        if (etSearch.getVisibility() == View.GONE){
            // Displays the search bar
            animateShow(etSearch);
        }else {
            // Hide the search baranimateHide(etSearch); }}/** * hide */
    private void animateHide(final View view) {
        ValueAnimator va = createDropAnimator(view,etWidth,0);
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                view.setVisibility(View.GONE);
                btSearch.setEnabled(true); }}); va.setInterpolator(new DecelerateInterpolator());
        va.setDuration(2000);
        va.start();
    }
	
    /** * displays */
    private void animateShow(final View view) {
        view.setVisibility(View.VISIBLE);
        ValueAnimator va = createDropAnimator(view,0,etWidth);
        va.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                btSearch.setEnabled(true); }}); va.setInterpolator(new AccelerateInterpolator());
        va.setDuration(2000);
        va.start();
    }

    private ValueAnimator createDropAnimator(final View view, int start, int end){
        ValueAnimator va = ValueAnimator.ofInt(start,end);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                params.width = (int) valueAnimator.getAnimatedValue(); view.setLayoutParams(params); }});returnva; }}Copy the code

Common Scenarios

Set transition animations for the show and hide of the ViewGroup’s child views

The Android system provides four types of transition animations: Open the ViewGroup animateLayoutChanges property in the Layout file to use the default transition animations provided by the system.

public class CustomSwitchingActivity extends AppCompatActivity {

    /** * ViewGroup adds a transition animation for the child View */
    private LinearLayout llImgs;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_switching);
        initView();
        initData();
    }

    private void initData(a) {
        // Add a transition animation to the child View of the ViewGroup
        imgsTransition();
    }

    private void initView(a) {
        llImgs = findViewById(R.id.ll_imgs);
    }
	
    /** * add image */
    public void imgAdd(View view){
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.setMargins(0.0.10.0);
        FrameLayout frameLayout = new FrameLayout(this);
        frameLayout.setLayoutParams(layoutParams);

        FrameLayout.LayoutParams imgParams = new FrameLayout.LayoutParams(200.200);
        ImageView imageView = new ImageView(this);
        imageView.setImageResource(R.drawable.ic_launcher_background);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setLayoutParams(imgParams);

        FrameLayout.LayoutParams tvParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        TextView tv = new TextView(this);
        tvParams.gravity = Gravity.RIGHT|Gravity.BOTTOM;
        tv.setText(String.valueOf(llImgs.getChildCount()+1));
        tv.setLayoutParams(tvParams);

        frameLayout.addView(imageView);
        frameLayout.addView(tv);

        llImgs.addView(frameLayout, llImgs.getChildCount());
    }

    /** * remove the image */
    public void imgRemove(View view){
        int count = llImgs.getChildCount();
        if (count > 0) {
            llImgs.removeViewAt(llImgs.getChildCount()-1); }}private void imgsTransition(a) {
// APPEARING
// When a child View is VISIBLE by setting it to VISIBLE or by adding it to the addView method,
// The child View executes that type of animation.
// This type of animation has a period of 300 milliseconds, with a default delay of 300 milliseconds.
//
// DISAPPEARING
// Hide a child View by setting its visibility to GONE or removing it from removeView.
// The child View executes that type of animation.
// The period of this type of animation is 300 ms, with a default delay of 0 ms.
//
// CHANGE_APPEARING
// When a child View is displayed, all sibling views immediately execute that type of animation in sequence and the animation gap between sibling views is 0 milliseconds by default before the child View is animated.
// The period of this type of animation is 300 ms, with a default delay of 0 ms.
//
// CHANGE_DISAPPEARING
// When the animation of the hidden child View is complete, all sibling views will execute that type of animation in sequence and the gap between sibling views is 0 ms by default.
// This type of animation has a period of 300 milliseconds, with a default delay of 300 milliseconds.
//
// Note that this is the default behavior of the system, we can make appropriate changes.
// Set transition animations of the Change_Chat, Chat, bate and Change_bate types in turn
        LayoutTransition transition = new LayoutTransition();

        // This method is used to set the interval between executing animations of the same type for multiple sub-views. The default interval is 0 ms.
        transition.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
        // Sets the animation execution period for the specified type of transition animation. Default is 300 ms.
        transition.setDuration(LayoutTransition.CHANGE_APPEARING, transition.getDuration(LayoutTransition.CHANGE_APPEARING));
        // Sets the execution delay for the specified type of transition animation. By default, it depends on the type of transition animation.
        transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, 0);

        ObjectAnimator appearingAnimator = ObjectAnimator.ofPropertyValuesHolder(
                        (Object) null,
                        PropertyValuesHolder.ofFloat("scaleX".0.0 f.1.0 f),
                        PropertyValuesHolder.ofFloat("scaleY".0.0 f.1.0 f),
                        PropertyValuesHolder.ofFloat("alpha".0.1.0 f));
        // Sets a custom property animation for the specified type of transition animation.
        transition.setAnimator(LayoutTransition.APPEARING, appearingAnimator);
        transition.setDuration(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.APPEARING));
        transition.setStartDelay(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.CHANGE_APPEARING));

        ObjectAnimator disappearingAnimator = ObjectAnimator
                .ofPropertyValuesHolder(
                        (Object) null,
                        PropertyValuesHolder.ofFloat("scaleX".1.0 f.0.0 f),
                        PropertyValuesHolder.ofFloat("scaleY".1.0 f.0.0 f),
                        PropertyValuesHolder.ofFloat("alpha".1.0 f.0));
        transition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnimator);
        transition.setDuration(LayoutTransition.DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));
        transition.setStartDelay(LayoutTransition.DISAPPEARING, 0);

        transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
        transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));

        // The setLayoutTransition method sets the transition animation for the ViewGroup.llImgs.setLayoutTransition(transition); }}Copy the code

Property to animate listener

There are two main interfaces:

  • AnimatorUpdateListener: Listens for the start, end, cancel, and repeat of an animation. ,
  • AnimatorListener: Listens for the entire animation process and is called once each frame is played.

note

Welcome to follow wechat official account:No reason also