Android property animation full parsing (medium), ValueAnimator and ObjectAnimator advanced usage, androidanimator
Reprinted please indicate the source: http://blog.csdn.net/guolin_blog/article/details/43536355
Hello everyone, in the previous article, we learned the basic usage of Android property animation. Of course, it is also the most common usage, which is enough to cover our animation needs in most situations. However, as mentioned in the previous article, property animation has greatly improved the animation population. Previously, property animation can also be achieved through property animation, the property animation that the animation cannot do now can also be done. So today, let's take a look at the advanced usage of property animation to see how to implement some functions that cannot be implemented by the animation population.
Reading this article requires you to have a certain understanding of property animation and master the basic usage of property animation. If you do not know enough about property animation, we recommend that you read it first.Complete parsing of Android property Animations (I): basic usage of property animations.
ValueAnimator advanced usage
As mentioned in the previous article about the shortcomings of the compensation animation, the animation operation can only be performed on the View object. Attribute animation is no longer subject to this restriction. It can perform animation operations on any object. So you should remember the example I gave in the previous article. For example, we have a custom View, in which a Point object is used to manage coordinates, in the onDraw () method, the Point object is drawn based on the coordinate value of the Point object. That is to say, if we can perform animation operations on the Point object, the animation effect of the entire custom View will be available. OK. Next we will learn how to achieve this effect.
Before getting started, we also need to master another knowledge point, that is, the usage of TypeEvaluator. TypeEvaluator may not be used when we use Attribute animation in most cases, but you should understand its usage, to prevent us from encountering problems that cannot be solved, we can think of such a solution.
So what is the role of TypeEvaluator? Simply put, it is to tell the animation system how to transition from initial value to end value. The ValueAnimator. ofFloat () method we learned in the previous article implements the smoothing between the initial value and the end value. How can we achieve this Smoothing? In fact, the system has a built-in FloatEvaluator, which tells the animation system how to transition from the initial value to the end value through computation. Let's take a look at the code implementation of FloatEvaluator:
public class FloatEvaluator implements TypeEvaluator { public Object evaluate(float fraction, Object startValue, Object endValue) { float startFloat = ((Number) startValue).floatValue(); return startFloat + fraction * (((Number) endValue).floatValue() - startFloat); }}
As you can see, FloatEvaluator implements the TypeEvaluator interface and then overrides the evaluate () method. Three parameters are input in the evaluate () method. The first parameter fraction is very important. This parameter is used to indicate the animation completion degree, we should calculate the value of the current Animation Based on it. The second and third parameters indicate the initial and end values of the animation respectively. Then the logic of the above Code is clear. Use the end value minus the initial value to calculate the difference between them, multiply by the fraction coefficient, and then add the initial value, then the animation value is obtained.
Okay. FloatEvaluator is a built-in function of the system and does not need to be compiled by ourselves. However, the implementation method of FloatEvaluator is to pave the way for subsequent functions. Previously we used the ValueAnimator ofFloat () and ofInt () Methods to animation floating point and integer data, but in fact there is an ofObject () method in ValueAnimator, it is used to animation any object. However, compared with floating-point or integer data, the animation operation of an object is obviously more complex, because the system will not be able to know how to overwrite the object from the initial object to the end object, therefore, we need to implement a TypeEvaluator to tell the system how to overdo it.
Define a Point class as follows:
public class Point { private float x; private float y; public Point(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public float getY() { return y; }}
The Point class is very simple. Only the x and y variables are used to record the coordinates, and the constructor is provided to set the coordinates, and the get method is used to obtain the coordinates. Define PointEvaluator as follows:
public class PointEvaluator implements TypeEvaluator{ @Override public Object evaluate(float fraction, Object startValue, Object endValue) { Point startPoint = (Point) startValue; Point endPoint = (Point) endValue; float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX()); float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY()); Point point = new Point(x, y); return point; }}
As you can see, PointEvaluator implements the TypeEvaluator interface and overwrites the evaluate () method. In fact, the logic in the evaluate () method is still very simple. First, the startValue and endValue are forced into Point objects, and then the fraction is used to calculate the x and y values of the current animation, finally, it is assembled into a new Point object and returned.
In this way, we have completed the compilation of PointEvaluator. Next, we can easily perform animation operations on the Point object. For example, we have two Point objects, now we need to smooth Point1 over Point2 Through animation, so we can write it like this:
Point point1 = new Point(0, 0);Point point2 = new Point(300, 300);ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);anim.setDuration(5000);anim.start();
The code is very simple. Here we first create two new Point objects and set their coordinate points in the constructor. Then call the ofObject () method of ValueAnimator to construct the ValueAnimator instance. Note that the ofObject () method requires one more TypeEvaluator parameter, here we only need to pass in the defined PointEvaluator instance.
Okay, this is the full usage of the custom TypeEvaluator. After mastering this knowledge, we can try to animation the Point object, to achieve the animation effect of the entire custom View.
Create a new MyAnimView that inherits from the View. The Code is as follows:
public class MyAnimView extends View { public static final float RADIUS = 50f; private Point currentPoint; private Paint mPaint; public MyAnimView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); } @Override protected void onDraw(Canvas canvas) { if (currentPoint == null) { currentPoint = new Point(RADIUS, RADIUS); drawCircle(canvas); startAnimation(); } else { drawCircle(canvas); } } private void drawCircle(Canvas canvas) { float x = currentPoint.getX(); float y = currentPoint.getY(); canvas.drawCircle(x, y, RADIUS, mPaint); } private void startAnimation() { Point startPoint = new Point(RADIUS, RADIUS); Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS); ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); anim.setDuration(5000); anim.start(); }}
Basically, it is quite simple, and there are only a few lines of code in total. First, a Paint object is initialized in the Custom View constructor as the Paint brush, and the Paint color is set to blue, and then drawn in the onDraw () method. The logic we draw here is controlled by the currentPoint object. If the currentPoint object is not equal to null, call the drawCircle () method to draw a circle with a radius of 50 at the Coordinate Position of the currentPoint, if the currentPoint object is empty, call the startAnimation () method to start the animation.
Let's take a look at the code in the startAnimation () method. In fact, you should be familiar with it, that is, performing an animation operation on the Point object. Here we define a startPoint and an endPoint. The coordinates are the upper left corner and lower right corner of the View, and the animation duration is set to 5 seconds. Next, you need to pay attention to the fact that we listen to the animation process through the listener. The onAnimationUpdate () method will be called back whenever the Point value changes. In this method, the currentPoint object is re-assigned and the invalidate () method is called. In this way, the onDraw () method will be re-called, and because the coordinates of the currentPoint object have changed, the position of the painting will also change, so the animation effect of a translation will be realized.
Next we only need to introduce this custom control in the layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.tony.myapplication.MyAnimView android:layout_width="match_parent" android:layout_height="match_parent" /></RelativeLayout>
Finally, run the program, as shown in the following figure:
OK! In this way, the animation effect is successfully implemented by performing value operations on the object. This is the advanced usage of ValueAnimator.
Advanced ObjectAnimator usage
The basic usage and working principle of ObjectAnimator have been explained in the previous article. I believe everyone has mastered it. So everyone should remember that we mentioned it during the animation of the slot, animation population can only implement four types of animation operations: movement, scaling, rotation, and fade-in and fade-out. These are the limitations of the function. Basically, there is no scalability. For example, if we want to dynamically change the color of the View, there is no way to do the compensation animation.
However, attribute animation is not limited by these rules and its scalability is very strong. It is fully competent to dynamically change the color of the View, next we will learn how to achieve this effect.
Everyone should remember that the internal working mechanism of ObjectAnimator is to achieve the animation effect by searching for get and set methods for specific attributes and continuously changing values through methods. Therefore, we need to define a color attribute in MyAnimView and provide its get and set methods. Here we can set the color attribute to the string type and use the format # RRGGBB to represent the color value. The Code is as follows:
public class MyAnimView extends View {...private String color;public String getColor() {return color;}public void setColor(String color) {this.color = color;mPaint.setColor(Color.parseColor(color));invalidate();}...}
Note that in the setColor () method, we have compiled a very simple logic, which is to set the color of the paint brush to the color passed in by the method parameter, and then call the invalidate () method. Although this Code contains only three lines, it implements a very core function, that is, to refresh the view immediately after the paint color is changed, and then the onDraw () method is called. The onDraw () method draws based on the color of the current paint brush, so that the color will change dynamically.
The next question is how to call the setColor () method. There is no doubt that the ObjectAnimator class is required, but we need to do a very important job before using ObjectAnimator, is to write a TypeEvaluator that tells the system how to make excessive colors. Create a ColorEvaluator and implement the TypeEvaluator interface. The Code is as follows:
Public class ColorEvaluator implements TypeEvaluator {private int mCurrentRed =-1; private int mCurrentGreen =-1; private int mCurrentBlue =-1; @ Overridepublic Object evaluate (float fraction, Object startValue, object endValue) {String startColor = (String) startValue; String endColor = (String) endValue; int startRed = Integer. parseInt (startColor. substring (1, 3), 16); int startGreen = Integer. parseIn T (startColor. substring (3, 5), 16); int startBlue = Integer. parseInt (startColor. substring (5, 7), 16); int endRed = Integer. parseInt (endColor. substring (1, 3), 16); int endGreen = Integer. parseInt (endColor. substring (3, 5), 16); int endBlue = Integer. parseInt (endColor. substring (5, 7), 16); // initialize the color value if (mCurrentRed =-1) {mCurrentRed = startRed;} if (mCurrentGreen =-1) {mCurrentGreen = startGreen;} if (mC UrrentBlue =-1) {mCurrentBlue = startBlue;} // calculates the difference between the initial color and the ending color. int redDiff = Math. abs (startRed-endRed); int greenDiff = Math. abs (startGreen-endGreen); int blueDiff = Math. abs (startBlue-endBlue); int colorDiff = redDiff + greenDiff + blueDiff; if (mCurrentRed! = EndRed) {mCurrentRed = getCurrentColor (startRed, endRed, colorDiff, 0, fraction);} else if (mCurrentGreen! = EndGreen) {mCurrentGreen = getCurrentColor (startGreen, endGreen, colorDiff, redDiff, fraction);} else if (mCurrentBlue! = EndBlue) {mCurrentBlue = getCurrentColor (startBlue, endBlue, colorDiff, redDiff + greenDiff, fraction );} // assemble the calculated current color value and return String currentColor = "#" + getHexString (mCurrentRed) + getHexString (mCurrentGreen) + getHexString (mCurrentBlue); return currentColor ;} /*** calculate the current color based on the fraction value. */Private int getCurrentColor (int startColor, int endColor, int colorDiff, int offset, float fraction) {int currentColor; if (startColor> endColor) {currentColor = (int) (startColor-(fraction * colorDiff-offset); if (currentColor <endColor) {currentColor = endColor ;}} else {currentColor = (int) (startColor + (fraction * colorDiff-offset); if (currentColor> endColor) {currentColor = endColor ;} Return currentColor;}/*** converts a 10-digit color value to a hexadecimal value. */Private String getHexString (int value) {String hexString = Integer. toHexString (value); if (hexString. length () = 1) {hexString = "0" + hexString;} return hexString ;}}
This is probably the most complex class in our animation operation. That's right. The most technical method in the advanced usage of property animation is how to compile a proper TypeEvaluator. Fortunately, we have compiled a PointEvaluator and have an understanding of its basic working principles. Here we will mainly learn the logical process of ColorEvaluator.
First, get the initial value and end value of the color in the evaluate () method, divide the color into three parts by string truncation, and convert the RGB value to a decimal number, the value range of each color is 0-255. Next, calculate the difference between the initial color value and the ending color value. This difference is very important and determines the color change speed. If the initial color value is similar to the ending color value, then the color change will be slow, and if the color value is very different, for example, from black to white, it will go through the color range of 255*3, and the change will be very fast.
So the speed of controlling the color change is achieved through the getCurrentColor () method. This method calculates the current excessive color based on the current fraction value, in addition, the variation speed is controlled based on the difference between the initial color and the ending color, and the calculated color is returned.
Finally, because the color we calculated is a decimal number, we need to call the getHexString () method to convert them into hexadecimal strings, then, the RGB color is assembled and returned as the final result.
Well, after ColorEvaluator is written, we have completed the most complex work. The rest is some simple calling problems. For example, we want to achieve excessive animation from blue to red, it takes 5 seconds to write:
ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(), "#0000FF", "#FF0000");anim.setDuration(5000);anim.start();
The usage is very simple and easy to understand. I don't need to explain it again.
Next, we need to move the above Code to the MyAnimView class so that it can be played together with the Point animation, this requires the combination of animation technology we learned in the previous article. Modify the code in MyAnimView as follows:
public class MyAnimView extends View { ... private void startAnimation() { Point startPoint = new Point(RADIUS, RADIUS); Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS); ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); ObjectAnimator anim2 = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(), "#0000FF", "#FF0000"); AnimatorSet animSet = new AnimatorSet(); animSet.play(anim).with(anim2); animSet.setDuration(5000); animSet.start(); }}
As you can see, we haven't modified much of the Code. The focus is to modify some of the content in the startAnimation () method. Here we first move the code logic with excessive color to the startAnimation () method. Note that this code is executed in MyAnimView, so ObjectAnimator. you can directly pass this as the first parameter of ofObject. Then we created an animation set and set the two animations to play simultaneously. The animation lasts for five seconds and the animation is finally started. Run the code again, as shown in the following figure:
OK, the position animation and the color animation are very well integrated, and the effect looks quite good. In this way, we have mastered the advanced usage of ObjectAnimator.
Okay. Through the study in this article, we have a deep understanding of property animation. So far, the content of this article will be introduced in the next article.