Android animation is divided into: View animation, frame animation (also belongs to View animation), property animation. View animation is a graphical transformation of the View (pan, scale, rotate, transparency) to produce animation effects. Frame animation is a sequence of images played in order to create an animation effect. Property animation can animate objects by dynamically changing their properties.
First, View animation
The translation, zooming, rotation and transparency of View Animation correspond to four subclasses of Animation: TranslateAnimation, ScaleAnimation, RotateAnimation and AlphaAnimation. Views can be defined in XML or created in code. XML is recommended for readability.
XML 1.1 way
As shown below, r.animation_test is an xmL-defined animation. The tags Translate, Scale, alpha, and rotate correspond to four kinds of animations. The set tag is a collection of animations, corresponding to the AnimationSet class, consisting of multiple animations.
Where android:duration refers to the animation time, fillAfter is true, the animation will remain after the end, false will return to the initial state. Interpolator refers to the execution speed of an animation, which by default is interpolated at first acceleration then deceleration. Other tags and attributes are relatively simple and can be verified by their own research.
<? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="5000" android:fillAfter="true" android:interpolator="@android:anim/accelerate_decelerate_interpolator"> <! If duration in set has a value, Duration --> <translate Android :duration="1000" Android :fromXDelta="0" Android :toXDelta="400" /> <scale Duration ="2000" Android :fromXScale="0.5" Android :fromYScale="0.5" Android :toXScale="1" Android :toYScale="1" /> <alpha Android :duration="3000" Android :fromAlpha="0.2" Android :toAlpha="1" /> <rotate Android :fromDegrees="0" android:toDegrees="90" /> </set>Copy the code
Once the animation is defined, it is also easy to use by calling the View’s startAnimation method.
// View animation use, method 1: XML, recommended use.
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
textView1.startAnimation(animation);
Copy the code
1.2 Code creation dynamically
The code creation example below is also simple.
// View animation, method 2: new animation object
AnimationSet animationSet = new AnimationSet(false);
animationSet.setDuration(3000);
animationSet.addAnimation(new TranslateAnimation(0.100.0.0));
animationSet.addAnimation(new ScaleAnimation(0.1 f.1f.0.1 f.1f));
animationSet.setFillAfter(true);
textView2.startAnimation(animationSet);
// Use setAnimation to create a new animation object
AnimationSet animationSet2 = new AnimationSet(false);
animationSet2.setDuration(3000);
animationSet2.addAnimation(new TranslateAnimation(0.100.0.0));
animationSet2.addAnimation(new ScaleAnimation(0.1 f.1f.0.1 f.1f));
animationSet2.setFillAfter(true);
animationSet2.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}@Override
public void onAnimationEnd(Animation animation) {
MyToast.showMsg(AnimationTestActivity.this."View animation: code set: End of View animation ~");
}
@Override
public void onAnimationRepeat(Animation animation) {}}); textView3.setAnimation(animationSet2);Copy the code
Note:
- The startAnimation method plays the animation immediately; SetAnimation sets the next animation to play.
- SetAnimationListener listens for the start, end, and repeat of an animation.
1.3 Custom View animation
Usually we do not need to customize the View animation, the above four are basically enough.
To customize the View Animation, inherit the Animation and override the Initialize and applyTransformation methods. Initialization in Initialize, matrix transformation in applyTransformation (Camera required), and math required. Here is an example of a Rotate3dAnimation that rotates along the Y axis and pans along the Z axis to achieve a 3D effect.
/** * rotate along the Y axis and translate along the Z axis, Rotates the view on the Y axis between two specified angles. * This animation also adds a translation on the Z axis (depth) to improve the effect. */
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY. When the animation
* starts, a translation on the Z axis (depth) is performed. The length
* of the translation can be specified, as well as whether the translation
* should be reversed in time.
*
* @paramFromDegrees the start Angle of the 3D rotation along the y axis *@paramToDegrees the end Angle of the 3D rotation along the Y axis *@paramCenterX The X center of the 3D rotation Around the y axis (relative to itself) *@paramCenterY the Y center of the 3D rotation around the Y axis Y (relative to itself) *@paramDepthZ translation of the z axis. If it's >0, the bigger it is, the farther away it is, the smaller it becomes visually. *@param reverse true if the translation should be reversed, false otherwise
*/
public Rotate3dAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
if (mReverse) {
camera.translate(0.0 f.0.0 f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0 f.0.0 f, mDepthZ * (1.0 f- interpolatedTime)); } camera.rotateY(degrees); camera.getMatrix(matrix); camera.restore(); matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); }}Copy the code
1.4 the frame animation
Frame animation corresponds to the AnimationDrawable class and is used to play multiple images sequentially. Using an AnimationDrawable is simple. XML defines an AnimationDrawable, then sets it to the view as a background or resource and starts the animation. Examples are as follows:
R.rawable.frame_animation <? XML version="1.0" encoding=" UTF-8 "?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/home_icon_guide00" android:duration="50"/> <item android:drawable="@drawable/home_icon_guide01" android:duration="50"/> <item android:drawable="@drawable/home_icon_guide02" android:duration="50"/> <item android:drawable="@drawable/home_icon_guide03" android:duration="50"/> ...... </animation-list>Copy the code
tvFrameAnimation.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable frameAnimationBackground = (AnimationDrawable) tvFrameAnimation.getBackground();
frameAnimationBackground.start();
Copy the code
1.5 Special use scenes of View animation
1.5.1 Specify an exit animation for a ViewGroup child
Use LayoutAnimation to specify the child’s exit animation to the ViewGroup as follows:
1. Define the tag LayoutAnimation with XML:
- Android: Animation Sets the appearance animation of the child
- Android :animationOrder Sets the appearance order of the child, and Normal is the order
- Delay means that each child plays the animation 0.8 times later than the animation time specified in Android: Animation. If the animation time in android: Animation is 100ms, then each child will delay the animation by 800ms. If delay is not set, all children animate simultaneously.
<? The XML version = "1.0" encoding = "utf-8"? > <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" Android: animation = "@ anim/enter_from_left_for_child_of_group" android: animationOrder = "normal" android: delay = "0.8" > </layoutAnimation>Copy the code
R.anim.enter_from_left_for_child_of_group <? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="1000" android:fromXDelta="-100%p" android:toXDelta="0"/> </set>Copy the code
2. Set LayoutAnimation to ViewGroup
<LinearLayout android:id="@+id/ll_layout_animation" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layoutAnimation="@anim/layout_animation"> <TextView android:layout_width="50dp" android:layout_height="wrap_content" android:textColor="#ff0000" /> <TextView android:layout_width="60dp" Android :layout_height="wrap_content" android:textColor="#ff0000" android:text="qq" android:background="@color/colorPrimary"/> <TextView Android :layout_width="30dp" Android :layout_height="wrap_content" Android :textColor="#ff0000" Android :text=" aaargh "/> </LinearLayout>Copy the code
In addition to XML, of course, can also use LayoutAnimationController specified:
// This code sets up the LayoutAnimation to animate the ViewGroup's child
Animation enterAnim = AnimationUtils.loadAnimation(this, R.anim.enter_from_left_for_child_of_group);
LayoutAnimationController controller = new LayoutAnimationController(enterAnim);
controller.setDelay(0.8 f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
llLayoutAnimation.setLayoutAnimation(controller);
Copy the code
1.5.2 Activity switching effect
The Activity of the default switch animation effects, we can also custom: overridePendingTransition (int enterAnim, int exitAnim) can be specified Activity up or suspended animation.
- EnterAnim refers to the animation that the activity to open enters
- ExitAnim, to pause the activity to exit the animation
Note that it must be used after startActivity or Finish to take effect. As follows:
public static void launch(Activity activity) {
Intent intent = new Intent(activity, AnimationTestActivity.class);
activity.startActivity(intent);
// Open the activity, enter from the right, pause the activity exit to the left.activity.overridePendingTransition(R.anim.enter_from_right, R.anim.exit_to_left); }...@Override
public void finish(a) {
super.finish();
// Open the activity, the previous activity is entered from the left, and finish the activity exits to the right
overridePendingTransition(R.anim.enter_from_left, R.anim.exit_to_right);
}
Copy the code
R.anim.enter_from_right
<? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="500" android:fromXDelta="100%p" android:toXDelta="0"/> </set>Copy the code
R.anim.exit_to_left
<? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="500" android:fromXDelta="0" android:toXDelta="-100%p"/> </set>Copy the code
R.anim.enter_from_left
<? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="500" android:fromXDelta="-100%p" android:toXDelta="0"/> </set>Copy the code
R.anim.exit_to_right
<? The XML version = "1.0" encoding = "utf-8"? > <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:duration="500" android:fromXDelta="-100%p" android:toXDelta="0"/> </set>Copy the code
Second, property animation
Property animation can do almost anything, as long as the object has that property, you can animate that property. You can even have no objects. ObjectAnimator, ValueAnimator, AnimatorSet can be used to achieve rich animation.
2.1 Usage
Property animation can animate any object, not just a View. The default animation time is 300ms, 10ms/ frame. The value of an attribute of an object can be changed from value1 to value2 within a given time interval.
It is simple to use and can be implemented directly in code (recommended) or in XML, as shown in the following example:
// Property animation use, method 1: code, recommended use. transverse
ObjectAnimator translationX = ObjectAnimator
.ofFloat(textView6, "translationX".0.200)
.setDuration(1000);
translationX.setInterpolator(new LinearInterpolator());
setAnimatorListener(translationX);
// Animation of the property, method 2: XML. Vertical move
Animator animatorUpAndDown = AnimatorInflater.loadAnimator(this, R.animator.animator_test);
animatorUpAndDown.setTarget(textView6);
// Text color changes
ObjectAnimator textColor = ObjectAnimator
.ofInt(textView6, "textColor".0xffff0000.0xff00ffff)
.setDuration(1000);
textColor.setRepeatCount(ValueAnimator.INFINITE);
textColor.setRepeatMode(ValueAnimator.REVERSE);
// Note that the color jumps if it is not set, and ArgbEvaluator is a continuous excessive color change
textColor.setEvaluator(new ArgbEvaluator());
//animatorSet
mAnimatorSet = new AnimatorSet();
mAnimatorSet
.play(animatorUpAndDown)
.with(textColor)
.after(translationX);
mAnimatorSet.start();
/** * Sets the listener for the property animation *@param translationX
*/
private void setAnimatorListener(ObjectAnimator translationX) {
translationX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// this is called every time a frame is played}});if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
translationX.addPauseListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationResume(Animator animation) {
super.onAnimationResume(animation); }}); } translationX.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation); }}); }Copy the code
Animator_test is placed in res/animator.
<? The XML version = "1.0" encoding = "utf-8"? > <! -- Property animation test, generally recommended to use code implementation, Don't use XML - > < set XMLNS: android = "http://schemas.android.com/apk/res/android" android: ordering = "sequentially" > <! RepeatCount: default is 0, -1 is infinite loop --> <! RepeatMode: restart- repeat again, reverse- repeat again --> <! --valueType: Specifies the propertyName type. Optional: intType, floatType--> <! --android:pathData="" android:propertyXName="" android:propertyYName=""--> <objectAnimator android:propertyName="translationY" android:duration="1000" android:valueFrom="0" android:valueTo="120" android:startOffset="0" android:repeatCount="0" android:repeatMode="reverse" android:valueType="floatType" android:interpolator="@android:interpolator/accelerate_decelerate" /> <! -- Animator uses vueAnimator, less propertyName than objectAnimator. --<animator--> <! --android:duration="2000"--> <! --android:valueFrom=""--> <! --android:valueTo=""--> <! --android:startOffset=""--> <! --android:repeatCount=""--> <! --android:repeatMode=""--> <! --android:valueType=""--> <! --android:interpolator=""--> <! --android:pathData=""--> <! --android:propertyXName=""--> <! --android:propertyYName=""/>--> </set>Copy the code
TranslationX implements horizontal, animatorUpAndDown implements vertical, and textColor implements textColor changes. AnimatorUpAndDown is defined in XML, and the meaning of the tag is easy to understand. Finally, we use the Play, With, and After animatorSets to move horizontally, then vertically, and color changes simultaneously.
Note:
- As for the translation of View animation and property animation, the effect of setTranslationX is the same as the translation of View animation, the actual position of View layout is unchanged, only the position of View is changed. The difference is that the property animation adds displacement to the area where the touch point is active (whereas the view animation only changes the view position).
- Interpolator: Interpolator calculates the percentage change in the value of a current attribute based on the percentage of time elapsed. For example, if duration is 1000 and start is over 200, then the percentage of time is 0.2. If the LinearInterpolator is a LinearInterpolator, then the percentage of the property value changes is also 0.2
- Evaluator: Evaluator, which calculates the changed attribute value based on the percentage of attribute value obtained by the differentiator. IntEvaluator and FloatEvaluator are automatically set inside ofInt and onFloat. If ofInt is used and the attribute is color dependent, ArgbEvaluator is set. Textcolor.setevaluator (new ArgbEvaluator())).
- AnimatorUpdateListener and AnimatorListenerAdapter are two listener interfaces. The AnimatorUpdateListener callback method is called once every frame update; AnimatorListenerAdapter can listen to start, end, pause, continue, repeat, cancel, and override the methods you want to focus on.
2.2 Animate any property
A question for the Button below, how to achieve the width of the animation gradually elongated, i.e. the text unchanged, only elongated background width?
<Button
android:id="@+id/button_animator_test"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:text="Animate any property - Lengthen width"/>
Copy the code
First of all, the ScaleAnimation of View animation cannot be realized, because the scale of View is to enlarge the View, so the text will be elongated and deformed. What about property animations? Try ~
ObjectAnimator width1 = ObjectAnimator.ofInt(button, "width".1000);
width1.setDuration(2000);
width1.start();
Copy the code
But found, no effect! Why is that? The explanation is as follows.
Animating any property of an object requires two conditions:
- Object has a set method for corresponding properties. If the initial value is not set in the animation, there must be a GET method, and the system needs to get the initial value (if it does not meet the initial value, it will crash).
- The set method makes changes to an object, such as UI changes. If not, there will be no animation
The Button above has no animation effect, but does not satisfy the second item. Look at Button’s setWidth method:
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
Copy the code
It’s actually the setWidth method of TextView, and you can see that the values that you set in only affect the maximum and minimum width. Android :layout_width wrap_content setWidth will change the width only when the Button/TextView XML sets android:layout_width to wrap_content. SetWidth does not work when the Button/TextView XML sets Android :layout_width to a fixed dp value. Button XML is fixed at 180dp, so the setWidth attribute “width” is invalid. If the second requirement is not met, there will be no animation. (The property animation performed above works when you modify the Button XML setting Android :layout_width to “wrap_content”.)
So, how do you solve this problem when conditions are not met? There are the following processing methods:
- Add set and get methods to object, if you have permissions. (Generally not, such as TextView is in the SDK can not be directly changed)
- Wrap Object with a wrapper class that provides set and GET methods.
- Using ValueAnimator, listen for Value changes and implement attribute changes yourself.
private void testAnimatorAboutButtonWidth(a) {
//Button Width property animation: Animation works if the XML width is wrap_content.
// If you set the button's exact dp value, it will not work because the setWidth() method for the property "width" is valid only at wrap_content.
ObjectAnimator width1 = ObjectAnimator.ofInt(button, "width".1000);
width1.setDuration(2000);
// width1.start();
// How to animate the width animation when the button has an exact dp value?
// Wrap a layer and use layoutParams
ViewWrapper wrapper = new ViewWrapper(button);
ObjectAnimator width2 = ObjectAnimator.ofInt(wrapper, "width".1000);
width2.setDuration(2000);
// width2.start();
// Use ValueAnimator to display the width change for each frame
ValueAnimator valueAnimator = ValueAnimator.ofInt(button.getLayoutParams().width, 1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (Integer) animation.getAnimatedValue();
Log.i("hfy"."onAnimationUpdate: animatedValue=" + animatedValue);
// IntEvaluator intEvaluator = new IntEvaluator();
//// Get the change ratio of the attribute value and calculate the attribute value
// float animatedFraction = animation.getAnimatedFraction();
// Integer evaluate = intEvaluator.evaluate(animatedFraction, 300, 600);
// Log.i("hfy", "onAnimationUpdate: evaluate="+evaluate);
if(button ! =null) { button.getLayoutParams().width = animatedValue; button.requestLayout(); }}}); valueAnimator.setDuration(4000).start();
}
/** * package layer, provide the corresponding attribute set, get method */
private class ViewWrapper {
private final View mView;
public ViewWrapper(View view) {
mView = view;
}
public int getWidth(a) {
return mView.getLayoutParams().width;
}
public void setWidth(int width) { ViewGroup.LayoutParams layoutParams = mView.getLayoutParams(); layoutParams.width = width; mView.setLayoutParams(layoutParams); mView.requestLayout(); }}Copy the code
2.3 Principle of attribute animation
Property animation requires the object to have the set method of this property. The set method will be called to set the property value of the current moment according to the initial and final value of the property passed in during the execution of each frame update. As time passes, the set property values approach the final values for animation. If no initial value is passed in, the object also has a get method to get the initial value.
When obtaining the initial value and set attribute value, the method of get and set is called by reflection. See PropertyValuesHolder’s setupValue, setAnimatedValue methods:
private void setupValue(Object target, Keyframe kf) {
if(mProperty ! =null) {
Object value = convertBack(mProperty.get(target));
kf.setValue(value);
} else {
try {
if (mGetter == null) {
Class targetClass = target.getClass();
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
return;
}
}
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString()); }}}Copy the code
void setAnimatedValue(Object target) {
if(mProperty ! =null) {
mProperty.set(target, getAnimatedValue());
}
if(mSetter ! =null) {
try {
mTmpValueArray[0] = getAnimatedValue();
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString()); }}}Copy the code
The above renderings:
Three, the use of animation matters needing attention
- Use frame animation, avoid OOM. Because there are so many pictures.
- Property animations, if looping, should be stopped when the page exits to avoid memory leaks.
- If setVisibility(view.gone) fails after using the View animation, use view.clearAnimation() to resolve the problem.
Attached are some animation effects recorded previously
Custom view: used by TextSwitcher
Custom View: Information floating window/bullet screen — AutoSwitchTextView
Custom View: ProgressBar Foreground color, background color, smooth display progress
.
If you like this article or think it is well written, please help to like, bookmark and share it. Thanks!
.
Welcome to my public account: