Android Animation全面總結(jié)

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)存泄露問題悬赏,

  1. AlphaAnimation <alpha>
  2. RotateAnimation <rotate>
  3. ScaleAnimation <scale>
  4. TranslationAnimation <translate>
  5. 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)系


image.png

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);
}
TimeInterpolator子類.png

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é)束就越快缘圈。
        }
    }
image.png

數(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());
3.gif
通過代碼實現(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)

效果:


3.gif

在數(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);
        }
    }

效果:

3.gif
將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();

效果:


3.gif
如果將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ū)別的


3.gif
我們來修改上面自定義的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.gif

將row的值改為固定值,則同一列中的所有Item都將以相同的時間開始

        animationParams.column = index % columns;
        animationParams.row = 1;//如果是固定值拜马,整個一列的動畫同時進(jìn)行

效果:


colum.gif

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:滑動


2.gif
接下來我們看看如何自定義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:滑動

效果:
2.gif

轉(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);
    }
override_pending_transition.gif
(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文字沒有改阵赠,只看效果即可, 下同):


normal.gif

但是如果以上面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:


priority.gif
  • 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>

效果:


window.gif
  • 以上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è)置效果:(與我想的效果不一樣)


a.gif

B設(shè)置A不設(shè)置效果:(與我想的效果不一樣)


b.gif

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è)置效果:(與我想的效果不一樣)


bb.gif

B設(shè)置A不設(shè)置效果:(效果一樣, 但是會閃一下)


aa.gif
  • ActivityAnimation與WindowAnimation:

一. windowAnimation包括 windowEnterAnimationwindowExitAnimation, windowHideAnimationwindowShowAnimation不知道干什么的绑洛。以上設(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();
alertdailog1.gif
(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)

效果:返回時沒效果


a.gif

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());

效果:


b.gif

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());

效果:不是特別明顯,因為動畫時間無法自定義桑寨。


c.gif

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());
d.gif

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());
e.gif
(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包下:


image.png

繼承關(guān)系如下:


image.png
  • 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

效果:


a.gif

ActivityB

        Explode transition = new Explode();
        transition.excludeTarget(android.R.id.statusBarBackground, true);//去掉狀態(tài)欄的動畫效果
        getWindow().setEnterTransition(transition);//B
        getWindow().setReturnTransition(transition);//B

效果:更為合理


b.gif

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

效果:


c.gif

Fade
ActivityB

        Fade transition = new Fade();
        transition.excludeTarget(android.R.id.statusBarBackground, true);
        getWindow().setEnterTransition(transition);//B
        getWindow().setReturnTransition(transition);//B

效果:


d.gif

以上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);
a.gif
  • 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>

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
}
a.gif

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市湿刽,隨后出現(xiàn)的幾起案子的烁,更是在濱河造成了極大的恐慌,老刑警劉巖诈闺,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渴庆,死亡現(xiàn)場離奇詭異,居然都是意外死亡雅镊,警方通過查閱死者的電腦和手機(jī)襟雷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仁烹,“玉大人耸弄,你說我怎么就攤上這事∽跨郑” “怎么了叙赚?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵老客,是天一觀的道長。 經(jīng)常有香客問我震叮,道長胧砰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任苇瓣,我火速辦了婚禮尉间,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘击罪。我一直安慰自己哲嘲,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布媳禁。 她就那樣靜靜地躺著眠副,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竣稽。 梳的紋絲不亂的頭發(fā)上囱怕,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音毫别,去河邊找鬼娃弓。 笑死,一個胖子當(dāng)著我的面吹牛岛宦,可吹牛的內(nèi)容都是我干的台丛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼砾肺,長吁一口氣:“原來是場噩夢啊……” “哼挽霉!你這毒婦竟也來了篷扩?” 一聲冷哼從身側(cè)響起巴柿,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扁眯,沒想到半個月后疫衩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荣德,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年闷煤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涮瞻。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲤拿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出署咽,到底是詐尸還是另有隱情近顷,我是刑警寧澤生音,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窒升,受9級特大地震影響缀遍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饱须,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一域醇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓉媳,春花似錦譬挚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玩荠,卻和暖如春漆腌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姨蟋。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工屉凯, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眼溶。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓悠砚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堂飞。 傳聞我的和親對象是個殘疾皇子灌旧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎(chǔ)知識绰筛,剛好有人微博私信讓全面說說Android的動畫枢泰,所以今...
    未聞椛洺閱讀 2,711評論 0 10
  • 3.0以前,android支持兩種動畫模式铝噩,tween animation,frame animation衡蚂,在an...
    Ten_Minutes閱讀 1,654評論 0 4
  • 【Android 動畫】 動畫分類補(bǔ)間動畫(Tween動畫)幀動畫(Frame 動畫)屬性動畫(Property ...
    Rtia閱讀 6,164評論 1 38
  • 轉(zhuǎn)載一篇高質(zhì)量博文,原地址請戳這里轉(zhuǎn)載下來方便今后查看骏庸。1 背景不能只分析源碼呀毛甲,分析的同時也要整理歸納基礎(chǔ)知識,...
    Elder閱讀 1,942評論 0 24
  • Android框架提供了兩種類型的動畫:View Animation(也稱視圖動畫)和Property Anima...
    RxCode閱讀 1,635評論 1 5