⬆️ ⬇️

Yandex Lecture: Advanced UI, Part Two

This is the second part of a lecture by Dmitry Svirikhin - a developer from the Yandex.Mail mobile team.



- We continue to consider the typical problems of the Android developer and how to solve them. We have already considered how to solve the problem of inconsistency in the UI in our application, the problems that can arise when interacting with the keyboard, and the problems of losing the state, as well as we learned how we can effectively use custom view. We will devote the whole second part to another problem - it is called “insufficient interactivity”. Let's see how we can make our application more interactive and understandable for the user.





First, I will give an example of insufficient interactivity. Let us return to the annex with the main theses of the report, which I am now telling you. Here the user opens it, tapes on the second part, and something happens, absolutely incomprehensible to the user.

')





Look again. Tap on the second part, and bang, suddenly something happened. It will take some time for the user to understand, realize what has happened at all now, how can I go back and see the first part. Therefore, we can prompt this user to the desired order of actions using animation, something like this.



Oh, the second part has opened up, and the first went upstairs. At the same time, the user understands that if we then scroll up, we will again return to the first part, and this becomes obvious to him due to such animation.



As you have already guessed, we will now talk about animation, about what methods of creating animation exist in 2017, and which of them we really can and should use in reality.



In general, there are a lot of ways to create something moving in 2017. Here is a list. And you, perhaps, in your lectures in part some of them concerned. But now we will make a deeper immersion in each of them, and we will understand which of them we may really need.



We start with View animation. This is the main way to animate the view, which we had before Android with version 2.3. And then in 2009–2010, no one really thought at all about animations, as long as something spun there. Therefore, View animation has an absolutely small set of changes that we can make with the help of animation. But it has one big minus - that it changes only the view representation at the drawing stage. That is, if we make some button that, for example, we fly out of the screen, if during the animation you click on the place where the button was originally located, this will be calculated as a click on this button. Therefore, we can say that this method of animation is outdated, you should not use it in 2017, so we will not consider it in detail, we will immediately proceed to the next one.





Drawable animation is such a way to create animations, to understand it, the easiest way to look at the XML that represents it. In fact, this is the usual frame-by-frame animation, and nothing else. And it should be used only for some very complicated cases that we cannot do by any other methods. This animation is quite complex for the system, because we need a new drawable load, perhaps even for each frame. Therefore, it is also worth using it only in extreme cases.



And we turn to the most interesting - ValueAnimator, which appeared in our third version of Android. This is the basic engine for animation. Plus it consists in the fact that it is absolutely not tied to the view. By ValueAnimator has its own call hierarchy in the process of forming the frame. And what do we need to do something with a ValueAnimator? We need to specify the initial value, the final value, specify the Listener, in which we will receive intermediate values, and we will be able to apply them to some of our view.



In general, client-side development of ValueAnimator is not used very often, but we still see what code we need to write to make it work for us.



We will watch the simplest and most boring animation that you can only imagine - this is a crossfade. Although it is simple and boring, but, nevertheless, it performs its work well when it is necessary, when it is necessary to smooth out some corners when changing the screen.





So, let's see at once the code that we need from ValueAnimator in order for us to make this crossfade work. We need to create two ValueAnimator, respectively, to revise the value from zero to one and from one to zero. Then set UpdateListener for each of them, in which we will get the current animation value from the animator, go directly to the view using the setAlpha method. We also need AnimatorSet, which defines a number of animators that can be executed either sequentially or in parallel. We define it ourselves. In our case, they are executed at the same time, which is called the playTogether method.





We also set the duration of this animation, and some listeners that we call, methods that we call before the animation starts and after it ends. Accordingly, before the start of the animation, we will make visible the view that we should have. After the end of the animation, we will hide the view, which we should, respectively, disappear.



And now let's look at how the animator engine works on the example of animating an integer from 0 to 255 using ValueAnimator.ofint.



The time in Animator is represented as a real value from 0 to 1. That is, if we take into account that a frame in Android should be rendered for 16 and 2/3 ms, and, let's say, our animation will be 167 ms, then this animation should be executed for 10 frames. That is, 1/10 of the total time, then, as it appears in the engine, for each frame.



Let's see how the animation calculation for the fourth frame will look like, that is, with an animator time value of 0.4.



First, the value of 0.4 falls into the TimeInterpolator - this is some function that determines the speed with which we want the animation to take place. For example, if we want our animation speed to increase, we can use, as an example, the AccelerateInterpolator. It defines the increasing function as a regular quadratic function, and it is also worth noting that the TimeInterpolator should receive some value from 0 to 1 at the input, and, accordingly, return it as well.



So, the TimeInterpolator in this case will return us a value of 0.16, we will call this the interpolated time, and it is already being fed to the input to the TypeEvaluator. TypeEvaluator determines how the object should change in the course of animation relative to time. That is, for example, in the case of animation simple int here everything is calculated very simply: we take the final value, subtract the initial value from it, and multiply it by time. In our case, this is 0.16. The resulting value of 40 is returned to Animator, it is found in Animator, and after that this Animator is thrown into UpdateListener. We can get everything we need from it — these are animated values ​​— and apply some kind of view. This was done in the sample code.



The most important thing is that here it is necessary to understand that we can replace TimeInterpolator and TypeEvaluator exactly the way we need it in each specific case.



Let's first consider what the TimeInterpolators are in the system.



To begin, consider the most classic. Here are three of them, which are mostly and most often used - this is AccelerateInterpolator, which defines a function with increasing speed. It, as a rule, should be used for elements that are going to disappear from the screen. If you look at the yellow ball, it flies off the screen.



Why should there be a function with increasing speed? So that most of the time this element simply spent on the screen, so that the user accurately noticed it. If we used, for example, the DecelerateInterpolator, which, on the contrary, uses the increasing speed, for the missing element, the user may not even notice this.



Accordingly, we should use the DecelerateInterpolator (blue ball) for the elements that appear on the screen. And AccelerateDecelerateInterpolator defines a function, the speed of which first increases, and then, accordingly, decreases. It should be used for animation, which is associated with the view, which simply changes on the screen, changes its size or position, and remains on the screen.



Also in Android 5, there were such modern interpolators that are absolutely similar to those we have already considered. They are believed to work more smoothly and naturally. Indeed, they look a little nicer, and we can use them, by the way, not only from Android 5, but they are present in the Support library, that is, we can use them absolutely on any versions. And it seems that for current projects, these interpolators are more preferable.





Now back to our code. It turns out that last time we did not write it, this huge bundle of code, there were still a lack of interpolators.



As for TypeEvaluators, as a rule, we have to set them manually rarely, because we often animate some simple values, or they are just some numbers, either integers or real ones, and the interpolator is not required there, the animator itself aware of all this. But, nevertheless, if some rendering of your custom view will depend on some complex objects, for example, Point or Rect, you can also use the predefined in the PointFE system or RectEvaluator in order to animate these values.



Also, a custom TypeEvaluator can be used for some atypical change of primitives. Like when we animate a color.



Let's remember at all what color is. This is just a regular integer, which by 2 bytes sets the color of each channel: alpha, red, green and blue. If we animate it as a normal int, we’ll get a beautiful transition from one color to another, but a traffic light or some kind of rainbow.



It is also acceptable to create your own TypeEvaluators, no one can interfere with this.





Let's look at an example implementation of a typical TypeEvaluator, for example, ArgbEvaluator. It exists in the system, so we do not need to implement it, but it can serve as an excellent illustration.



The TypeEvaluator interface is as follows. There is only one Evaluate method, as a parameter, the fraction is passed to it - this is the interpolated animation time. Both starting and ending values.



What do we need to do? We need to get the color of each channel from the starting and ending values. We do this from the starting one, this from the final one.





And then based on the fraction, which will have a value from 0 to 1, find the value, what the color will look like in the current value for the animation. That's the way to calculate it.



Let's go to ObjectAnimator. And here you can ask me this question: why did we even consider how ValueAnimator works, if I said that it is almost never used in client development? Yes, the fact is that ObjectAnimator is a direct extension of ValueAnimator, and everything that I just said about ValueAnimatorbet is true for it - for ObjectAnimator, except that we don’t need to use UpdateListener. Instead, ObjectAnimator presents a new concept of property, which encapsulates some variable parameters during the animation. We now understand what it is.





Let's look at the code again, what CrossFade will look like with ObjectAnimators. AnimatorSet is saved here, but ObjectAnimators appear already inside. And let's see what parameters ObjectAnimator should be created with. As the first parameter, an appropriately animated object is passed, and as the second parameter, the property that we need to animate will be passed. And further variable values.



Also remains entering the duration of the animation.





And - the same Listener, we will not get anywhere from it, which inflates all the code to us.





And now let's see how Property looks inside. You do not need to implement the Property Alpha manually, because it is in the system, but from it you can perfectly see what it is and what is contained in this Property in general.



As you can see, for Property, we need to override two methods in order for it to work correctly with us. Accordingly, the setValue method, which shows how we should mesh all the new values ​​that typeevaluator calculates for us. Thus they will be set in our animated object. And the Get method is when the animator needs to know the current value of the animation for this property. The Get method can be used when the animator wants to get the initial value of the animation.



Let's now determine how ValueAnimator differs from ObjectAnimator.



We have already considered with you that the value that we get in the end for ValueAnimator, it is found in Animator and transferred to UpdateListener. Just ObjectAnimator its API saves us from this, he himself, using Property, messes all the necessary values.



Great, we have a plus - we have less code.



