8.4.4 Android Animation Collection - Property Animation - Revisited
Category Android Basic Entry Tutorial
Introduction:
In the previous section, we had an initial learning session on Android's property animation. I believe that everyone is no longer in a state of knowing just the basics about property animation. In this section, we will continue to explore some more advanced uses of Android's property animation! Let's still refer to Guo Shen's three articles:
Android Property Animation Complete Guide (Part 1), Basic Usage of Property Animation
Android Property Animation Complete Guide (Part 3), Usage of Interpolator and ViewPropertyAnimator
The content is still based on the above three articles. Okay, let's start this section's content:
1. Custom Evaluator
1) Evaluator Introduction
In the previous section 8.4.3 Android Animation Collection - Property Animation - First Encounter, the first step in using the animation was:
Calling the ofInt(), ofFloat(), or ofObject() static methods of ValueAnimator to create a ValueAnimator instance!
In the example, we used both ofInt and ofFloat, which are used for animating floating-point and integer data types respectively.
So, what about ofObject()? Initial object and end object? How is the transition done? Or how is this thing used?
Alright, with these questions in mind, let's first understand something: Evaluator. In fact, we mentioned this thing in the babbling about property animation concepts:
It tells the animation system how to transition from the initial value to the end value. Okay, our entry point is correct! Let's look at the source code of IntEvaluator to see what's inside:
It implements the TypeEvaluator interface and overrides the evaluate() method, which has three parameters, namely:
-fraction: The completion degree of the animation, based on which we calculate what the animated value should be.
-startValue: The starting value of the animation.
-endValue: The ending value of the animation.
Animated value = initial value + completion degree * (end value - initial value)
Similarly, there is also FloatEvaluator. If we want to tell the system how to transition from the initial object to the end object, then we need to implement the TypeEvaluator interface ourselves, that is, to customize the Evaluator. Talking too much is useless, let's write an example to see:
2) Usage Example
Running Effect Diagram:
Code Implementation:
Define an object Point.java, which only has x and y attributes and their get and set methods:
/**
* Created by Jay on 2015/11/18 0018.
*/
public class Point {
private float x;
private float y;
public Point() {
}
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
}
Then customize the Evaluator class: PointEvaluator.java, implement the interface and override the evaluate method:
/**
* Created by Jay on 2015/11/18 0018.
*/
public class PointEvaluator implements TypeEvaluator<Point>{
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX());
float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY());
Point point = new Point(x, y);
return point;
}
}
Then customize a View class: AnimView.java, which is very simple:
/**
* Created by Jay on 2015/11/18 0018.
*/
public class AnimView extends View {
public static final float RADIUS = 80.0f;
private Point currentPoint;
private Paint mPaint;
public AnimView(Context context) {
this(context, null);
}
public AnimView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
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(3000l);
anim.start();
}
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
drawCircle(canvas);
startAnimation();
} else {
drawCircle(canvas);
}
}
}
Finally, instantiate this View in MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new AnimView(this));
}
}
3) Enhanced Example
Based on the above example, let's add color changes during the circle's movement. Here, we use another ObjectAnimator to load the color change animation. We add an int color to control the color in the View, and write getColor() and setColor() methods. Let's first customize an Evaluator:
Running Effect Diagram:
Implementation Code:
ColorEvaluator.java:
/**
* Created by Jay on 2015/11/18 0018.
*/
public class ColorEvaluator implements TypeEvaluator<Integer>{
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int alpha = (int) (Color.alpha(startValue) + fraction *
(Color.alpha(endValue) - Color.alpha(startValue)));
int red = (int) (Color.red(startValue) + fraction *
(Color.red(endValue) - Color.red(startValue)));
int green = (int) (Color.green(startValue) + fraction *
(Color.green(endValue) - Color.green(startValue)));
int blue = (int) (Color.blue(startValue) + fraction *
(Color.blue(endValue) - Color.blue(startValue)));
return Color.argb(alpha, red, green, blue);
}
}
(Color.blue(endValue) - Color.blue(startValue)));
return Color.argb(alpha, red, green, blue);
}
}
Then add a color getter and setter method in the custom View; create an ObjectAnimator and an AnimatorSet, and then combine the animations together. Just adding a few things here, in case the reader has questions, let's create a new View directly.
AnimView2.java:
/**
* Created by Jay on 2015/11/18 0018.
*/
public class AnimView2 extends View {
public static final float RADIUS = 80.0f;
private Point currentPoint;
private Paint mPaint;
private int mColor;
public AnimView2(Context context) {
this(context, null);
}
public AnimView2(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
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();
}
});
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(),
Color.BLUE, Color.RED);
// The animation set combines the previous two animations to play simultaneously
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(anim).with(objectAnimator);
animatorSet.setStartDelay(1000l);
animatorSet.setDuration(3000l);
animatorSet.start();
}
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint == null) {
currentPoint = new Point(RADIUS, RADIUS);
drawCircle(canvas);
startAnimation();
} else {
drawCircle(canvas);
}
}
// Getter and setter methods for color
public int getColor() {
return mColor;
}
public void setColor(int color) {
mColor = color;
mPaint.setColor(color);
invalidate();
}
}
Then in MainActivity, change the setContentView to use AnimView2 instead of AnimView.
2. Interpolator (Interpolator)
Yes, we mentioned this when talking about tween animation. Do you still remember?
The interpolators mentioned above can be used for both tween animation and property animation. Tween animation also introduced a new TimeInterpolator interface, which is used to be compatible with the previous Interpolator, allowing all previous Interpolator implementation classes to be used directly in property animation. We can call the setInterpolator() method of the animation object to set different Interpolators. Let's make a few changes to make the ball fall from the top center of the screen to the bottom. Then we will call the following statement for our set animation: animatorSet.setInterpolator(new AccelerateInterpolator(2f)); The value in the parentheses controls the acceleration.
Running effect:
It seems a bit unreasonable. Normally, it should bounce, right? Let's try BounceInterpolator.
Hey, the effect is pretty good. There are many other system-provided Interpolators. You can try them one by one. We won't go into details here.
Now let's look at:
1) Internal implementation mechanism of Interpolator
Let's look at the source code of the TimeInterpolator interface, and we find that there is only one getInterpolation() method.
Simple explanation: The getInterpolation() method receives an input parameter, which changes as the animation runs, but it changes very regularly, increasing uniformly according to the set animation duration, with a range from 0 to 1. That is, when the animation starts, the value of input is 0, and when the animation ends, the value of input is 1, and the intermediate values change between 0 and 1 as the animation duration changes.
The input value here determines the fraction value in our TypeEvaluator interface. The input value is calculated by the system and passed into the getInterpolation() method. Then we can implement the algorithm in the getInterpolation() method ourselves, calculate a return value based on the input value, and this return value is the fraction.
Let's look at the code in LinearInterpolator:
Here, the input value is returned directly without any processing, that is, the fraction value is equal to the input value, which is the implementation method of the uniform motion Interpolator. The algorithm is different, which involves some mathematics, and I feel the importance of mathematics again. Let's post the source code of BounceInterpolator here:
Don't ask me about the algorithm here; I don't know either. Let's look at an easier one: AccelerateDecelerateInterpolator.
This Interpolator has an effect of accelerating first and then decelerating: (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f. The understanding of the algorithm:
Solution: Since the range of input is [0,1], the range of values in cos is [π,2π], corresponding to -1 and 1; after dividing this value by 2 and adding 0.5, the final return value of the getInterpolation() method is still in the range [0,1], and the corresponding curve is as follows:
So it's an acceleration first and then deceleration process.
Well, I can't play as a dunce anymore...
2) Custom Interpolator
Okay, let's not be sad for now, let's write a custom Interpolator example first: It's very simple, implement the TimeInterpolator interface, and override the getInterpolation method.
Sample code:
private class DecelerateAccelerateInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
if (input < 0.5) {
return (float) (Math.sin(input * Math.PI) / 2);
} else {
return 1 - (float) (Math.sin(input * Math.PI) / 2);
}
}
}
Call setInterpolator(new DecelerateAccelerateInterpolator()) to set it. Due to space limitations, no images are posted.
3. ViewPropertyAnimator
Added a new feature to the system since 3.1, providing a more convenient way to operate View animations. If it was before, to make a TextView change from normal state to transparent state, it would be written like this:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);
animator.start();
With ViewPropertyAnimator, it becomes more understandable:
textview.animate().alpha(0f);
It also supports chained usage, combining multiple animations, setting duration, setting Interpolator, etc.
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
The usage is simple, just check the documentation when using it. Also, there are a few detailed points to pay attention to:
- The entire ViewPropertyAnimator function is based on the new animate() method added to the View class, which creates and returns an instance of ViewPropertyAnimator. All methods called and all properties set are done through this instance. After defining the animation using ViewPropertyAnimator, the animation will start automatically. This mechanism also works for combined animations; as long as we keep adding new methods, the animation will not execute immediately. It will only start after all methods set on ViewPropertyAnimator have been executed. However, if you do not wish to use this default mechanism, you can also explicitly call the start() method to initiate the animation.
All interfaces of ViewPropertyAnimator are designed using chained syntax, where each method returns its own instance. Therefore, after calling one method, you can directly chain another method call, allowing all functionalities to be connected in a single line of code, enabling even complex animations to be achieved with minimal code.
4. Sample Code Download
A collection of animations can be found on Github, featuring many animation effects. Here is the link:
To study how various animations are implemented, you can check the source code yourself.
Summary of This Section
>
In this section, we covered some more advanced topics such as Evaluators, Interpolators, and ViewPropertyAnimator, expanding your knowledge. This section also marks the end of the basic introduction to drawing in Android. If you have mastered the content of this chapter, learning about custom controls or understanding custom controls written by others should no longer be daunting.
Thank you to Guo Shen for his articles, as much of the content on property animations was taken directly from him.
PS: The screenshots were switched to an emulator because my N5 was acting up...
-1.0 Android Basic Introduction Tutorial
-1.0.1 2015 Latest Android Basic Introduction Tutorial Table of Contents
-1.1 Background and System Architecture Analysis
-1.2 Development Environment Setup
-1.2.1 Using Eclipse + ADT + SDK to Develop Android APP
-1.2.2 Using Android Studio to Develop Android APP
-1.3 Solving SDK Update Issues
-1.4 Genymotion Emulator Installation
-1.5.1 Git Tutorial for Basic Local Repository Operations
-1.5.2 Using GitHub to Set Up a Remote Repository
-1.6 How to Use the 9-Patch Image
-1.7 Interface Prototype Design
-1.8 Project Source Analysis (Various Files, Resource Access)
-1.9 Android Program Signing and Packaging
-1.11 Decompiling APK to Obtain Code & Resources
-2.1 Concepts of View and ViewGroup
-2.2.1 LinearLayout (Linear Layout)
-2.2.2 RelativeLayout (Relative Layout)
-2.2.3 TableLayout (Table Layout)
-2.2.4 FrameLayout (Frame Layout)
-2.2.5 GridLayout (Grid Layout)
-2.2.6 AbsoluteLayout (Absolute Layout)
-2.3.1 Detailed Explanation of TextView (Text Box)
-2.3.2 Detailed Explanation of EditText (Input Box)
-2.3.5 RadioButton (Radio Button) & Checkbox (Checkbox)
-2.3.6 ToggleButton and Switch
-2.3.7 ProgressBar (Progress Bar)
-2.3.9 RatingBar (Star Rating Bar)
-2.4.1 ScrollView (Scroll Bar)
-2.4.2 Date & Time Components (Part 1)
-2.4.3 Date & Time Components (Part 2)
-2.4.4 Basic Explanation of Adapter
-2.4.6 Optimization of BaseAdapter
-2.4.7 Focus Issues with ListView
-2.4.8 Solving Checkbox Misalignment Issues in ListView
-2.4.9 Data Update Issues with ListView
-2.5.0 Building a Reusable Custom BaseAdapter
-2.5.1 Implementation of Multi-Layout ListView Items
-2.5.2 Basic Use of GridView (Grid View)
-2.5.3 Basic Use of Spinner (List Option Box)
-2.5.4 Basic Use of AutoCompleteTextView (Auto-Complete Text Box)
-2.5.5 Basic Use of ExpandableListView (Collapsible List)
-2.5.6 Basic Use of ViewFlipper (Flip View)
-2.5.8 Detailed Explanation of Notification (Status Bar Notification)
-2.5.9 Detailed Explanation of AlertDialog (Dialog Box)
-2.6.0 Basic Use of Other Common Dialogs
-2.6.1 Basic Use of PopupWindow (Floating Box)
-2.6.3 Simple Use of ViewPager
-2.6.4 Simple Use of DrawerLayout (Official Side Menu)
-3.1.1 Event Handling Mechanism Based on Listeners
-3.2 Event Handling Mechanism Based on Callbacks
-3.3 Analysis of Handler Message Passing Mechanism
-3.4 TouchListener vs OnTouchEvent + Multi-Touch
-3.5 Listening for Content Changes in EditText
-3.6 Responding to System Setting Events (Configuration Class)
-3.7 AsyncTask Asynchronous Task
- 4.2.1 Service Beginner
- 4.2.2 Service Intermediate
- 4.2.3 Service Advanced
- 4.3.1 BroadcastReceiver Basic
- 4.3.2 BroadcastReceiver Advanced
- 4.4.1 ContentProvider Beginner
- 4.4.2 ContentProvider Intermediate - Document Provider
- 4.5.1 Basic Usage of Intent
- 4.5.2 Passing Complex Data with Intent
- 5.1 Fragment Basic Overview
- 5.2.1 Fragment Example - Bottom Navigation Bar Implementation (Method 1)
- 5.2.2 Fragment Example - Bottom Navigation Bar Implementation (Method 2)
- 5.2.3 Fragment Example - Bottom Navigation Bar Implementation (Method 3)
- 5.2.4 Fragment Example - Bottom Navigation Bar + ViewPager Page Swipe
- 5.2.5 Fragment Example - Simple Implementation of News/Shopping App List Fragment
- 6.1 Data Storage and Access - File Storage and Reading/Writing
- 6.2 Data Storage and Access - SharedPreferences for Saving User Preferences
- 6.3.1 Data Storage and Access - Introduction to SQLite Database
- 6.3.2 Data Storage and Access - Further Exploration of SQLite Database
- 7.1.1 Android Network Programming Essentials and HTTP Protocol Study
- 7.1.2 Android HTTP Request and Response Headers Study
- 7.1.3 Android HTTP Request Method: HttpURLConnection
- 7.1.4 Android HTTP Request Method: HttpClient
- 7.2.1 Android XML Data Parsing
- 7.2.2 Android JSON Data Parsing
- 7.3.1 Android File Upload
- 7.3.2 Android File Download (1)
- 7.3.3 Android File Download (2)
- 7.4 Android WebService Call
- 7.5.1 WebView (Web View) Basic Usage
- 7.5.2 WebView and JavaScript Interaction Basics
- 7.5.3 WebView Considerations After Android 4.4
- 7.5.4 WebView File Download
- 7.5.5 WebView Cache Issues
- 7.5.6 WebView Handling Webpage Error Codes
- 7.6.1 Socket Network Basics Preparation
- 7.6.2 TCP Protocol Based Socket Communication (1)
- 7.6.3 TCP Protocol Based Socket Communication (2)
- 7.6.4 UDP Protocol Based Socket Communication
- 8.1.1 13 Drawable Types in Android Summary Part 1
- 8.1.2 13 Drawable Types in Android Summary Part 2
- 8.1.3 13 Drawable Types in Android Summary Part 3
- 8.2.1 Bitmap (Bitmap) Comprehensive Analysis Part 1
- 8.2.2 Bitmap OOM Issues
- 8.3.1 Detailed Explanation of Three Drawing Tool Classes
- 8.3.2 Drawing Class Practical Examples
- 8.3.3 Paint API - MaskFilter (Mask)
- 8.3.4 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 1)
- 8.3.5 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 2)
- 8.3.6 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 3)
- 8.3.7 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 4)
- 8.3.8 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 5)
- 8.3.9 Paint API - ColorFilter (Color Filter) (1/3)
- 8.3.10 Paint API - ColorFilter (Color Filter) (2/3)
- 8.3.11 Paint API - ColorFilter (Color Filter) (3/3)
- 8.3.12 Paint API - PathEffect (Path Effect)
- 8.3.13 Paint API - Shader (Image Rendering)
- 8.3.14 Paint Enum/Constants and ShadowLayer Shadow Effect
- 8.3.15 Paint API - Typeface (Font Style)
- 8.3.16 Canvas API Detailed Explanation (Part 1)
- 8.3.17 Canvas API Detailed Explanation (Part 2) Clipping Methods Collection
- 8.3.18 Canvas API Detailed Explanation (Part 3) Matrix and drawBitmapMesh
- 8.4.1 Android Animation Collection - Frame Animation
- 8.4.2 Android Animation Collection - Tween Animation
- 8.4.3 Android Animation Collection - Property Animation - Introduction
- 8.4.4 Android Animation Collection - Property Animation - Further Exploration
- 9.1 Using SoundPool to Play Sound Effects (Duang~)
- 9.2 MediaPlayer for Audio and Video Playback
- 9.3 Using Camera to Take Photos
- 9.4 Using MediaRecord for Audio Recording
- 10.1 TelephonyManager (Telephony Manager)
- 10.2 SmsManager (SMS Manager)
- 10.3 AudioManager (Audio Manager)
- 10.4 Vibrator (Vibrator)
- 10.5 AlarmManager (Alarm Service)
- 10.6 PowerManager (Power Service)
- 10.7 WindowManager (Window Management Service)
- 10.8 LayoutInflater (Layout Service)
- 10.9 WallpaperManager (Wallpaper Manager)
- 10.10 Sensor Topic (1) — Introduction
- 10.11 Sensor Topic (2) — Orientation Sensor
- 10.12 Sensor Topic (3) — Accelerometer/Gyroscope Sensor
- 10.12 Sensor Topic (4) — Understanding Other Sensors
- 10.14 Introduction to Android GPS
- 11.0 Completion of the 2015 Latest Android Basic Beginner's Tutorial
- 12.1 Android Practice: DrySister Viewing Girls App (Version 1) — Project Setup and Simple Implementation
- 12.2 DrySister Viewing Girls App (Version 1) — 2. Parsing Backend Data
- 12.3 DrySister Viewing Girls App (Version 1) — 3. Image Loading Optimization (Writing a Small Image Cache Framework)
- 12.4 DrySister Viewing Girls App (Version 1) — 4. Adding Data Caching (Introducing SQLite)
- 12.5 DrySister Viewing Girls App (Version 1) — 5. Code Review, Adjustment, and Logging Class Writing
- 12.6 DrySister Viewing Girls App (Version 1) — 6. Icon Creation, Obfuscation, Signing and Packaging, APK Slimming, App Release