📜 ⬆️ ⬇️

Animator - what is it? Why is it needed? Why use it instead of Animation?

Hello! My name is Danil Perevalov, I work as an Android developer at Live Taiping, and today I have the honor to open our blog on Habré. The debut material is devoted to the type of classes Animator. The article will be useful to those who are just faced with the creation of animations, and those who are not enough already existing knowledge on the subject. Those who are familiar with the topic for a long time, we strongly ask you to share your experience in the comments.

What is Animator?


A bit of history. Since the launch of the Android platform, there has been a View Animation framework. It was intended, as the name implies, for animations. But the performance of the devices at the end of the zero was so low that nobody really thought about the beautiful animations, so the framework was not comfortable and flexible. He had only four types of animation (TranslateAnimation, AlphaAnimation, ScaleAnimation, RotateAnimation), a class that allows them to be combined (AnimationSet), and the ability to work only with classes inherited from View.

In Android 3.0, a much more flexible Property Animation framework has appeared. He is able to change any available property, and can also work with any classes. Its main tool is Animator.
')
Animator is a type of classes designed to change the values ​​of a selected object with respect to time. Roughly speaking, it is a tool for controlling the flow of a given duration, which changes a certain property from the initial value to the final one. Such a smoothly changing property in animation can be, for example, transparency.

The ideological difference between the Animator classes and View Animation is that View Animation changes the “view” of an object without changing the object itself (the exception is using setFillAfter (true), but with this flag the object changes at the end of the animation). Animator is designed to change the properties of the object itself.


Classes inherited from Animator


ValueAnimator (inherited from Animator)

In the simplest version, we set this class the type of the variable value, the initial value and the final value, and run it. In response, we will receive events at the beginning, end, repetition and cancellation of animation, and another two events, which are set separately for pause and change of value. The change event is perhaps the most important: it will receive a modified value, with which we will change the properties of objects.

Look at changing alpha with it:

ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { view.setAlpha((Float) animation.getAnimatedValue()); } }); animator.start(); 


ObjectAnimator, inherited from ValueAnimator

This is a class designed to simplify working with ValueAnimator. With it, you do not need to manually change any value on the change event — you simply give the Animator an object and specify the field you want to change, for example, scaleX. Using Java Refliction, a setter is searched for this field (in this case, setScaleX. Next, Animator will independently change the value of this field.
Using ObjectAnimator, the alpha change will look like this:

 ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).start(); 

The View class has several properties specifically designed for animating with Animator:


AnimatorSet (inherited from Animator)

This is a class that allows you to combine animations in various ways: run simultaneously or sequentially, add delays, etc.

ViewPropertyAnimator

This is a separate class. It is not inherited from Animator, but it has the same logic as ObjectAnimator for View, and is designed to easily animate any View without unnecessary problems.
This is how you can change alpha with it:

 view.animate().alphaBy(0).alpha(1).start(); 


How we started using Animator


About a year ago I was faced with the task of making an animation when clicking on an element. Here is this:


Not that I haven't done animations before, but they are rarely needed at outsourcing. Therefore, I google Animation Android. The first five links described in some detail how animations are made, and I started. Here is the first result:

Animation code
  public static void likeAnimation(@DrawableRes int icon, final ImageView imageView) { imageView.setImageResource(icon); imageView.setVisibility(View.VISIBLE); AlphaAnimation showAlphaAnimation = new AlphaAnimation(0.0f, 1.0f); showAlphaAnimation.setDuration(SHOW_DURATION); ScaleAnimation showScaleAnimation = new ScaleAnimation(0.2f, 1.4f, 0.2f, 1.4f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f); showScaleAnimation.setDuration(SHOW_DURATION); AnimationSet showAnimationSet = new AnimationSet(false); showAnimationSet.addAnimation(showAlphaAnimation); showAnimationSet.addAnimation(showScaleAnimation); showAnimationSet.setAnimationListener(new OnEndAnimationListener() { @Override public void onAnimationEnd(android.view.animation.Animation animation) { ScaleAnimation toNormalScaleAnimation = new ScaleAnimation(1.4f, 1.0f, 1.4f, 1.0f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f); toNormalScaleAnimation.setDuration(TO_NORMAL_DURATION); toNormalScaleAnimation.setAnimationListener(new OnEndAnimationListener() { @Override public void onAnimationEnd(android.view.animation.Animation animation) { AlphaAnimation hideAlphaAnimation = new AlphaAnimation(1.0f, 0.0f); hideAlphaAnimation.setDuration(HIDE_DURATION); ScaleAnimation hideScaleAnimation = new ScaleAnimation(1.0f, 0.2f, 1.0f, 0.2f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f); hideScaleAnimation.setDuration(HIDE_DURATION); AnimationSet hideAnimationSet = new AnimationSet(false); hideAnimationSet.setStartOffset(HIDE_DELAY); hideAnimationSet.addAnimation(hideAlphaAnimation); hideAnimationSet.addAnimation(hideScaleAnimation); hideAnimationSet.setAnimationListener(new OnEndAnimationListener() { @Override public void onAnimationEnd(android.view.animation.Animation animation) { imageView.setVisibility(View.GONE); } }); imageView.startAnimation(hideAnimationSet); } }); imageView.startAnimation(toNormalScaleAnimation); } }); imageView.startAnimation(showAnimationSet); } 


