1.帧动画Frame-by-frame Animations
2.补间动画Tweened Animations
3.属性动画 Property Animations(自定义Interpolator及Evaluator)
4.ViewPropertyAnimator
5.LayoutAnimationController
6.LayoutTransition
7.转场动画 Transition
8.ViewAnimationUtil
9.ArcMotion
10.AnimatedVectorDrawable
11.Ripple点击效果
12.SpringAnimation
13.FlingAnimation
帧动画与补间动画在android3.0前就存在,针对View
而属性动画在android3.0后出现,不仅仅针对View,适用于任何对象的属性(包含有相应的getter,setter)。
帧动画
由一系列图片构成的一帧一帧的动画,和以前放电影的原理相似。但要注意如果图片过多过大会出现OOM,应用场景如:页面加载动画,下拉刷新动画等。
特别注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中调运,因为AnimationDrawable还未完全附着到window上,所以最好的调运时机是onWindowFocusChanged()方法中。
使用方法:
res/drawable/frame_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> //只播放一次,默认false循环播放
<item
android:drawable="@drawable/drawable_0"
android:duration="100" />
<item
android:drawable="@drawable/drawable_1"
android:duration="100" />
<item
android:drawable="@drawable/drawable_2"
android:duration="100" />
</animation-list>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/frame_animation"
/>
ImageView imageView= (ImageView) findViewById(R.id.imageView);
//imageView.setImageResource(R.drawable.frame_animation);//也可通过xml文件的src设置。
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
animationDrawable .start();
补间动画
包括以下几种:所在包为android.view.animation(从包名可以看出补间动画是针对View的)。
都继承自 android.view.animation.Animation抽象类。
优点:利用率高,不用担心内存泄露问题,
- AlphaAnimation <alpha>
- RotateAnimation <rotate>
- ScaleAnimation <scale>
- TranslationAnimation <translate>
- AnimationSet <set> 将以上动画组合
res/anim/alpha.xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="1000"
android:fromAlpha="1.0"//渐渐消失
android:toAlpha="0.0" />4
Animation animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
或者通过代码来实现:
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
alphaAnimation.setDuration(500);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
res/anim/scale.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="@android:integer/config_longAnimTime"
android:fillAfter="true"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:toXScale="1.5"
android:fromYScale="1.0"
android:toYScale="1.5">
</scale>
Animation animation = AnimationUtils.loadAnimation(this, R.anim.scale);
view.startAnimation(animation);
或者通过代码来实现:
ScaleAnimation animation = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
animation.setInterpolator(new AccelerateInterpolator());
view.startAnimation(animation);
res/anim/rotate.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/anticipate_interpolator"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotY="50%"
android:pivotX="50%"
android:drawable="@mipmap/ic_launcher">//当前anim可以设置为ImageView的src背景,该Drawable为RotateDrawable,代码中通过ImageView的getDrawable()获得,并设置Level来达到控件不动,背景旋转的目的。
</rotate>
Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate);
view.startAnimation(animation);
或者通过代码来实现:
RotateAnimation animation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(300);
animation.setInterpolator(this, android.R.anim.anticipate_interpolator);
view.startAnimation(animation);
res/anim/translate.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0%"
android:toXDelta="100%"
android:fromYDelta="0%"
android:toYDelta="100%"
android:duration="500"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
>
</translate>
Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate);
view.startAnimation(animation);
或者通过代码来实现:
TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0, Animation.RELATIVE_TO_PARENT, 1.0f,Animation.RELATIVE_TO_PARENT, 0, Animation.RELATIVE_TO_PARENT, 1.0f);
animation.setDuration(1000);
view.startAnimation(animation);
xml文件由<set>标签包裹
<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="true">
<rotate/>...
<scale/>...
<translate/>...
<alpha/>...
<set></set>
</set>
set中的元素可以通过android:startOffset="1000"来设置动画播放的延迟。
Animation animation = AnimationUtils.loadAnimation(this, R.anim.set);
view.startAnimation(animation);
或者通过代码来实现:
AnimationSet animationSet = new AnimationSet(shareInterpolater);//shareInterpolater:是否将设置的interpolater应用到Set中的每一个动画。
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
alphaAnimation.setDuration(500);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
animationSet.addAnimation(alphaAnimation);
imageView.startAnimation(animationSet);
属性动画
所在包为android.animator下。Android3.0后才出现。
继承关系
AnimatorSet 对应xml中的<set>
ValueAnimator 对应xml中的 <animator>
ObjectAnimator 对应xml中的<objectAnimator>
Activity onStop时需要手动停止动画,不然会出现Activity无法释放而内容泄露。
ObjectAnimator
直接对对象的属性值进行改变操作,从而实现动画效果
ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0.0f).setDuration(500).start();
ObjectAnimator.ofFloat(view, "scaleX", 0.0f, 1.0f).setDuration(300).start();
ObjectAnimator.ofFloat(view, "scaleY", 0.0f, 2.0f).setDuration(300).start();
ObjectAnimator.ofFloat(view, "translationX", 0, 100).setDuration(300).start();//移动的像素
ObjectAnimator.ofFloat(view, "translationY", 0, 100).setDuration(300).start();
AnimatorSet set = new AnimatorSet();
set.playTogether(animA,animB);
set.setDuration(3000);
set.start();
通过xml定义
res/animator/alpha.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
<objectAnimator
android:duration="300"
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:valueType="floatType"/>
</set>
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.alpha);
animator.setTarget(view);
animator.start();
res/animator/rotation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together">
<objectAnimator
android:duration="300"
android:valueFrom="0"
android:valueTo="360"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:repeatCount="3"
android:repeatMode="reverse"
android:startOffset="2000"
android:propertyName="rotation"
android:valueType="floatType"/>
</set>
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.rotate);
animator.setTarget(view);
animator.start();
ValueAnimator
可以理解为一个数值生成器,根据duration, interpolator, evaluator,生成从开始数值到结束数值的中间数值。获得中间的值后再手动的设置的相应的布局上,实现无间的动画效果。
300毫秒内得到从0到10中间的数值:
ValueAnimator animator = ValueAnimator.ofInt(0, 10).setDuration(300);
animator.addUpdateListener(animation -> {
int animatedValue = (int) animation.getAnimatedValue();
Log.e(TAG, "onAnimationUpdate: "+animatedValue );
});
animator.start();
打印结果:(默认Interpolator不是线性的,而是AccelerateDecelerateInterpolator)
onAnimationUpdate: 0
onAnimationUpdate: 0
onAnimationUpdate: 0
onAnimationUpdate: 0
onAnimationUpdate: 0
onAnimationUpdate: 1
onAnimationUpdate: 1
onAnimationUpdate: 2
onAnimationUpdate: 3
onAnimationUpdate: 4
onAnimationUpdate: 5
onAnimationUpdate: 6
onAnimationUpdate: 6
onAnimationUpdate: 7
onAnimationUpdate: 8
onAnimationUpdate: 8
onAnimationUpdate: 9
onAnimationUpdate: 9
onAnimationUpdate: 9
onAnimationUpdate: 10
属性动画的两个监听器:
/**
* 监听器一:监听动画变化时的实时值
* 添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
*/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
/**
* 监听器二:监听动画变化时四个状态
* 添加方法为:public void addListener(AnimatorListener listener)
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
/**
* 移除AnimatorUpdateListener
*/
void removeUpdateListener(AnimatorUpdateListener listener);
void removeAllUpdateListeners();
/**
* 移除AnimatorListener
*/
void removeListener(AnimatorListener listener);
void removeAllListeners();
AnimatorListenerAdapter AnimatorListener的代理,默认空实现了所有的AnimatorListener的接口。
//只需要实现想要监听的方法即可。
animator1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
PropertyValueHolder
针对多个属性值同时计算的情况,可以通过PropertyValueHolder来实现
PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", 30, 600);
PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", 30, 600);
ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(x, y);
animator.addUpdateListener(animation -> {
Integer animatedValue = animation.getAnimatedValue();//看源码可知与x1的值是相同的,取的是PropertyValuesHolder[0].getAnimatedValue();而PropertyValuesHolder[0]是上面的x
Integer x1 = animation.getAnimatedValue("x");
Integer y1 = animation.getAnimatedValue("y");
//再设置mPoint的X,Y坐标并invalidate();
});
animator1.setDuration(1000);
animator1.start();
KeyFrame
通过更加精准的设置每一帧的属性值,来实现动画。
Keyframe kf0 = Keyframe.ofInt(0, 400);
Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
Keyframe kf3 = Keyframe.ofInt(1f, 500);
PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, valuesHolder);
animator.setDuration(2000);
animator.start();
TypeEvaluator
实现小球X轴及Y轴的同时移动。此时用IntEvaluator无法对两个值进行计算,所以需要自定义Evaluator
class PointEvaluator implements TypeEvaluator<Point>{
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int x = (int) (startPoint.x + fraction * (endPoint.x - startPoint.x));
int y = (int) (startPoint.y + fraction * (endPoint.y - startPoint.y));
return new Point(x, y);
}
}
自定义View的onDraw()中,通过mPoint设置小球的当前坐标:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mPoint.x, mPoint.y, 30, mPaint);
}
自定义View中通过start()方法,以动画的形式更改mPoint的值,然后invalidate(),实现动画效果
public void start() {
final ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new Point(30, 30), new Point(600, 600));
animator.setDuration(2000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point)animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
Interpolator
TimeInterpolator接口:
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end //input 从0到1匀速变化
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.//通过计算,返回值可以为大于1或小于1
*/
float getInterpolation(float input);
}
LinearInterpolator:
public float getInterpolation(float input) {
return input;//均匀的线性值
}
AccelarateInterpolator:
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;// y = x^2 会越来越快
} else {
return (float)Math.pow(input, mDoubleFactor);//大于1的话,看图可知,factor越大,开始就越慢,结束就越快。
}
}
数学好的可以自定义Evaluator及Interpolator
参考: https://www.cnblogs.com/wondertwo/p/5327586.html
ViewPropertyAnimator
Android3.1 Api12 出现。优点,对之前的属性动画的优化,一方面更加易读(链式调用),另一方面减少了invalidate()的次数,并且自动播放。
v.animate()
.alpha(0.5f)
.rotation(360)
.scaleX(1.5f)
.scaleY(1.5f)
.translationX(50)
.translationY(50)
.withLayer()//requires API level 16 开启硬件加速
.withStartAction(() -> Log.e(TAG, "startAction"))//requires API level 16 onAnimationStart回调前执行
.withEndAction(() -> Log.e(TAG, "endAction"))//requires API level 16 onAnimationEnd回调后执行
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Log.e(TAG, "onAnimationStart: ");
}
@Override
public void onAnimationEnd(Animator animation) {
Log.e(TAG, "onAnimationEnd: ");
}
@Override
public void onAnimationCancel(Animator animation) {
Log.e(TAG, "onAnimationCancel: ");
}
@Override
public void onAnimationRepeat(Animator animation) {
Log.e(TAG, "onAnimationRepeat: ");
}
})
.setDuration(5000);
控制台输出结果:
startAction
onAnimationStart:
onAnimationEnd:
endAction
StateListAnimator
https://www.cnblogs.com/android-blogs/p/5816965.html
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- the pressed state; increase x and y size to 150% -->
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
</set>
</item>
<!-- the default, non-pressed state; set x and y size to 100% -->
<item android:state_pressed="false">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
</set>
</item>
</selector>
<Button android:stateListAnimator="@xml/animate_scale"
... />
LayoutAnimationController GridLayoutAnimationController
LayoutAnimation可以用在任何ViewGroup上
通过xml实现:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layoutAnimation="@anim/layout_animation"//指定LayoutAnimation
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
res/anim/layout_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/item_animation"
android:animationOrder="reverse"
android:delay="0.2"
android:interpolator="@android:anim/decelerate_interpolator"
/>
res/anim/item_animation.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400">
<translate
android:fromYDelta="-20%"
android:toYDelta="0"
android:interpolator="@android:anim/decelerate_interpolator"
/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="@android:anim/decelerate_interpolator"
/>
<scale
android:fromXScale="105%"
android:fromYScale="105%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/decelerate_interpolator"
/>
</set>
而在Activity中,只需要常规设置RecyclerView即可。
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new ReAdapter());
通过代码实现:
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new ReAdapter());
LayoutAnimationController controller = new LayoutAnimationController(AnimationUtils.loadAnimation(this, R.anim.item_animation));
//也可以通过此方法获得
//LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
controller.setDelay(0.2f);//前面还没结束,后面已经开始,实现连续
recyclerView.setLayoutAnimation(controller);
recyclerView.startLayoutAnimation();//貌似不加这句动画也会自动实现
效果:
在数据改变时,再执行LayoutAnimation
ArrayList list = new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
initData();
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new ReAdapter());
Observable.timer(3, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
list.add(0, "added");
updateLayout(recyclerView);//3秒后加了条数据,执行动画
});
}
private void updateLayout(RecyclerView recyclerView) {
LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
recyclerView.setLayoutAnimation(controller);
recyclerView.getAdapter().notifyDataSetChanged();
recyclerView.scheduleLayoutAnimation();//没有该操作,同样可以执行动画。
}
private void initData() {
for (char i = 'A'; i <= 'z'; i++) {
list.add("character " + i);
}
}
效果:
将LinearLayoutManager改为GridLayoutManager
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new GridLayoutManager(this, 4));
recyclerView.setAdapter(new ReAdapter());
//LayoutAnimationController controller = new LayoutAnimationController(AnimationUtils.loadAnimation(this, R.anim.translate));
LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
controller.setDelay(0.1f);
recyclerView.setLayoutAnimation(controller);
recyclerView.startLayoutAnimation();
效果:
如果将LayoutAnimationController改为GridLayoutAnimationController 会出现异常
GridLayoutAnimationController controller = new GridLayoutAnimationController(AnimationUtils.loadAnimation(this, R.anim.item_animation));
controller.setDirection(GridLayoutAnimationController.DIRECTION_LEFT_TO_RIGHT);
controller.setRowDelay(0.1f);
controller.setColumnDelay(0.1f);
recyclerView.setLayoutAnimation(controller);
recyclerView.startLayoutAnimation();
java.lang.ClassCastException: android.view.animation.LayoutAnimationController$AnimationParameters cannot be cast to android.view.animation.GridLayoutAnimationController$AnimationParameters
解决方法:
自定义RecyclerView
package com.example.gy.myapplication;
import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.GridLayoutAnimationController;
public class GridRecyclerView extends RecyclerView {
/** @see View#View(Context) */
public GridRecyclerView(Context context) { super(context); }
/** @see View#View(Context, AttributeSet) */
public GridRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
/** @see View#View(Context, AttributeSet, int) */
public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
@Override
protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params,
int index, int count) {
final LayoutManager layoutManager = getLayoutManager();
if (getAdapter() != null && layoutManager instanceof GridLayoutManager){
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
// If there are no animation parameters, create new once and attach them to
// the LayoutParams.
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
// Next we are updating the parameters
// Set the number of items in the RecyclerView and the index of this item
animationParams.count = count;
animationParams.index = index;
// Calculate the number of columns and rows in the grid
final int columns = ((GridLayoutManager) layoutManager).getSpanCount();
animationParams.columnsCount = columns;
animationParams.rowsCount = count / columns;
// Calculate the column/row position in the grid
//animationParams.column与animationParams.row这两个必需要设置,不然不会出效果。
//column及row的值与item的位置,及动画时长,delay时长计算出当前item开始动画的时间,并应用到item view。
final int invertedIndex = count - 1 - index;
animationParams.column = columns - 1 - (invertedIndex % columns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;
//上面参考其他人的方法,本人觉得下面的算法与上面的是一样的,更加简单。
//animationParams.column = index % columns;//个人认为这样和上面的结果是一样的
//animationParams.row = index/columns;//如果是固定值,整个一列的动画同时进行
} else {
// Proceed as normal if using another type of LayoutManager
super.attachLayoutAnimationParameters(child, params, index, count);
}
}
}
修改布局
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.gy.myapplication.GridRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.gy.myapplication.GridRecyclerView>
</android.support.constraint.ConstraintLayout>
最终效果:和上面还是有区别的
我们来修改上面自定义的GridRecyclerView,来加深理解AnimationParams的含义
- AnimationParams.count,父容器中包含需要绑定该AnimationParams的Item的个数。(不是RecyclerView的所有Item的个数)
- AnimationParams.index Item的下标位置。
- AnimationParams.columnsCount RecyclerView设置列的个数
- AnimationParams.rowsCount RecyclerView设置行的个数
- AnimationParams.column 当前Item在第几列
- AnimationParams.row 当前Item在第几行
每个ITEM动画delay计算方式(来自引用)
itemAnimationDuration = 300ms
rowDelay = 10% (30ms)
columnDelay = 10% (30ms)
direction = top_to_bottom|left_to_right
+------->
| +---+---+---+
| | 0 | 1 | 2 |
| +---+---+---+
V | 3 | 4 | 5 |
+---+---+---+
| 6 | 7 | 8 |
+---+---+---+
ROW COLUMN sFinal
0 = 0*30 + 0*30 = 0ms
1 = 0*30 + 1*30 = 30ms
2 = 0*30 + 2*30 = 60ms
3 = 1*30 + 0*30 = 30ms
4 = 1*30 + 1*30 = 60ms
5 = 1*30 + 2*30 = 90ms
6 = 2*30 + 0*30 = 60ms
7 = 2*30 + 1*30 = 90ms
8 = 2*30 + 2*30 = 120m
animation order by delay
+-----+-----+-----+
| 0 | 30 | 60 |
+-----+-----+-----+
| 30 | 60 | 90 |
+-----+-----+-----+
| 60 | 90 | 120 |
+-----+-----+-----+
将column的值改为固定值,则同一行中的所有Item都将以相同的时间开始。
animationParams.column = 1;//如果是固定值,整个一行的动画同时进行
animationParams.row = index/columns;
效果:
将row的值改为固定值,则同一列中的所有Item都将以相同的时间开始
animationParams.column = index % columns;
animationParams.row = 1;//如果是固定值,整个一列的动画同时进行
效果:
LayoutTransition
LayoutTransition 是Android 3.0 API Level 11 才出现的
当ViewGroup中有View添加、删除、隐藏、显示的时候才会体现出来,即调用 ViewGroup的addView()、removeView(), setVisibility()时才会有动画效果。
目前系统中支持以下5种状态变化,应用程序可以为下面任意一种状态设置自定义动画:
1、APPEARING:容器中出现一个视图。
2、DISAPPEARING:容器中消失一个视图。
3、CHANGING:布局改变导致某个视图随之改变,例如调整大小,但不包括添加或者移除视图。
4、CHANGE_APPEARING:其他视图的出现导致某个视图改变。
5、CHANGE_DISAPPEARING:其他视图的消失导致某个视图改变。
我们先来看看系统默认的LayoutTransition
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/main_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加控件"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"//开启了LayoutTransition
android:id="@+id/main_container"
android:orientation="vertical"/>
</LinearLayout>
public class LayoutTransitionActivity extends AppCompatActivity implements View.OnClickListener {
private LinearLayout mContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout_transition);
mContainer = (LinearLayout) findViewById(R.id.main_container);
findViewById(R.id.main_btn).setOnClickListener(this);
}
@Override
public void onClick(View view) {
Button btn = new Button(this);
btn.setText("移除自己");
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mContainer.removeView(v);
}
});
mContainer.addView(btn, 0, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
}
}
看下面的动图得知系统默认的动画为:
APPEARING:逐渐显现
DISAPPEARING:逐渐消失
CHANGE_APPEARING:滑动
CHANGE_DISAPPEARING:滑动
接下来我们看看如何自定义LayoutTransition
LayoutTransition transition = new LayoutTransition();
mContainer.setLayoutTransition(transition);
//APPEARING
Animator appearAnim = ObjectAnimator.ofFloat(null, "rotationX", 90f, 0)//X轴翻转进入
.setDuration(transition.getDuration(LayoutTransition.APPEARING));
transition.setAnimator(LayoutTransition.APPEARING, appearAnim);
//DISAPPEARING
Animator disappearAnim = ObjectAnimator.ofFloat(null, "rotationX", 0, 90f)//X轴翻转消失
.setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnim);
//CHANGE_APPEARING(这其实就是系统默认的CHANGE_APPEARING)
PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
PropertyValuesHolder pvhScaleXa = PropertyValuesHolder.ofFloat("scrollX", 0f, 1f);
PropertyValuesHolder pvhScaleYa = PropertyValuesHolder.ofFloat("scrollY",0f, 1f);
ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleXa, pvhScaleYa)
.setDuration(transition.getDuration(LayoutTransition.CHANGE_APPEARING));
transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
//CHANGE_DISAPPEARING
PropertyValuesHolder pvhSlide = PropertyValuesHolder.ofFloat("y", 0, 1);//滑动
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.5f, 1f);//缩小一半
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.5f, 1f);//缩小一半
Animator changingDisappearAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhSlide, pvhScaleY, pvhScaleX);
changingDisappearAnim.setDuration(transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changingDisappearAnim);
APPEARING:X轴翻转进入
DISAPPEARING:X轴翻转消失
CHANGE_APPEARING:滑动
CHANGE_DISAPPEARING:滑动
转场动画
(1).overridePendingTransition(int enterAnim, int exitAnim);
在startActivity()或finish()后调用的,只能设置B页面的进入动画,和A页面的退出动画:
//CurrentActivity点击Button
public void onClick(View view) {
startActivity(new Intent(this, TargetActivity.class));
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
//TargetActivity点击Button
public void back(View view) {
finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
(2). Theme中设置android:windowAnimationStyle
-
ActivityAnimation
将A页面和B页面的进入退出动画统一设置,应用到所有页面。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:windowAnimationStyle">@style/WindowAnimationStyle</item>
</style>
<style name="WindowAnimationStyle" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>//打开B页面B页面进入动画
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>//打开B页面A页面的退出动画
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>//关闭B页面B页面退出动画
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>//关闭B页面A页面进入动画
</style>
activity_open_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<translate
android:fromXDelta="100%"
android:toXDelta="0" />
</set>
activity_open_exit.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<scale
android:fromXScale="1"
android:toXScale="0.9"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toYScale="0.9"/>
<translate
android:fromXDelta="0"
android:toXDelta="-50%"
/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
</set>
activity_close_enter.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<scale
android:fromXScale="0.9"
android:toXScale="1"
android:fromYScale="0.9"
android:toYScale="1"
android:pivotY="50%"
android:pivotX="50%"
/>
<translate
android:fromXDelta="-50%"
android:toXDelta="0" />
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
</set>
activity_close_exit.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime">
<translate android:fromXDelta="0"
android:toXDelta="100%"/>
</set>
正常的效果为(Button文字没有改,只看效果即可, 下同):
但是如果以上面1的例子为基础,运行后是没有效果的。原因是overridePendingTransition的优先权更高。尝试将overridePendingTransition()注释掉:
//CurrentActivity点击Button
public void onClick(View view) {
startActivity(new Intent(this, TargetActivity.class));
//overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
//TargetActivity点击Button
public void back(View view) {
finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
效果为,返回时的overridePendingTransition覆盖了Style中的android:windowAnimationStyle:
-
WindowAnimation
我们在定义windowAnimationStyle时 也会看到有这么几个属性:
<item name="android:windowEnterAnimation"></item>
<item name="android:windowExitAnimation"></item>
<item name="android:windowShowAnimation"></item>
<item name="android:windowHideAnimation"></item>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowAnimationStyle">@style/WindowAnimationStyle</item>
</style>
<style name="WindowAnimationStyle">
<item name="android:windowEnterAnimation">@anim/activity_open_enter</item>
<item name="android:windowExitAnimation">@anim/activity_close_exit</item>
</style>
效果:
-
以上ActivityAnimation与WindowAnimation都可以通过代码实现:
目前没找到什么规律,不推荐使用。以下将设置结果列出来,以供参考。
getWindow().setWindowAnimations(R.style.WindowAnimationStyle);
activityAnimation:
<style name="WindowAnimationStyle" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
</style>
A设置B不设置效果:(与我想的效果不一样)
B设置A不设置效果:(与我想的效果不一样)
windowAnimation:
<style name="WindowAnimationStyle">
<item name="android:windowEnterAnimation">@anim/activity_open_enter</item>
<item name="android:windowExitAnimation">@anim/activity_close_exit</item>
<!--<item name="android:windowHideAnimation">@anim/activity_open_exit</item>-->
</style>
A设置B不设置效果:(与我想的效果不一样)
B设置A不设置效果:(效果一样, 但是会闪一下)
- ActivityAnimation与WindowAnimation:
一. windowAnimation包括 windowEnterAnimation与windowExitAnimation, windowHideAnimation与windowShowAnimation不知道干什么的。以上设置后不起作用。且只能控制下一个Window的转场//TODO
而activityAnimation包括activityOpenEnterAnimation, activityOpenExitAnimation, activityCloseEnterAnimation, activityCloseExitAnimation四种,用来控制两个页面的转场动画。
二.经测试,windowAnimation与activityAnimation不冲突,都会执行。
三.activityAnimation只作用在Activity上, 而windowAnimation除了可以作用在Activity上(因为每个Acitivity中包含一个PhoneWindow),更重要的一点,还可以作用在带有Window的Dialog上。
-
AlertDialog
第一种方法:不起作用
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:alertDialogTheme">@style/DialogTheme</item>
</style>
<style name="DialogTheme" parent="ThemeOverlay.AppCompat.Dialog.Alert">
<item name="android:windowAnimationStyle">@style/DialogStyle</item>
</style>
<style name="DialogStyle" parent="Animation.AppCompat.Dialog">
<item name="android:windowEnterAnimation">@anim/activity_open_enter</item>
<item name="android:windowExitAnimation">@anim/activity_close_exit</item>
<item name="android:windowShowAnimation">@anim/scale_in</item>//没有什么效果@TODO
<item name="android:windowHideAnimation">@anim/scale_out</item>//没有什么效果@TODO
</style>
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("fuck")
.setMessage("message")
.create();
alertDialog.show();
第二种方法:将Style设置到Dialog中Attributes的windowAnimations属性中。
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("fuck")
.setMessage("message")
.create();
WindowManager.LayoutParams attributes = alertDialog.getWindow().getAttributes();
attributes.windowAnimations = R.style.DialogStyle;
alertDialog.getWindow().setAttributes(attributes);
alertDialog.show();
第三种方法:创建Dialog时将Theme传进去。
new AlertDialog.Builder(this, R.style.DialogTheme)
.setTitle("fuck")
.setMessage("message")
.show();
(3). ActivityOptions,ActivityOptionsCompat
Android4.1 API16后可以使用ActivityOptions() 和startActivity(Intent, Bundle);
而ActivityOptionsCompat是 android.support.v4.app兼容包中的类,支持兼容老版。
added in API level 16
首先设置(如果当前应用的主题是Material主题,AppCompat主题根据Android 版本,加载不同的主题,以下几种方法不设置也没问题, 自动设置为true.)
<style name="AppTheme" parent="Theme.AppCompat">
<item name="android:windowContentTransitions">true</item>
</style>
或在Activity onCreate()中:
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
ActivityOptionsCompat.makeCustomAnimation(context, enterResId, exitResId)
这个与overridePendingTransition(int enterAnim, int exitAnim);一样,只能设置B页面的进入动画,和A页面的退出动画
ActivityOptionsCompat options = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.activity_open_enter, R.anim.activity_open_exit);
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
ActivityCompat.startActivity(context, intent, options.toBundle());
但是还有一点,overridePendingTransition()在finish()后也能调用。而ActivityOptionsCompat就没办法控制返回时的动画了。(没有相关的API)
效果:返回时没效果
ActivityOptionsCompat.makeScaleUpAnimation(view, startX, startY, initialWidth, initialHeight)
实现点哪,新页面就从哪儿展开。
点击某个View,打开B页面,B页面会以相对于View的某个位置放大展开。
但是无法自定义动画时长,退出动画也无法设置。
ActivityOptionsCompat options = ActivityOptionsCompat.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
ActivityCompat.startActivity(context, intent, options.toBundle());
效果:
ActivityOptionsCompat.makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)
切换时,会先显示一个Bitmap,再过渡到新的页面。这里为了演示,用的是系统的启动图标。
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
ActivityOptionsCompat options = ActivityOptionsCompat.makeThumbnailScaleUpAnimation(view, bitmap, view.getWidth()/2, view.getHeight()/2);
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
ActivityCompat.startActivity(context, intent, options.toBundle());
效果:不是特别明显,因为动画时间无法自定义。
ActivityOptionsCompat makeClipRevealAnimation(View source, int startX, int startY, int width, int height)
跳转新页面时,新页面的剪切范围逐渐扩大,直到完全显示。
ActivityOptionsCompat options = ActivityOptionsCompat.makeClipRevealAnimation(view, 0, 0, view.getWidth(), view.getHeight());
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
ActivityCompat.startActivity(context, intent, options.toBundle());
ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)
与
ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
Pair<View, String>... sharedElements)
涉及共享元素之间的转换。
//view是RecyclerView的ItemView
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(context, view, "share");
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
ActivityCompat.startActivity(context, intent, options.toBundle());
(4). WindowTransition
与WindowAnimation不同。包括ContentTransition与Share Element Transition.
// 未设置setReturnTransition()默认和setEnterTransition一样
getWindow().setEnterTransition(transition);//android.R.styleable#Window_windowEnterTransition
// 未设置setReenterTransition()默认和setExitTransition一样
getWindow().setExitTransition(transition);//android.R.styleable#Window_windowExitTransition
getWindow().setReturnTransition(transition);//android.R.styleable#Window_windowReturnTransition
getWindow().setReenterTransition(transition);//android.R.styleable#Window_windowReenterTransition
//SharedElementEnter Required Api 21
getWindow().setSharedElementEnterTransition(transition);//android.R.styleable#Window_windowSharedElementEnterTransition
getWindow().setSharedElementExitTransition(transition);//android.R.styleable#Window_windowSharedElementExitTransition
getWindow().setSharedElementReturnTransition(transition);//android.R.styleable#Window_windowSharedElementReturnTransition
getWindow().setSharedElementReenterTransition(transition);//android.R.styleable#Window_windowSharedElementReenterTransition
//相应的Style
<item name="android:windowEnterTransition"></item>
<item name="android:windowExitTransition"></item>
<item name="android:windowReturnTransition"></item>
<item name="android:windowReenterTransition"></item>
<item name="android:windowSharedElementEnterTransition"></item>
<item name="android:windowSharedElementExitTransition"></item>
<item name="android:windowSharedElementReturnTransition"></item>
<item name="android:windowSharedElementReenterTransition"></item>
//Enter与Exit是否同时进行
<item name="android:windowAllowEnterTransitionOverlap">true</item>
//Retrun与Reenter是否同时进行
<item name="android:windowAllowReturnTransitionOverlap">true</item>
Transition相关的类在android.transition包下:
继承关系如下:
-
ContentTransition
接下来举几个例子,看看如何进行页面转换
通用的ActivityA
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(context);
Intent intent = new Intent(context, MeiziDetailActivity.class);
intent.putExtra("url", url);
// ActivityCompat.startActivity(context, intent, options.toBundle());//兼容api16以下的
context.startActivity(intent, options.toBundle());
Explode
ActivityB
Explode transition = new Explode();
getWindow().setEnterTransition(transition);//B
getWindow().setReturnTransition(transition);//B
效果:
ActivityB
Explode transition = new Explode();
transition.excludeTarget(android.R.id.statusBarBackground, true);//去掉状态栏的动画效果
getWindow().setEnterTransition(transition);//B
getWindow().setReturnTransition(transition);//B
效果:更为合理
Slide
ActivityB
Slide transition = new Slide(Gravity.RIGHT);//可以不传参数,默认为Gravity.BOTTOM.
transition.excludeTarget(android.R.id.statusBarBackground, true);
getWindow().setEnterTransition(transition);//B
getWindow().setReturnTransition(transition);//B
效果:
Fade
ActivityB
Fade transition = new Fade();
transition.excludeTarget(android.R.id.statusBarBackground, true);
getWindow().setEnterTransition(transition);//B
getWindow().setReturnTransition(transition);//B
效果:
以上Fade Slide Explode都是继承自Visibility,动画通常与可见度相关,用于页面间的切换
而接下来介绍与View的尺寸,位置,样式相关的几个Transition动画。
-
Share Element Transition
Share Element Transition 在上一节中已经介绍过了,通过ActivityOptionsCompat.makeSceneTransitionAnimation来实现。
我们并没有通过
getWindow().setSharedElement[]Transition(transition)
或
<item name="android:windowSharedElement[]Transition"></item>
来指定Transition的情况下,系统默认使用@android:transition/move - (将所有变换同时进行的一个TransitionSet )。
ChangeBounds -捕获共享元素的layout bound,然后播放layout bound变化动画。ChangeBounds 是共享元素变换中用的最多的,因为前后两个activity中共享元素的大小和位置一般都是不同的。
ChangeTransform - 捕获共享元素的缩放(scale)与旋转(rotation)属性 ,然后播放缩放(scale)与旋转(rotation)属性变化动画。
ChangeClipBounds - 捕获共享元素clip bounds,然后播放clip bounds变化动画。
ChangeImageTransform - 捕获共享元素(ImageView)的transform matrices 属性,然后播放ImageViewtransform matrices 属性变化动画。与ChangeBounds相结合,这个变换可以让ImageView在动画中高效实现大小,形状或者ImageView.ScaleType 属性平滑过度
以最常用的ChangeBounds为例 (Required Api 19)
Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setEnterTransition(fade);
getWindow().setReturnTransition(fade);
ChangeBounds changeBounds = new ChangeBounds();
getWindow().setSharedElementEnterTransition(changeBounds);
getWindow().setSharedElementExitTransition(changeBounds);
- XML方式定义
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<slide android:duration="500">
<targets >
<target android:excludeId="@android:id/statusBarBackground"/>
<target android:targetId="@android:id/targetId"/>
</targets>
</slide>
</transitionSet>
<?xml version="1.0" encoding="utf-8"?>
<fade android:duration="500"
xmlns:android="http://schemas.android.com/apk/res/android">
</fade>
<?xml version="1.0" encoding="utf-8"?>
<explode xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<targets>
<target android:excludeId="@android:id/statusBarBackground"/>
</targets>
</explode>
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_quad"
android:duration="2000"
android:transitionOrdering="together"
android:startDelay="1000">
<fade/>
<explode/>
<slide/>
<changeBounds/>
<changeTransform/>
<changeImageTransform/>
<changeClipBounds/>
<changeScroll/>
</transitionSet>
-
Scene 与 TransitionManager
https://developer.android.google.cn/training/transitions/index.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0905/3420.html
https://blog.csdn.net/wangjiang_qianmo/article/details/56494471?locationNum=8&fps=1
-
自定义Transition
https://blog.csdn.net/qibin0506/article/details/53248597
ViewAnimationUtil
只有一个方法,以一个中心慢慢扩展的显示View的动画效果。
https://developer.android.google.cn/reference/android/view/ViewAnimationUtils.html
@param view The View will be clipped to the animating circle.
@param centerX The x coordinate of the center of the animating circle, relative to view.
@param centerY The y coordinate of the center of the animating circle, relative to view.
@param startRadius The starting radius of the animating circle.
@param endRadius The ending radius of the animating circle.
Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius)
ArcMotion
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0901/3400.html
系统自带的PathInterpolator
@interpolator/fast_out_linear_in.xml
@interpolator/fast_out_slow_in.xml
@interpolator/linear_out_slow_in.xml
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Path path = new Path();
path.arcTo(0f, 0f, 1000f, 1000f, 270f, -180f, true);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
animator.setDuration(2000);
animator.start();
} else {
// Create animator without using curved path
}
AnimatedVectorDrawable
//www.greatytc.com/writer#/notebooks/15481674/notes/22126140/preview
Ripple
https://blog.csdn.net/AwayEagle/article/details/52583913
https://blog.csdn.net/a396901990/article/details/40187203
SpringAnimation
https://developer.android.google.cn/guide/topics/graphics/spring-animation.html
FlingAnimation
https://developer.android.google.cn/guide/topics/graphics/fling-animation.html
最后分享几个不错的动画效果及网址:
KeyFrames
NineOldAndroids
Transitions-Everywhere
参考 :
官网
https://www.cnblogs.com/ldq2016/p/5407061.html
//www.greatytc.com/p/2412d00a0ce4
http://blog.csdn.net/harvic880925/article/details/50525521
//www.greatytc.com/p/0f83fbe756aa
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0819/8397.html
http://blog.csdn.net/u010142437/article/details/53819573
//www.greatytc.com/p/e497123652b5
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html