This article has been published exclusively by guolin_blog, an official wechat account

Recently off work time are used to fitness and reading, the blog was drying for a period of time, forgive me ~~~~

For questions

Ok, without further ado, we have analyzed the operation principle of View Animation before, so this time we will learn the operation principle of property Animation.

Q1: We know that the Animation actually listens internally for the next screen refresh via ViewRootImpl, and when it receives the signal, executes the view-bound Animation as it moves through the View tree starting from the DecorView. Is this how property animators work? If not, how is it implemented?

Q2: What distinguishes Animator from Animation is that it modifies the properties of View. Then how is it implemented and how is the principle?

Q3: Property Animator is calledstart()What did you do after that? When do you start animating the current frame? What are the internal calculations?

basis

ObjectAnimator class ValueAnimator class ValueAnimator class ObjectAnimator There is also a view.animate () principle, which is also a property animation, and it is convenient to use the commonly used animation package, but there is a hole, we will save for the next article, we will focus on the property animation principle.

Take a look at the basic steps:

ValueAnimator animator = valueanimator.ofint (500); animator.setDuration(1000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (int) animation.getAnimatedValue(); mView.setX(value); }}); animator.start(); ObjectAnimator animator = ObjectAnimator. OfInt (mView, ObjectAnimator)"X", 500).setDuration(1000).start();
Copy the code

So you can see a pan animation that executes for 1s, and then it’s time to start following the source code. We need to sort out when the property animation starts, how it executes, where it really takes effect, how it lasts for 1s, and how it calculates internally.

Now that we’ve looked at Animation working principles, we’ve also looked at the Android screen refresh mechanism. Now we know that the key to Android screen refresh is the Choreographer class.

As we know, Android will refresh the screen every 16.6ms, that is, the bottom layer will send a screen refresh signal every 16.6ms. When our app receives this screen refresh signal, it will calculate screen data, which is often referred to as measurement, layout and drawing. The key point in the whole process is that the APP needs to register with the bottom layer to listen to the next screen refresh signal event, so that when the bottom layer sends the refresh signal, it can find the upper app and call back its method to notify the arrival of the event, and then the APP can continue to do the work like computing screen data.

Choreographer is responsible for registering listeners and providing callback interfaces for low-level calls. Animation is performed using the scheduleTraversals() method of the current View tree’s ViewRootImpl. The internal logic of this method will go to Choreographer to complete the work of registering to listen for the next screen refresh signal and receiving the event.

If you need to interact with screen refresh signals, it will ultimately be through the Choreographer class.

So, when we go through the flow source of the Animator we have an initial goal, at least we know that we need to track down Choreographer before we can stop. As for the process principle of property Animation, it is realized through ViewRootImpl like Animation process. Or some other way? These are the source code we need to comb out, then the following began the source code.

The source code parsing

Ps: This analysis of the source code based on android-25 version, version is not the same, the source code may be some differences, we pay attention to their own time.

Animation source code starting point should be very simple, follow start() step by step to trace down to comb it out.

We know that ObjectAnimator is derived from ValueAnimator, so we can start from start() of ValueAnimator. Just look back and see what more ObjectAnimator’s start() does:

start(boolean)

startAnimation()

initAnimation()
notifyStartListeners()

Emmm, we’ve been following the animation from start() all the way down, and so far we’ve just done some initialization of the animation, and it’s obvious that we’re at the end of the line, there’s no more code, so where exactly is the next step after the animation initialization? Remember we skipped some methods in the start(Boolean) method earlier? Maybe the key is here, so go back and look:

startAnimation()
addAnimationFrameCallback()

setStartDelay()

MAnimationCallbacks is an ArrayList. Each item holds objects of the AnimationFrameCallback interface. This is a callback interface. If ValueAnimator’s implementation of this interface is called back, it will be called back. If ValueAnimator’s implementation of this interface is called back, it will be called back.

When the mAnimationCallbacks list size is 0, a method will be called. Obviously, if the animation is executed for the first time, the list size should be 0, because adding the callback object to the list is done after this judgment. So here’s where we can follow up:

Wow, seeing Choreographer so soon, it feels like we’re getting close to the truth, keep going:

So the inside is actually invoked the postCallbackDelayedInternal () method, if you have read I write a blog on the Android screen refresh mechanism, here is already almost can clarify, have time can go back to see, I give some conclusion here synoptically.

Choreographer has several queues inside, and the first parameter of the above method, CALLBACK_ANIMATION, is used to distinguish between these queues, and each queue can hold FrameCallback objects as well as Runnable objects. Animation essentially generates a doTraversal() Runnable object via ViewRootImpl and stores it in Choreographer’s queue. The work in these queues is taken out for execution when a screen refresh signal is received. The key point, however, is that for Choreographer to be able to receive screen refresh events, Choreographer’s scheduleVsyncLocked() method needs to be called first to register with the underlying layer to listen for the next screen refresh event.

And if you continue to track postCallbackDelayedInternal () this way, you will find that it is eventually reached the scheduleVsyncLocked (), these in a blog on has combed the Android screen refresh mechanism, I won’t go into the details here.

So, here, we can first sort out the current information:

When ValueAnimator calls the start() method, it initializes some variables and notifies the animation that it has started. ValueAnimator then implements the AnimationFrameCallback interface, The AnimationHander caches this as a parameter to the mAnimationCallbacks list. The AnimationHandler will cache an mFrameCallback work to Choreographer through the inner class MyFrameCallbackProvider when the mAnimationCallbacks list size is 0 And registers with the underlying layer to listen for the next screen refresh event.

When the screen refresh signal arrives, Choreographer doFrame() will take the work out of the queue and execute it, calling back the AnimationHandler’s mFrameCallback work.

So far, we have been able to determine that the first time an animation calls start() should be the first time an animation calls start() in any of the property animations in the project, since AnimationHandler is a singleton class that obviously serves all property animations. If start() is called for the first time, an event is registered with the underlying layer to listen for the next screen refresh signal. So the processing logic of the animation should be called indirectly in the mFrameCallback job after receiving the screen refresh signal.

So let’s see what mFrameCallback does when it receives the screen refresh signal:



doAnimationFrame()

Moving on to the second thing, we know that animation is a continuous process, that is, each frame should process one animation progress until the animation is finished. In this case, we need to receive every screen refresh until the end of the animation, so we need to register the bottom layer to listen for the next screen refresh event in each frame. So what you can see is that in the code above, the argument is this, which is mFrameCallback itself, and if you combine that with the previous process, what you get here is:

When the first attribute animation calls the start (), because mAnimationCallbacks list the size of 0, so directly by addAnimationFrameCallback () method of the internal indirect registered the next screen refresh signal with the underlying event, Then add the animation to the list. When a screen refresh is received, mFrameCallback’s doFrame() is called back. This method does two things internally, one is to process the animation of the current frame, and the other is to register with the underlying layer to listen for the next screen refresh event based on whether the list size is not zero. Until the list size is 0.

So, it’s safe to assume that if the current animation ends, it needs to be removed from the mAnimationCallbacks list, which can be verified later in the source code process. So, let’s follow doAnimationFrame() to see how the property animation works:

One is to loop through the list, fetch each ValueAnimator, and determine if the animation has a delay start or if it is time to execute. If it is, doAnimationFrame() of ValueAnimator will be called.

The cleanUpList() method is called to clean up the list of animations that have already been finished. Since the AnimationHandler is used for all attribute animations, there may be multiple animations in progress at the same time, so animations must be finished in different order. Finished animations must be removed from the list, so that when all animations are finished and the list size is 0, mFrameCallback can stop registering with the underlying layer to listen for the next screen refresh event and the AnimationHandler can go idle. You don’t have to animate every frame anymore.

So, let’s take a look at cleanUpList() first, because it feels easy to work with, so I’ll comb it out first:

Now that the cleanup is done, it’s time to continue with the animation process. Remember we mentioned above that the other thing is to iterate through the list to call doAnimationFrame() for each animation ValueAnimator to handle the animation logic, so let’s follow this method:

To recap, this method does three things internally: first, it handles some of the work of the first frame of the animation;

The second is to calculate the Animation progress of the current frame according to the current time, so the core of the Animation should be in the method animateBaseOnTime(), the meaning is similar to the getTransformation() method of Animation;

When the animation is finished, call endAnimation(), which removes the current animation from the mAniamtionCallbacks list.

Let’s take a look at the work after the animation is over, because the above part has just been combed, while you still have some impression, and this part of the work is easier to understand, first eat the simple:

Remove All back() with AniamtionHandler ().

Ok, that’s a little bit, so let’s move on to the next two things, the first one, working on the first frame of the animation:

Referring to the principle of Animation, the work of the first frame is usually to record the time stamp of the first frame, because each subsequent frame needs to calculate the progress of the Animation in the current frame according to the current time, the time of the first frame and the duration of the Animation. We have combed the Animation. So this should be a little bit more logical as we go through the logic on frame 1. Let’s see if the first frame of a property Animation works like an Animation:

Emmm, which seems much more complex than Animation, basically does two things:

The AnimationHandler addOneShotCommitCallback() method is called.

The second is to record the time of the first frame of the animation. The mStartTime variable is the timestamp of the first frame, and the subsequent animation progress calculation must use this variable. And then there’s the mSeekFraction variable, which is kind of like when you’re watching a video on your computer, you can choose to start at some point. The property animation provides an interface called setCurrentPlayTime().

ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setDuration(4000);
animator.start();
Copy the code

For example, If we call start(), then mSeekFraction defaults to -1, so mStartTime is the current time for the first frame of the animation. If we call setCurrentPlayTime(2000), which means that we want the animation to start at 2s, then it’s an animation that lasts for 2s(4-2) from 50 to 100 (assuming the interpolator is linear), so at this point, MStartTime is used as the first frame of the animation 2s earlier than the current time. If the animation progress is calculated according to mStartTime, it will be found that the original animation has passed 2s.

It’s like when we watch TV, we don’t want to watch the opening credits, so we choose to start with the main film, similar.

Ok, remember we said that processing the first frame of an animation is basically two things, and the other thing is calling a method. Let’s go back and see what’s going on here:

Just add ValueAnimator to another list in the AnimationHandler. You can filter the list of variable names to see where it is used:

callback.doAnimationFrame(frameTime)
doAnimationFrame()

ValueAnimator implements the AnimationFrameCallback interface, which essentially calls back the ValueAnimator method and removes it from the queue. See what ValueAnimator’s implementation does:

Okay, so this is actually fixing the first frame of the animation, mStartTime. So, in fact, the two things in doAnimationFrame() of ValueAnimator that do the first frame work are all used to calculate the time of the first frame of the animation, except that one is calculated based on whether “skip the header “(setCurrentPlayTime()), The other thing is the correction here.

So why fix the first frame time mStartTime here?

CommitAnimationFrame () : AnimationFrameCallback commitAnimationFrame() : AnimationFrameCallback commitAnimationFrame() :

In fact, this has to do with how property animations are implemented through Choreographer. As we know, Choreographer is responsible for the screen refresh signal events and there are several queues inside Choreographer that store work that can be taken out and processed when a signal is received. So, what’s the difference between these queues?

In order of execution, we assume that these queues are named 1 > 2 > 3. The mFrameCallback work encapsulated by AnimationHandler is put into queue 1. The Animation whose doTraversal() is encapsulated by ViewRootImpl is placed in queue 2; The Runnable work that just finished correcting the first frame of the animation was put into the 3 queue.

That is, when a screen refresh signal is received, the property animation is processed first. The next step is to calculate the current screen data, which is measurement, layout, and drawing. One problem with this is that if the page is too complex and it takes too long to draw the current interface, when the next screen refresh signal, the property animation will calculate the animation progress based on the previously recorded timestamp of the first frame, and you will find that the first several frames are missing, obviously the animation has not been executed. So, that’s why we need to fix the first frame of the animation.

Of course, if the animation has already started, at some point in the middle of the animation, it’s not going to fix it, it’s going to fix it for the first frame of the animation. If the first frame is too time-consuming to draw and the first few frames are lost, we can avoid losing frames by delaying the start of the animation. However, if drawing the interface is too time-consuming during animation execution, no strategy can prevent frame loss.


Summary 1:

Let’s go through all the information so far, or I’m sure you’ve forgotten what it says:

  1. The ValueAnimator property animation is calledstart()After that, some initialization work is done, including initialization of variables, notification of animation start events;

  2. The AnimationHandller is a singleton class that serves all property animations. The list holds all property animations that are in process or ready to start.

  3. If there are currently animations to run, then The AnimationHandler will go through Choreographer to register with the underlying layer to listen for the next screen refresh signal, and when it receives the signal, its mFrameCallback will start working. This consists of walking through the list to process the behavior of each property animation individually in the current frame. After all animations in the list have been processed, if the list is not zero, it will go back through Choreographer to register for the next screen refresh signal event, and so on until all animations have finished.

  4. AnimationHandler traverses the list of animations that are processed indoAnimationFrame()The specific processing logic for each animation is in its own, that is, ValueAnimatordoAnimationFrame()The AnimationHandler will remove all null references from the list to clean up the resource. The AnimationHandler will remove all null references from the list to clean up the resource.

  5. Each animation ValueAnimator handles its own animation behavior. First, if it is currently the first frame of the animation, it will be based on whether there is a “skip to the beginning” (setCurrentPlayTime()) to record what mStartTime should be for the first frame of the current animation.

  6. In fact, the animation of the first frame is just the recording time of mStartTime and initialization of some variables. The animation progress is still 0, so the next frame is the key to start the animation. However, since the processing of the attribute animation is before the drawing of the interface, it may be due to the drawing time, As a result, the interval between the time of the first frame recorded by mStartTime and the time of the second frame is too long, resulting in the loss of the first several frames. Therefore, if this is the case, mStartTime will be corrected.

  7. The specific method of correction is when the drawing work is completed, at this time, according to the current time and the time recorded by mStartTime to do comparison, and then the correction.

  8. If it is in the animation process of a certain frame before the phenomenon of drawing time, then, can only mean that powerless, lost frame is unavoidable, want to solve the problem you have to analyze why the drawing time; If the drawing time occurs in the first frame, then the system can still help fix it by correcting the mStartTime to avoid frame loss.

Ok, the rest is over, we continue, there is still a way to go, in fact, the whole process is generally out, but the lack of the animation progress of the current frame specific calculation and implementation details, this part of the estimate will be more head.

In the previous analysis of ValueAnimator’s doAnimationFrame(), we summarized it as doing three things: first, processing the first frame of the animation; Second, according to the current time calculation and achieve the animation work of that year frame; Third, according to whether the animation is over to clean up some resources; AnimateBasedOnTime () : animateBasedOnTime()

From here, the Animation logic of the current frame is calculated. The whole process is basically the same as Animation. In the above code, I omitted part, that part is used to process whether the animation needs to restart after the end according to whether the mRepeatCount is set, we will not look at these, we will focus on sorting out a normal process.

So, to summarize, there are really three things that this method does:

One is to calculate the current animation progress according to the current time, the first frame of the animation and the duration of the animation.

The second is to make sure that the animation progress is between 0 and 1. There are two methods called to assist in the calculation, which we won’t follow. The reason there are so many auxiliary calculations is that the property animation supports setRepeatCount() to set the number of cycles of the animation. The time of the first frame of the animation from the beginning to the end is always mStrtTime, so when calculating the animation progress according to the current time in the first step, the progress value may exceed 1, such as 1.5, 2.5, 3.5, etc., so the auxiliary calculation of the second step, I’m just going to take these values and convert them to something between 0 and 1.

The animateValue() method is similar to applyTransformation() in Animation. This method can be applied to the Animation effect after the Animation progress is calculated.

AnimateValue () : animateValue() : animateValue() : animateValue() : animateValue() : animateValue()

The work here is roughly divided into three things:

The first is to calculate the actual animation progress according to the interpolator. Interpolator is a relatively important concept in animation, and may be rarely used. If we do not specify which interpolator to use, then the system will usually have a default interpolator.

The other is to map the actual animation progress value calculated by the interpolator to the value we need. Let’s just say that even after the interpolator calculation, the animation progress value is somewhere in the 0-1 range. Usually we don’t need a 0-1 number, for example if we want a 0-500 change, then we need to convert ourselves after we get the 0-1 progress value. The second step, which basically helps us with the work, is to tell ValueAnimator that we need a change of 0-500, and it will convert when it gets the progress value.

Three is just the notification of the animation’s progress callback.

The process is almost sorted out, but I am personally interested in how the internal process is converted to the value of our specified interval based on the progress value of the 0-1 interval, so let’s analyze it a little further. MValues [I]. CalculateValue (fraction). MValues is an array of type PropertyValuesHolder. So the key is to see what the calculateValue() of this class does:

When we use ValueAnimator, we register the animation progress callback, and then fetch the current value of the mAnimatedValue variable in the callback, which is calculated using mKeyframes. GetValue (). So follow up:

KeyFrames is an interface, so the next step is to find out where this interface is implemented:

To do this, filter mKeyframes = in the PropertyValuesHolder class with Ctrl + F to see where it is instantiated. KeyframeSet ofXXX method to instantiate the object, so the specific implementation should be in the KeyframeSet class.

One thing I want to mention before I follow along, if you notice, is that the places where mKeyframes are instantiated, ofInt(), onFloat(), etc., are familiar. Valueanimator.ofint () internally creates the mKeyframes according to the corresponding method. That is, the mKeyframes are also instantiated when the property is instantiated. Want to confirm, everyone can go with their own source code to see, I don’t stick here.

Ok, let’s look at the ofInt() method of KeyframeSet and see what’s created inside: KeyframeSet

There’s a new mechanism involved here, Keyframe, KeyframeSet, Keyframes, you can look it up if you’re interested, but I don’t know much about it. But read some of the introduction of others, here is about it. In direct translation, this means keyframes, just as a movie is made up of frames, so an animation is made up of frames.

Remember why we’re following you here. When the animation processes the work of the current frame, it will calculate the animation progress of the current frame, and then map it to the value we need according to the progress of the 0-1 interval, and the value after the mapping is obtained from getValue() of mKeyframes. MKeyframes is a KeyframeSet object that is also created when we create the property animation, and when we create the property animation, we pass in a value that we want, such as ValueAnimator.ofint (100) which means that we want the animation to vary from 0 to 100, The 100 will also be passed internally to keyFrameset.ofint (100), which will then go into the creation of the code block above.

In this method, 100 is a keyframe. So what is a keyframe for an animation? Obviously, at the very least, animation needs to know where to start and where to end, right? So, for an animation, we need at least two keyframes, and if we call ofInt(100) and only pass in one value, internally it will assume that the start is 0, and the 100 that comes in is the end keyframe, so internally it will create two keyframes.

So, how are these keyframes used in animation? This is going back to the mKeyframes. GetValue () method that we originally traced. Look at the code block above, keyFrameset.ofint () and finally create an IntKeyframeSet object, So let’s follow the class’s getValue() method to see how it uses these keyframes:

GetIntValue () is the value of the current frame’s animation progress calculated by the interpolator, in the 0-1 interval. GetIntValue () is a bit too much code, so let’s look at the first one:

Valueanimator.ofint (100), which is often used when there are only two keyframes, internally creates only two keyframes, one for the start point 0 and one for the end point 100. Then, in the case of only two frames, it is very simple to convert the 0-1 animation progress value into the value within the 0-100 range we need. If no valuator is set, that is, mEvaluator, then the conversion is directly proportional, for example, the progress is 0.5. So the scaling is (100-0) * 0.5 = 50. If there is an estimator set, it should follow the rules set by the estimator. The estimator is actually similar to the interpolator, which is only introduced in the property Animation, but not in the Animation, because the value conversion is only done inside the property Animation.

The above is the processing logic when the keyframe is only two frames, but when the keyframe is more than two frames:

So, when do keyframes go beyond two? Valueanimator. ofInt(0, 100, 0, -100, 0); the number of keyframes is determined by the number of arguments.

So, let’s look at the processing logic of the three cases in detail, the first is the processing logic of the first frame:

Fraction <= 0f should represent more than the meaning of the first frame, but I don’t know what other scenes are except for the first frame. For the time being, this should not have a big impact on the understanding of the first frame.

The logic is pretty simple, remember how it works when there are only two keyframes. So when dealing with the first frame, we only need to treat the second frame as the last frame, so the first frame and the second frame can also be regarded as the scene with only two frames. However, the parameter fraction animation progress is calculated from the actual first frame to the last frame, so it needs to be converted first to calculate its progress between the first frame and the second frame, and then the logic is the same as that for the two frames.

Similarly, when processing the last frame, you only need to take out the information of the penultimate frame and penultimate frame, and then convert the progress to the progress between these two pins, and the processing logic will be the same. I won’t post the code.

But the logic for intermediate frames is different, because depending on the 0-1 animation progress, we can easily distinguish whether we are in the first frame or the last frame, either one is 0, the other is 1. However, when the animation progress value between 0 and 1, we don’t have any way to see this progress value is directly fall in the middle of which between two key frames, if there is a way to calculate the current animation progress between which two key frames, then it follows that the logic is the same, so the key is to find out the current progress in which between two key frames:

The system search method is also very simple, starting from the first frame, traverse each frame in sequence, and then judge the current animation progress and the position information saved in this frame to find out whether the current progress is falling between two key frames. In addition to its corresponding value, each key frame saves information about its position between the first frame and the last frame. As for the value of this position, it is controlled by the creation of this series of key frames.

If you remember where you created this set of keyframes, go back to KeyframeSet’s ofInt() :