Link to code

The code turned out to be incomprehensible, which prompted me to search for a different approach in drawing up a sequence of animations. A solution was found on StackOveflow . The idea is to place each successive animation in the AnimationSet in the animation sequence with a shift equal to the sum of the durations of the previous animations. It turned out much better than it was:

AnimationSet
  public static void likeAnimation(@DrawableRes int icon, final ImageView imageView) { imageView.setImageResource(icon); imageView.setVisibility(View.VISIBLE); AnimationSet animationSet = new AnimationSet(false); animationSet.addAnimation(showAlphaAnimation()); animationSet.addAnimation(showScaleAnimation()); animationSet.addAnimation(toNormalScaleAnimation()); animationSet.addAnimation(hideAlphaAnimation()); animationSet.addAnimation(hideScaleAnimation()); animationSet.setAnimationListener(new OnEndAnimationListener() { @Override public void onAnimationEnd(Animation animation) { imageView.setVisibility(View.GONE); } }); imageView.startAnimation(animationSet); } private static Animation showAlphaAnimation() { AlphaAnimation showAlphaAnimation = new AlphaAnimation(0.0f, 1.0f); showAlphaAnimation.setDuration(SHOW_DURATION); return showAlphaAnimation; } private static Animation showScaleAnimation() { ScaleAnimation showScaleAnimation = new ScaleAnimation( 0.2f, 1.4f, 0.2f, 1.4f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); showScaleAnimation.setDuration(SHOW_DURATION); return showScaleAnimation; } private static Animation toNormalScaleAnimation() { ScaleAnimation toNormalScaleAnimation = new ScaleAnimation( 1.4f, 1.0f, 1.4f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); toNormalScaleAnimation.setDuration(TO_NORMAL_DURATION); toNormalScaleAnimation.setStartOffset(SHOW_DURATION); return toNormalScaleAnimation; } private static Animation hideAlphaAnimation() { AlphaAnimation hideAlphaAnimation = new AlphaAnimation(1.0f, 0.0f); hideAlphaAnimation.setDuration(HIDE_DURATION); hideAlphaAnimation.setStartOffset(SHOW_DURATION + TO_NORMAL_DURATION + HIDE_DELAY); return hideAlphaAnimation; } private static Animation hideScaleAnimation() { ScaleAnimation hideScaleAnimation = new ScaleAnimation( 1.0f, 0.2f, 1.0f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); hideScaleAnimation.setDuration(HIDE_DURATION); hideScaleAnimation.setStartOffset(SHOW_DURATION + TO_NORMAL_DURATION + HIDE_DELAY); return hideScaleAnimation; } 


Link to code

The code has become clearer and more readable, but there is one “but”: it is rather inconvenient to follow the shift for each animation even in such a simple sequence. If you add a few more steps, it will become an almost impossible task. Also an important disadvantage of this approach was the strange behavior of the animation: the size of the animated object, for reasons unknown to me, was larger than with the usual sequence of animations. Attempts to sort things out did not lead to anything, but I didn’t go deeper any more - I still didn’t like the approach. But I decided to develop this idea and split each step into a separate AnimatorSet. This is what happened:

AnimatorSet in AnimatorSet
 public static void likeAnimation(@DrawableRes int icon, final ImageView imageView) { imageView.setImageResource(icon); imageView.setVisibility(View.VISIBLE); AnimationSet animationSet = new AnimationSet(false); animationSet.addAnimation(showAnimationSet()); animationSet.addAnimation(toNormalAnimationSet()); animationSet.addAnimation(hideAnimationSet()); animationSet.setAnimationListener(new OnEndAnimationListener() { @Override public void onAnimationEnd(Animation animation) { imageView.setVisibility(View.GONE); } }); imageView.startAnimation(animationSet); } private static AnimationSet showAnimationSet() { AlphaAnimation showAlphaAnimation = new AlphaAnimation(0.0f, 1.0f); ScaleAnimation showScaleAnimation = new ScaleAnimation( 0.2f, 1.4f, 0.2f, 1.4f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); AnimationSet set = new AnimationSet(false); set.addAnimation(showAlphaAnimation); set.addAnimation(showScaleAnimation); set.setDuration(SHOW_DURATION); return set; } private static AnimationSet toNormalAnimationSet() { ScaleAnimation toNormalScaleAnimation = new ScaleAnimation( 1.4f, 1.0f, 1.4f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); AnimationSet set = new AnimationSet(false); set.addAnimation(toNormalScaleAnimation); set.setDuration(TO_NORMAL_DURATION); set.setStartOffset(SHOW_DURATION); return set; } private static AnimationSet hideAnimationSet() { AlphaAnimation hideAlphaAnimation = new AlphaAnimation(1.0f, 0.0f); ScaleAnimation hideScaleAnimation = new ScaleAnimation( 1.0f, 0.2f, 1.0f, 0.2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); AnimationSet set = new AnimationSet(false); set.setDuration(HIDE_DURATION); set.addAnimation(hideAlphaAnimation); set.addAnimation(hideScaleAnimation); set.setStartOffset(SHOW_DURATION + TO_NORMAL_DURATION + HIDE_DELAY); return set; } 