Now let's see how we can set the Property data in which ways.



First of all, with the help of the successor Property, as it was in the example of our code. But we can also set this with a string. For example, if we have the alpha string set as an animated property, the animator will expect that in our View object such methods as setAlpha and getAlpha are set in the process of animation. If such methods are absent in the object, respectively, we will get a crash.



It is also worth understanding that when we animate some of our views with the help of a line, we enter our view with the help of Reflection, and this also additionally slows down the process of working and changing our object in our animation.



So, now we will look, what Property by default already is in system which we can use. This is Alpha (transparency), these are parameters related to the positioning of the view on the screen, these are parameters related to the screen rotation and scaling. And, of course, we can create the Property we want, and use them to animate our view.



Go ahead. The next step is ViewPorpertyAnimator, it also works on the basis of ValueAnimator, and its main advantage is that it provides a very convenient API for animating some views with simple property. It may even be slightly faster than ObjectAnimator when we have several values ​​animated. But this is also its minus - simplicity. We cannot make any custom attributes that would be animated using ViewPorpertyAnimator. There are only the simplest ones, the same Alpha, positioning, scale and rotation.





Let's take a look at how the crossfade method will look like with a ViewPorpertyAnimator. By the way, this is the only crossfade code that got into one slide, which is good for that.



I think everything is clear here. The Animate method that is called on the view returns a ViewPorpertyAnimator. Then we insist on it, call the Alpha method, show the final value that this view should have, specify the duration of the animation, specify the interpolator. And here you can notice another method withEndAction, which determines what actions we have to occur after the animation is executed.



So, we have already considered quite a rather large number of ways with which we can view some of our views. Let's determine when and which one we should use at all.



With ViewAnimation and DrawableAnimation, everything should be clear. ViewAnimation should never be used; DrawableAnimation is only for very complex cases, if we can’t actually do the animation using other methods. We say to our designer: “And we started recording frame-by-frame animation,” the designer tries, and we use it. But the most interesting, we consider in detail.



ValueAnimator in client development, probably, should be used only in those cases when we fail to write Property. For example, we cannot write Property to an animated object due to the fact that it does not have a getter, but we know for sure that it will not be called, we are animating, starting from some initial value to the final one, and we can simply define DateListener, and distribute a new value, which is obtained in the process of animation. Then you can use ValueAnimator.



In all other cases, and for very complex or complex animations, when we have many animated properties or even animated objects, we must use ObjectAnimator. In this case, most likely, for complex animations, you will have it used inside the AnimatorSet.



And for some completely simple animations, you can use the ViewPropertyAnimator.



How to deal with those situations when we need to complete the animation ahead of time?



Beginner developers often have this error. They start some kind of animation using the ViewPropertyAnimator, and then, when they want to cancel it, call the clearAnimation method. This method will not work, because clearAnimation refers only to canceling the animation that we set using ViewAnimation, and since we have agreed that you are not using ViewAnimation, you will never need it.



If you started your animation using ViewPropertyAnimator, then you need to cancel it. We also first call the Animate method on your view, and then call the cancel method.



If you want to cancel an animation that was started using Value or ObjectAnimator, you need to store a link to it, and then call cancel at the right time.



When can we need to cancel the current animation that is currently taking place? First of all, the user let you know with some actions that he cancels the current action for which you started the animation. Then you can cancel it and return the view to what initial state. But there is another situation.



For example, the user decided to close this activity altogether, leave this screen, and if Animator remains to work with us, then we can simply lose context, which is not always very good. Therefore, even if it seems to you that this time is a ride, it is important to store a reference to Value or ObjectAnimator, it should upset you: you should almost always keep the reference to Animator, and in the onStop methods, if this is activity or fragment, or in the onDetachFromWindow method if we are talking about view, we need to cancel the current animation.



view , Measure/Layout, view . , . - , Android 16 2/3 , ? , , layout pass, Measure Layout, , , . , , Measure Layout, , , , . , , , , , .



, , . , Measure Layout — , , , .



? . view ObjectAnimators, Top Bottom, , set Top Bottom , . , , , Layout, , , , , , , .



. , Measure Layout — , - , , . , — Measure/Layout, — , - . , - , , , Measure/Layout. - , . , .



, LayoutTransition, animateLayoutChanges, . ValueAnimator. ? LayoutTransition - ViewGroup, childs ViewGroup, , , . LayoutTransition , , .



LayoutTransition, - , , , LinearLayout, animateLayoutChanges, true, , , , , . . , . layout , LayoutTransition - .



, . . , . - , -, , , -, , .



. ?





LayoutTransition, , LayoutTransition Changing. , childs layout, LayoutTransition, , . LinearLayout, LinearLayouts . , LinearLayouts, , .



, , , . , , , . , layout, , ? LayoutTransition LinearLayout .





. ?



