1.幀動畫Frame-by-frame Animations
2.補(bǔ)間動畫Tweened Animations
3.屬性動畫 Property Animations(自定義Interpolator及Evaluator)
4.ViewPropertyAnimator
5.LayoutAnimationController
6.LayoutTransition
7.轉(zhuǎn)場動畫 Transition
8.ViewAnimationUtil
9.ArcMotion
10.AnimatedVectorDrawable
11.Ripple點擊效果
12.SpringAnimation
13.FlingAnimation
幀動畫與補(bǔ)間動畫在android3.0前就存在发绢,針對View
而屬性動畫在android3.0后出現(xiàn),不僅僅針對View,適用于任何對象的屬性(包含有相應(yīng)的getter,setter)绳锅。
幀動畫
由一系列圖片構(gòu)成的一幀一幀的動畫,和以前放電影的原理相似。但要注意如果圖片過多過大會出現(xiàn)OOM骡送,應(yīng)用場景如:頁面加載動畫世杀,下拉刷新動畫等惧盹。
特別注意抵怎,AnimationDrawable的start()方法不能在Activity的onCreate方法中調(diào)運(yùn)奋救,因為AnimationDrawable還未完全附著到window上,所以最好的調(diào)運(yùn)時機(jī)是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"> //只播放一次,默認(rèn)false循環(huán)播放
<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設(shè)置演侯。
AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
animationDrawable .start();
補(bǔ)間動畫
包括以下幾種:所在包為android.view.animation(從包名可以看出補(bǔ)間動畫是針對View的)姿染。
都繼承自 android.view.animation.Animation抽象類。
優(yōu)點:利用率高秒际,不用擔(dān)心內(nèi)存泄露問題悬赏,
- 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"http://漸漸消失
android:toAlpha="0.0" />4
Animation animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
或者通過代碼來實現(xiàn):
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);
或者通過代碼來實現(xiàn):
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">//當(dāng)前anim可以設(shè)置為ImageView的src背景,該Drawable為RotateDrawable,代碼中通過ImageView的getDrawable()獲得娄徊,并設(shè)置Level來達(dá)到控件不動闽颇,背景旋轉(zhuǎn)的目的。
</rotate>
Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate);
view.startAnimation(animation);
或者通過代碼來實現(xiàn):
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);
或者通過代碼來實現(xiàn):
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>標(biāo)簽包裹
<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="true">
<rotate/>...
<scale/>...
<translate/>...
<alpha/>...
<set></set>
</set>
set中的元素可以通過android:startOffset="1000"來設(shè)置動畫播放的延遲兵多。
Animation animation = AnimationUtils.loadAnimation(this, R.anim.set);
view.startAnimation(animation);
或者通過代碼來實現(xiàn):
AnimationSet animationSet = new AnimationSet(shareInterpolater);//shareInterpolater:是否將設(shè)置的interpolater應(yīng)用到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后才出現(xiàn)天吓。
繼承關(guān)系
AnimatorSet 對應(yīng)xml中的<set>
ValueAnimator 對應(yīng)xml中的 <animator>
ObjectAnimator 對應(yīng)xml中的<objectAnimator>
Activity onStop時需要手動停止動畫滔金,不然會出現(xiàn)Activity無法釋放而內(nèi)容泄露。
ObjectAnimator
直接對對象的屬性值進(jìn)行改變操作入撒,從而實現(xiàn)動畫效果
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
可以理解為一個數(shù)值生成器豆励,根據(jù)duration, interpolator, evaluator,生成從開始數(shù)值到結(jié)束數(shù)值的中間數(shù)值。獲得中間的值后再手動的設(shè)置的相應(yīng)的布局上椰棘,實現(xiàn)無間的動畫效果巨朦。
300毫秒內(nèi)得到從0到10中間的數(shù)值:
ValueAnimator animator = ValueAnimator.ofInt(0, 10).setDuration(300);
animator.addUpdateListener(animation -> {
int animatedValue = (int) animation.getAnimatedValue();
Log.e(TAG, "onAnimationUpdate: "+animatedValue );
});
animator.start();
打印結(jié)果:(默認(rèn)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
屬性動畫的兩個監(jiān)聽器:
/**
* 監(jiān)聽器一:監(jiān)聽動畫變化時的實時值
* 添加方法為:public void addUpdateListener(AnimatorUpdateListener listener)
*/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
/**
* 監(jiān)聽器二:監(jiān)聽動畫變化時四個狀態(tài)
* 添加方法為: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的代理,默認(rèn)空實現(xiàn)了所有的AnimatorListener的接口故源。
//只需要實現(xiàn)想要監(jiān)聽的方法即可。
animator1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
PropertyValueHolder
針對多個屬性值同時計算的情況聂沙,可以通過PropertyValueHolder來實現(xiàn)
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");
//再設(shè)置mPoint的X,Y坐標(biāo)并invalidate();
});
animator1.setDuration(1000);
animator1.start();
KeyFrame
通過更加精準(zhǔn)的設(shè)置每一幀的屬性值钦睡,來實現(xiàn)動畫荧嵌。
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
實現(xiàn)小球X軸及Y軸的同時移動。此時用IntEvaluator無法對兩個值進(jìn)行計算搏嗡,所以需要自定義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設(shè)置小球的當(dāng)前坐標(biāo):
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mPoint.x, mPoint.y, 30, mPaint);
}
自定義View中通過start()方法,以動畫的形式更改mPoint的值采盒,然后invalidate()旧乞,實現(xiàn)動畫效果
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越大迂猴,開始就越慢卑吭,結(jié)束就越快缘圈。
}
}
數(shù)學(xué)好的可以自定義Evaluator及Interpolator
參考: https://www.cnblogs.com/wondertwo/p/5327586.html
ViewPropertyAnimator
Android3.1 Api12 出現(xiàn)蹲坷。優(yōu)點,對之前的屬性動畫的優(yōu)化曹锨,一方面更加易讀(鏈?zhǔn)秸{(diào)用)谬莹,另一方面減少了invalidate()的次數(shù)诅迷,并且自動播放窃祝。
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回調(diào)前執(zhí)行
.withEndAction(() -> Log.e(TAG, "endAction"))//requires API level 16 onAnimationEnd回調(diào)后執(zhí)行
.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);
控制臺輸出結(jié)果:
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實現(xiàn):
<?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"http://指定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中屡贺,只需要常規(guī)設(shè)置RecyclerView即可。
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new ReAdapter());
通過代碼實現(xiàn):
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);//前面還沒結(jié)束锌杀,后面已經(jīng)開始,實現(xiàn)連續(xù)
recyclerView.setLayoutAnimation(controller);
recyclerView.startLayoutAnimation();//貌似不加這句動畫也會自動實現(xiàn)
效果:
在數(shù)據(jù)改變時泻仙,再執(zhí)行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秒后加了條數(shù)據(jù)糕再,執(zhí)行動畫
});
}
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();//沒有該操作,同樣可以執(zhí)行動畫玉转。
}
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 會出現(xiàn)異常
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這兩個必需要設(shè)置突想,不然不會出效果。
//column及row的值與item的位置究抓,及動畫時長猾担,delay時長計算出當(dāng)前item開始動畫的時間,并應(yīng)用到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;//個人認(rèn)為這樣和上面的結(jié)果是一樣的
//animationParams.row = index/columns;//如果是固定值工腋,整個一列的動畫同時進(jìn)行
} 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>
最終效果:和上面還是有區(qū)別的
我們來修改上面自定義的GridRecyclerView,來加深理解AnimationParams的含義
- AnimationParams.count,父容器中包含需要綁定該AnimationParams的Item的個數(shù)畅卓。(不是RecyclerView的所有Item的個數(shù))
- AnimationParams.index Item的下標(biāo)位置擅腰。
- AnimationParams.columnsCount RecyclerView設(shè)置列的個數(shù)
- AnimationParams.rowsCount RecyclerView設(shè)置行的個數(shù)
- AnimationParams.column 當(dāng)前Item在第幾列
- AnimationParams.row 當(dāng)前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;//如果是固定值趁冈,整個一行的動畫同時進(jìn)行
animationParams.row = index/columns;
效果:
將row的值改為固定值,則同一列中的所有Item都將以相同的時間開始
animationParams.column = index % columns;
animationParams.row = 1;//如果是固定值拜马,整個一列的動畫同時進(jìn)行
效果:
LayoutTransition
LayoutTransition 是Android 3.0 API Level 11 才出現(xiàn)的
當(dāng)ViewGroup中有View添加渗勘、刪除沐绒、隱藏、顯示的時候才會體現(xiàn)出來呀邢,即調(diào)用 ViewGroup的addView()洒沦、removeView(), setVisibility()時才會有動畫效果。
目前系統(tǒng)中支持以下5種狀態(tài)變化价淌,應(yīng)用程序可以為下面任意一種狀態(tài)設(shè)置自定義動畫:
1申眼、APPEARING:容器中出現(xiàn)一個視圖。
2蝉衣、DISAPPEARING:容器中消失一個視圖括尸。
3、CHANGING:布局改變導(dǎo)致某個視圖隨之改變病毡,例如調(diào)整大小濒翻,但不包括添加或者移除視圖。
4啦膜、CHANGE_APPEARING:其他視圖的出現(xiàn)導(dǎo)致某個視圖改變有送。
5、CHANGE_DISAPPEARING:其他視圖的消失導(dǎo)致某個視圖改變僧家。
我們先來看看系統(tǒng)默認(rèn)的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"http://開啟了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));
}
}
看下面的動圖得知系統(tǒng)默認(rèn)的動畫為:
APPEARING:逐漸顯現(xiàn)
DISAPPEARING:逐漸消失
CHANGE_APPEARING:滑動
CHANGE_DISAPPEARING:滑動
接下來我們看看如何自定義LayoutTransition
LayoutTransition transition = new LayoutTransition();
mContainer.setLayoutTransition(transition);
//APPEARING
Animator appearAnim = ObjectAnimator.ofFloat(null, "rotationX", 90f, 0)//X軸翻轉(zhuǎn)進(jìn)入
.setDuration(transition.getDuration(LayoutTransition.APPEARING));
transition.setAnimator(LayoutTransition.APPEARING, appearAnim);
//DISAPPEARING
Animator disappearAnim = ObjectAnimator.ofFloat(null, "rotationX", 0, 90f)//X軸翻轉(zhuǎn)消失
.setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnim);
//CHANGE_APPEARING(這其實就是系統(tǒng)默認(rèn)的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軸翻轉(zhuǎn)進(jìn)入
DISAPPEARING:X軸翻轉(zhuǎn)消失
CHANGE_APPEARING:滑動
CHANGE_DISAPPEARING:滑動
轉(zhuǎn)場動畫
(1).overridePendingTransition(int enterAnim, int exitAnim);
在startActivity()或finish()后調(diào)用的,只能設(shè)置B頁面的進(jìn)入動畫雀摘,和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中設(shè)置android:windowAnimationStyle
-
ActivityAnimation
將A頁面和B頁面的進(jìn)入退出動畫統(tǒng)一設(shè)置,應(yīng)用到所有頁面八拱。
<!-- 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頁面進(jìn)入動畫
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>//打開B頁面A頁面的退出動畫
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>//關(guān)閉B頁面B頁面退出動畫
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>//關(guān)閉B頁面A頁面進(jìn)入動畫
</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的例子為基礎(chǔ)肌稻,運(yùn)行后是沒有效果的清蚀。原因是overridePendingTransition的優(yōu)先權(quán)更高。嘗試將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都可以通過代碼實現(xiàn):
目前沒找到什么規(guī)律枷邪,不推薦使用。以下將設(shè)置結(jié)果列出來旦棉,以供參考齿风。
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設(shè)置B不設(shè)置效果:(與我想的效果不一樣)
B設(shè)置A不設(shè)置效果:(與我想的效果不一樣)
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設(shè)置B不設(shè)置效果:(與我想的效果不一樣)
B設(shè)置A不設(shè)置效果:(效果一樣, 但是會閃一下)
- ActivityAnimation與WindowAnimation:
一. windowAnimation包括 windowEnterAnimation與windowExitAnimation, windowHideAnimation與windowShowAnimation不知道干什么的绑洛。以上設(shè)置后不起作用救斑。且只能控制下一個Window的轉(zhuǎn)場//TODO
而activityAnimation包括activityOpenEnterAnimation, activityOpenExitAnimation, activityCloseEnterAnimation, activityCloseExitAnimation四種,用來控制兩個頁面的轉(zhuǎn)場動畫真屯。
二.經(jīng)測試脸候,windowAnimation與activityAnimation不沖突,都會執(zhí)行。
三.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設(shè)置到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();
第三種方法:創(chuàng)建Dialog時將Theme傳進(jìn)去。
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
首先設(shè)置(如果當(dāng)前應(yīng)用的主題是Material主題,AppCompat主題根據(jù)Android 版本左敌,加載不同的主題瘾蛋,以下幾種方法不設(shè)置也沒問題, 自動設(shè)置為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);一樣,只能設(shè)置B頁面的進(jìn)入動畫叼风,和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()后也能調(diào)用。而ActivityOptionsCompat就沒辦法控制返回時的動畫了无宿。(沒有相關(guān)的API)
效果:返回時沒效果
ActivityOptionsCompat.makeScaleUpAnimation(view, startX, startY, initialWidth, initialHeight)
實現(xiàn)點哪甲葬,新頁面就從哪兒展開。
點擊某個View懈贺,打開B頁面,B頁面會以相對于View的某個位置放大展開坡垫。
但是無法自定義動畫時長梭灿,退出動畫也無法設(shè)置。
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堡妒,再過渡到新的頁面。這里為了演示溉卓,用的是系統(tǒng)的啟動圖標(biāo)皮迟。
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)
跳轉(zhuǎn)新頁面時伏尼,新頁面的剪切范圍逐漸擴(kuò)大,直到完全顯示尉尾。
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)
涉及共享元素之間的轉(zhuǎn)換爆阶。
//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.
// 未設(shè)置setReturnTransition()默認(rèn)和setEnterTransition一樣
getWindow().setEnterTransition(transition);//android.R.styleable#Window_windowEnterTransition
// 未設(shè)置setReenterTransition()默認(rèn)和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
//相應(yīng)的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是否同時進(jìn)行
<item name="android:windowAllowEnterTransitionOverlap">true</item>
//Retrun與Reenter是否同時進(jìn)行
<item name="android:windowAllowReturnTransitionOverlap">true</item>
Transition相關(guān)的類在android.transition包下:
繼承關(guān)系如下:
-
ContentTransition
接下來舉幾個例子,看看如何進(jìn)行頁面轉(zhuǎn)換
通用的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);//去掉狀態(tài)欄的動畫效果
getWindow().setEnterTransition(transition);//B
getWindow().setReturnTransition(transition);//B
效果:更為合理
Slide
ActivityB
Slide transition = new Slide(Gravity.RIGHT);//可以不傳參數(shù)辨图,默認(rèn)為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班套,動畫通常與可見度相關(guān),用于頁面間的切換
而接下來介紹與View的尺寸,位置故河,樣式相關(guān)的幾個Transition動畫吱韭。
-
Share Element Transition
Share Element Transition 在上一節(jié)中已經(jīng)介紹過了,通過ActivityOptionsCompat.makeSceneTransitionAnimation來實現(xiàn)鱼的。
我們并沒有通過
getWindow().setSharedElement[]Transition(transition)
或
<item name="android:windowSharedElement[]Transition"></item>
來指定Transition的情況下理盆,系統(tǒng)默認(rèn)使用@android:transition/move - (將所有變換同時進(jìn)行的一個TransitionSet )。
ChangeBounds -捕獲共享元素的layout bound鸳吸,然后播放layout bound變化動畫熏挎。ChangeBounds 是共享元素變換中用的最多的,因為前后兩個activity中共享元素的大小和位置一般都是不同的晌砾。
ChangeTransform - 捕獲共享元素的縮放(scale)與旋轉(zhuǎn)(rotation)屬性 坎拐,然后播放縮放(scale)與旋轉(zhuǎn)(rotation)屬性變化動畫。
ChangeClipBounds - 捕獲共享元素clip bounds养匈,然后播放clip bounds變化動畫哼勇。
ChangeImageTransform - 捕獲共享元素(ImageView)的transform matrices 屬性,然后播放ImageViewtransform matrices 屬性變化動畫呕乎。與ChangeBounds相結(jié)合积担,這個變換可以讓ImageView在動畫中高效實現(xiàn)大小,形狀或者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
只有一個方法猬仁,以一個中心慢慢擴(kuò)展的顯示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
系統(tǒng)自帶的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
http://www.reibang.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
最后分享幾個不錯的動畫效果及網(wǎng)址:
KeyFrames
NineOldAndroids
Transitions-Everywhere
參考 :
官網(wǎng)
https://www.cnblogs.com/ldq2016/p/5407061.html
http://www.reibang.com/p/2412d00a0ce4
http://blog.csdn.net/harvic880925/article/details/50525521
http://www.reibang.com/p/0f83fbe756aa
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0819/8397.html
http://blog.csdn.net/u010142437/article/details/53819573
http://www.reibang.com/p/e497123652b5
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html