When you create each keyframe, you pass in two parameters. The first parameter indicates the location of the keyframe between the entire area, and the second parameter indicates what value it represents. Look at the code above, I represents the number of frames, numKeyframes represents the total number of key frames, so I /(numKeyframes – 1) means that the series of key frames are allocated in equal proportions.

For example, valueAnimator.ofint (0, 50, 100, 200), there are four keyframes in total, so the first frame is at the starting position 0, the second frame is at 1/3, the third frame is at 2/3, and the last frame is at 1.


Summary 2:

Here, we will sort out the content of the following part:

  1. Upon receiving the screen refresh signal, the AnimationHandler iterates through the list, pulling out all the property animations to be performed to calculate the animation behavior of the current frame.

  2. When each animation processes the animation logic of the current frame, it will initially calculate the progress of the animation in the current frame according to the current time, the time of the first frame of the animation and the duration of the animation, and then convert the progress value to the range of 0-1.

  3. Then, the interpolator will use this preliminary calculated progress value to calculate the actual animation progress value according to the set rules, and the value is also in the range of 0-1.

  4. Valueanimator. ofInt(0, 100) indicates that the value we need varies from 0 to 100, so the interpolator calculates the progress value between 0 and 1. The next step is to map between 0 and 100 using the keyframe mechanism.

  5. The number of keyframes is determined by the value of valueAnimator.ofint (0, 1, 2, 3). For example, there are four keyframes in this case. The first and last frames are required, so there are at least two keyframes. When the ofInt() method is called, the keyframe group is created.

  6. When there are only two key frames, the mapping rule is that if no estimator is set, then equal scale mapping is used. For example, if the animation progress is 0.5 and the required value range is 0-100, then the value after equal scale mapping is 50. So we in the callback onAnimationUpdate through animation. GetAnimatedValue () this is the reason for access to the value of the 50.

  7. If a valuer is set, the mapping is done according to the valuer’s rules.

  8. When key frames for more than two, need to find the current animation progress which is behind the first between two key frames, and then the progress values mapped to the first value between these two key frames, then the two key frames can be seen as the first frame and the last frame, can be in accordance with the only two key frames of mapping rules for calculation.

  9. The progress value maps to the value between two key frames, which requires knowing the position information, or weight, of each key frame in the whole key frame group. This location information is passed in with each keyframe created. The rule of onInt() is that all keyframes are weighted equally. For example, if there are three keyframes, the first frame is 0, the second frame is 0.5, and the last frame is 1.

So far, we have sorted out the entire process, and the two parts of the summary are the whole process of sorting out the property animation from the start(), to the onAnimationUpdate callback where we get the value we need, to the whole process of cleaning up the resources after the animation.

How does a property animation receive a screen refresh event? How do I repeatedly receive the screen refresh signal event until the entire animation is finished? Is the mode different from Animation? What does the animation effort to calculate the current frame involve? How does the 0-1 animation progress map to the values we need?

If you have all these questions in mind by the end of this passage, you have absorbed the main points of this passage. Of course, if there are mistakes, welcome to point out, after all, there is a lot of content, it is very likely that there are mistakes not found.

Let’s draw the end of the chronology diagram:

Finally, one thing I want to mention is that we’ve just gone through the whole process principle of ValueAnimator, but notice that we haven’t seen any UI manipulation anywhere. As we learned in our last blog ***Android screen refresh mechanism ***, the drawing of the interface is actually initiated by the ViewRootImpl, but ValueAnimator and ViewRootImpl clearly have no overlap.

How does ValueAnimator animate? In fact, ValueAnimator simply outputs a value per frame through a series of computations, transformations, etc., following our set interval (ofInt(0, 100)), duration (setDuration(1000)), interpolator rules, and estimator rules. If we want to animate an effect, we can only take the output value from the progress callback interface and apply it manually to a View (mview.setx ()). So, this is still essentially the View’s internal method that eventually goes to the View wrotimPL to trigger the update drawing of the interface.

ObjectAnimator, on the other hand, has internal operations that involve the UI. How this works will be discussed later.

legacy

It is said that property animation is achieved by changing the value of the property. When I planned to write this article, I thought I could clarify this point. However, it would be too long to just clarify the principle of ValueAnimator process, so ObjectAnimator will be explained at another time. This problem is left as a legacy issue.

Q1: It is said that attribute animation is achieved by changing attribute values. How does it work?