. , - . . - , - . , . , . Why is this happening? Let's see.



LayoutTransition - , , childs, , LayoutTransition, , , . , , — setAnimateParentHierarchy, true.



, LinearLayout, , , , .



LinearLayouts, , AnimateParentHierarchy true, view LayoutTransition, , . , , LayoutTransition, , delay, , setAnimateParentHierarchy false, .





. layout. - , .



, LayoutTransition . Appearing Disappearing — view .



Changeappearing Changedisappearing — transitions, . parent childs parent.



Changing, , view.



, , LayoutTransition , - . , , ViewGroup LayoutTransition, , . — .



. , : , view, . There is nothing else. , ImageView , .



, view . , , , , . , view, , view, .



Android -, - , , Transition Framework.



Transition Framework , LayoutTransition. , , Transition, , , . childs, . , LayoutTransition . . Transition Framework . , Android 4.4. transitions Android. Android 5.



, , .





, view, , .



? TransitionSet, transition. ChangeBounds — Transition, layout, layout .



Fade, Fade.



TransitionSet — AnimatorSet, Transition.



beginDelayedTransition, Transition. , , , , Transition, , . , .



, , . LayoutTransition ViewGroup, .



, BeginDelayedTransition, view, layout, .



transitions ? There are a lot of them. ChangeBounds Fade . Android 5 , . , - Transition, , .



, Transition — , , . , . , Transition , .



— Slide. , Fade, - . , -, view , - , , . Transition Slide.



, , , . . Slide, TransitionChangeBounds.



Transition, Explode. , - . , , - . , Explode, , , , . API.



ChangeTransform — Transition, view.



ImageView. Transition, ChangeImageTransform. , preview- SkillType ImageView, , , , Transition ImageView.



ChangeImageTransform, ImageView ChangeBounds.



, , , , . , , excludeTarget, ID, , . ID, true.



, , . , , , , , - , ID, view. - , : «, - , , view», excludeTarget, false. ID, view. excludeTarget, excludeChildren.



— , view , , : , view root. addTarget, Transition, view .



ID, view. -, - , removeTarget.





Transition Framework , Transitions, internet- XML. , , Transition. TransitionInflater Java- XML Transition. Transition.





Transition Framework . , layout.



? XML view ID, , Transition Framework , view , . go, layout .



ID - , TransitionName, , , Android 5, Transition Framework view, ID.



Transition Framework Android. , , Support Library, . Fade ChangeBounds Android 4.4, 4.0 4.3. . . , , , , , , , , , , Overview Support Library. , , , , , , .



, - layout : LayoutTransition Transition Framework? LayoutTransition , , - , , , 4.0, . Transition Framework, , , - Android.



, . , , , - , , , . , , , , - .







, . , , , , , . , , , .



, . , - , . view . rocket science . , , , ValueAnimator.



ValueAnimator, ? , , , , , , . , . - , .



Android , Dynamic animation, Physic-based animation. , , , .



, , , , FlingAnimation SpringAnimation. FlingAnimation , - , , , , , . SpringAnimation — - .



, , Dynamic animation .



, , , ValueAnimator ObjectAnimator. , , .



Dynamic animation .



: « ? ?». Everything is very simple. , , . .



Dynamic animation . — , .



FlingAnimation «», , . SpringAnimation , , , , , . «».



, , .



, , FlingAnimation. Out! , . . Fine!





, .



, , touch-. , view, Listener RecyclerView, onTouchListener ( , ), RecyclerView Listener, .



velocityTracker — Android, , — , , , , ActionUp, ActionCancel, - view , , , . velocityTracker, computeCurrentvelocity, FlingAnimation, , velocityTracker, , , view , , .



EndListener — , .



, , , onStop.



, , - , Start.



, view? view, - . , , view .



, Dynamic animation.



, Fling. , SpringAnimation, view . , .



, , , , .



, . ( GitHub: bit.ly/2uQPiSY .)





FlingAnimation EndListener , , , , SpringAnimation, SpringForce, , , , , .



, , , , , , - Dynamic Animation.



Dynamic animation. FlingAnimation , , , SpringAnimation, , , - .



, - .



, — , , . . : « ?», , - .



-, , , Transition Framework, ObjectAnimation, ViewPropertyAnimator, Dynamic animation.



, : Android 4.3 . , . . - - . , . .



And the last. , . , . Developer Settings, . , 5x — , . — , , - .



, , - , Android. ItemAnimator, RecyclerView. . ItemAnimator — , , .



ItemAnimator, , , DefaultItemAnimator, RecyclerView. .



AnimatedVectorDrawable, . Android, compatibility- Support Library. - , Android 4, . StackOverflow , AnimatedVectorDrawableCompat .



Activity Fragment Transition — activity . Transition Framework, API .



, . . , . .



: , Telegram

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



All Articles