Link to code

Incorrect animation work, bad approach, everything is bad. Again, I turned to Google, and came across the fact that Animation is already a Legacy code, that is, outdated and not supported, although it is used.
I realized that you need to make animations completely different. And in the open spaces of Android Developers, I came across Animator. An attempt to make an animation with its help looked like this:

Animator
 public static void likeAnimation(@DrawableRes int icon, final ImageView view) { if (view != null && !isAnimate) { AnimatorSet set = new AnimatorSet(); set.playSequentially( showAnimatorSet(view), toNormalAnimatorSet(view), hideAnimatorSet(view)); set.addListener(getLikeEndListener(view, icon)); set.start(); } view.animate().alphaBy(0).alpha(1).start(); } private static AnimatorListenerAdapter getLikeEndListener(final ImageView view, final int icon) { return new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); isAnimate = true; view.setVisibility(View.VISIBLE); view.setImageResource(icon); view.setLayerType(View.LAYER_TYPE_HARDWARE, null); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); isAnimate = false; view.setVisibility(View.GONE); view.setImageDrawable(null); view.setLayerType(View.LAYER_TYPE_NONE, null); } }; } private static AnimatorSet showAnimatorSet(View view) { AnimatorSet set = new AnimatorSet(); set.setDuration(SHOW_DURATION).playTogether( ObjectAnimator.ofFloat(view, View.ALPHA, 0f, 1f), ObjectAnimator.ofFloat(view, View.SCALE_X, 0.2f, 1.4f), ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.2f, 1.4f) ); return set; } private static AnimatorSet toNormalAnimatorSet(View view) { AnimatorSet set = new AnimatorSet(); set.setDuration(TO_NORMAL_DURATION).playTogether( ObjectAnimator.ofFloat(view, View.SCALE_X, 1.4f, 1f), ObjectAnimator.ofFloat(view, View.SCALE_Y, 1.4f, 1f) ); return set; } private static AnimatorSet hideAnimatorSet(View view) { AnimatorSet set = new AnimatorSet(); set.setDuration(HIDE_DURATION).playTogether( ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0f), ObjectAnimator.ofFloat(view, View.SCALE_X, 1f, 0.2f), ObjectAnimator.ofFloat(view, View.SCALE_Y, 1f, 0.2f) ); set.setStartDelay(HIDE_DELAY); return set; } 


Link to code

The animation worked flawlessly, which means that the search can be considered finished. The only thing you need to remember when working with Animator is whether any Animator is already running for a specific view, because otherwise the old one will continue to execute, as if nothing had happened.

Deeper into the animator


I started searching for what else you can do with Animator. Flight of thought led me to the following:


When you press a button, four Animators are simultaneously executed:
Simultaneous launch
  AnimatorSet showHideSet = new AnimatorSet(); showHideSet.playTogether( ScrollAnimatorUtils.translationYAnimator(translationY, footerButtons), ScrollAnimatorUtils.translationYAnimator(translationY, footerText), ScrollAnimatorUtils.scrollAnimator(startScroll, endScroll, scrollView), ScrollAnimatorUtils.alphaAnimator(1, 0, recyclerView) ); showHideSet.start(); 


1) move down the list footer;
2) move down the buttons;
translationYAnimator
  public static Animator translationYAnimator(final float start, int end, final View view, int duration) { ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, end); animator.setDuration(duration); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); view.setTranslationY(start); } }); return animator; } 


3) ScrollView scroll down to the bottom;
scrollAnimator
 public static Animator scrollAnimator(int start, int end, final View view, int duration) { ValueAnimator scrollAnimator = ValueAnimator.ofInt(start, end); scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { view.scrollTo(0, (int) valueAnimator.getAnimatedValue()); } }); scrollAnimator.setDuration(duration); scrollAnimator.addListener(getLayerTypeListener(view)); return scrollAnimator; } 


4) imposes an alpha effect on recyclerView.
alphaAnimator
 public static Animator alphaAnimator(int start, int end, View view, int duration) { ValueAnimator alphaAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, start, end); alphaAnimator.setDuration(duration); alphaAnimator.addListener(getLayerTypeListener(view)); return alphaAnimator; } 


Link to the full code

This concludes the Animator overview. If you are aware of some interesting tricks related to Animator - supplement the article with your comments.
Link to the project in Github: github.com/princeparadoxes/AnimationVsAnimator

Source: https://habr.com/ru/post/274135/


All Articles