Android View加入動畫之後使其對使用者更加友好,使用者體驗也得到極大的增強,特別是Android 3.0之後,加入的動畫新成員——屬性動畫,使其更加具備互動的特性,而且通過動畫可以做出各種比較炫比較酷的效果。如果沒有動畫,那麼View的表現比較生硬,給使用者的體驗很不友好。Android動畫使其可以做到可以和iphone一樣的友好體驗,在Android5.0之後,加入了Android Material Design,其使用者體驗得到了更加友好的發揮,甚至超過了iphone,所以Android動畫是學習Android的重點之一。
Android動畫可以分為三種:View動畫、幀動畫和屬性動畫,上面已經提到了屬性動畫是Android API11之後新增的,如果想要相容API11以下的版本,可以使用NineOldAndroids動畫庫。
View動畫
View動畫是通過View的平移、旋轉、縮放和改變透明度來實現動畫,這也是一種漸進式動畫。
View動畫原理:每次繪製檢視時,View的所在ViewGroup中的drawChild函式獲取該View的Animation的Transformation值,然後呼叫canvas.concat(TransformToApply .getMatrix()),然後通過矩陣運算完成動畫幀,如果動畫沒有完成,就繼續呼叫invalidate()函式,啟動下次繪製來驅動動畫,從而完成整個動畫的繪製。
View動畫提供了TranslateAnimation、RotateAnimation、ScaleAnimation和AlphaAnimation四種動畫。通過這四種動畫基本上能夠完成所有的View的特效效果,不過View動畫明顯的缺點就是不具備互動性。而Android3.0新增的屬性動畫就具有互動性。
TranslateAnimation
TranslateAnimation也就是平移動畫,顯然該動畫能夠將View在水平或者垂直方向上進行移動。
如在X軸上移動200px
/**
* translateAnimation
* @return
*/
private Animation getTranslate(){
/*TranslateAnimation(int fromXType, float fromXValue,
int toXType, float toXValue,
int fromYType,float fromYValue,
int toYType, float toYValue)
*/
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 200,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
translateAnimation.setDuration(300);//動畫時間
translateAnimation.setFillAfter(false);//動畫結束後View是否保持動畫結束時的狀態
return translateAnimation;
}
複製程式碼
啟動該動畫
TranslateAnimation anim = (TranslateAnimation) getTranslate(); mTargetView.startAnimation(anim);
也可以在xml中寫:在專案res下新建一個anim目錄,在目錄下新建一個.xml檔案。
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="0%p"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="100%p"/>
複製程式碼
android:fromXDelta:起始點X軸座標,可以是數值、百分數、百分數p 三種樣式,比如 30、30%、30%p。 android:fromYDelta:起始點Y軸從標,可以是數值、百分數、百分數p 三種樣式; 當為數值時,表示在當前View的左上角,即原點處加上50px; 如果是50%,表示在當前控制元件的左上角加上自己寬度的50%; 如果是50%p,表示在當前的左上角加上父控制元件寬度的50%。 android:toXDelta:結束點X軸座標 android:toYDelta:結束點Y軸座標
使用:
TranslateAnimation anim = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.my_animation); mTargetView.startAnimation(anim);
RotateAnimation
RotateAnimation旋轉動畫,能夠將View進行旋轉,達到動畫的效果。
/**
* 圍繞自身中點
* 旋轉270度
*
* @return
*/
private Animation getRotate() {
RotateAnimation rotateAnimation = new RotateAnimation(
0, 270,//開始角度和旋轉的到的度數
Animation.RELATIVE_TO_SELF, 0.5f,//x
Animation.RELATIVE_TO_SELF, 0.5f);//y
rotateAnimation.setDuration(1000);
rotateAnimation.setFillAfter(true);
return rotateAnimation;
}
複製程式碼
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%"
android:duration="300"
android:fillAfter="true"/>
複製程式碼
ScaleAnimation
ScaleAnimation對View進行縮放的動畫。
/**
* 縮放
*
* @return
*/
private Animation getScaleAnim() {
/* (float fromX, float toX,
float fromY, float toY,
int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue)*/
ScaleAnimation scaleAnimation = new ScaleAnimation(
1.0f, 0.0f, 1.0f, 0.0f,//xy軸上的縮放起始值
Animation.RELATIVE_TO_SELF, 0.5f,//x軸的縮放中心
Animation.RELATIVE_TO_SELF, 0.5f);//y軸的縮放中心
scaleAnimation.setDuration(300);
scaleAnimation.setFillAfter(false);
return scaleAnimation;
}
複製程式碼
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"
android:duration="300"
android:fillAfter="true"/>
複製程式碼
android:fromXScale:起始的X方向上相對自身的縮放比例; android:toXScale:結尾的X方向上相對自身的縮放比例,浮點值; android:fromYScale:起始的Y方向上相對自身的縮放比例,浮點值, android:toYScale:結尾的Y方向上相對自身的縮放比例,浮點值; android:pivotX:縮放起點X軸座標,可以是數值、百分數、百分數p 三種樣式; android:pivotY:縮放起點Y軸座標,取值及意義跟android:pivotX一樣。
ScaleAnimation
AlphaAnimation是可以對View進行透明度變化的動畫。
/**
* 改變透明度動畫
* @return
*/
private Animation getAlpha(){
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
alphaAnimation.setDuration(300);
alphaAnimation.setFillAfter(true);
return alphaAnimation;
}
複製程式碼
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="300"
android:fillAfter="true"/>
複製程式碼
以上便是View動畫提供了TranslateAnimation、RotateAnimation、ScaleAnimation和AlphaAnimation四種動畫,上文中的動畫已經經過驗證的了。但是上文中的動畫都是單一形式的,那麼有沒有提供一種方法,能夠混合一起播放以上View動畫提供的四種動畫的呢?答案是:當然有,那就是使用AnimationSet動畫集合。
網上也有相當多的文章是關於AnimationSet的使用和介紹的,那麼什麼是AnimationSet呢?其實就是一個動畫集合,能夠將View動畫的提供的幾種動畫可以一起播放,從而產生更炫的動畫效果。AnimationSet也可以在xml檔案中設定,其對應的標籤就是標籤。
AnimationSet set = new AnimationSet(false);
ScaleAnimation anim01 = (ScaleAnimation) getScaleAnim();
RotateAnimation anim02 = (RotateAnimation) getRotate();
AlphaAnimation anim03 = (AlphaAnimation) getAlpha();
set.addAnimation(anim01);
set.addAnimation(anim02);
set.addAnimation(anim03);
set.setDuration(300);
mTargetView.startAnimation(set);
複製程式碼
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true">
<alpha
android:toAlpha="0.0"
android:fromAlpha="1.0"/>
<rotate
android:fromDegrees="0"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
</set>
複製程式碼
set的屬性有:
android:duration:動畫持續時間,以毫秒為單位 ;
android:fillAfter:如果設定為true,控制元件動畫結束時,將保持動畫最後時的狀態;
android:fillBefore:如果設定為true,控制元件動畫結束時,還原到開始動畫前的狀態;
android:fillEnabled:與android:fillBefore效果相同,都是在動畫結束時,將控制元件還原到初始化狀態;
android:repeatCount 重複次數;
android:repeatMode:重複型別,有reverse和restart兩個值,reverse表示倒序回放,restart表示重新放一遍,必須與repeatCount一起使用才能看到效果。因為這裡的意義是重複的型別,即回放時的動作;
android:interpolator:設定插值器。
使用:
AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.my_animation); mTargetView.startAnimation(set);
上文中就是View動畫的種類和集合的基本用法,除了以上的知識還有一個重要的知識點就是動畫的監聽。動畫的監聽可實現很多需求,比如在動畫開始或者結束後要做的事情,或者在動畫不斷回撥的方法onAnimationRepeat中實現一些動畫不能實現的效果。
新增動畫監聽很簡單,如下所示:
set.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
複製程式碼
自定義屬性動畫
在View動畫中除了上文中的四種動畫之外,其實自定義動畫也算是View動畫的一種,自定義需要繼承Animation類,並重寫覆蓋initialize和applyTransformation方法。在initialize方法中主要是做一些初始化的工作,applyTransformation通過矩陣變換來實現動畫效果,一般採用Camera來簡化矩陣變換過程。
applyTransformation(float interpolatedTime, Transformation t)方法有兩個引數 interpolatedTime:動畫當前完成的百分比和當前時間所對應的插值所計算得來的,取值0到1.0,也就是插值器的時間因子。
Transformation :矩陣的封裝類,可以使用這個類獲得當前的矩陣物件
Matrix matrix = t.getMatrix();
然後通過矩陣變化實現動畫。
public class MyAnimation extends Animation {
private float mWidth;
private float mHeight;
private Camera mCamera;
private float mRorateX = 270.0f;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(300);
setFillAfter(true);
mWidth = width/2;
mHeight = height/2;
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
Matrix matrix = t.getMatrix();
mCamera.save();
mCamera.rotateX(mRorateX*interpolatedTime);
mCamera.getMatrix(matrix);
mCamera.restore();
//通過pre方法設定矩陣作用前的偏移量來改變旋轉中心
matrix.preTranslate(mWidth,mHeight);
matrix.postTranslate(-mWidth,-mHeight);
}
}
複製程式碼
以上程式碼就是沿著X軸方向旋轉。
幀動畫
幀動畫是順序播放一組預先定義好的圖片,類似電影,逐幀播放圖片。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/loadingmore01" android:duration="30"/>
<item android:drawable="@drawable/loadingmore02" android:duration="30"/>
<item android:drawable="@drawable/loadingmore03" android:duration="30"/>
<item android:drawable="@drawable/loadingmore04" android:duration="30"/>
<item android:drawable="@drawable/loadingmore05" android:duration="30"/>
<item android:drawable="@drawable/loadingmore06" android:duration="30"/>
<item android:drawable="@drawable/loadingmore07" android:duration="30"/>
<item android:drawable="@drawable/loadingmore08" android:duration="30"/>
<item android:drawable="@drawable/loadingmore09" android:duration="30"/>
<item android:drawable="@drawable/loadingmore10" android:duration="30"/>
<item android:drawable="@drawable/loadingmore11" android:duration="30"/>
<item android:drawable="@drawable/loadingmore12" android:duration="30"/>
<item android:drawable="@drawable/loadingmore13" android:duration="30"/>
<item android:drawable="@drawable/loadingmore14" android:duration="30"/>
<item android:drawable="@drawable/loadingmore15" android:duration="30"/>
<item android:drawable="@drawable/loadingmore16" android:duration="30"/>
<item android:drawable="@drawable/loadingmore17" android:duration="30"/>
<item android:drawable="@drawable/loadingmore18" android:duration="30"/>
<item android:drawable="@drawable/loadingmore19" android:duration="30"/>
<item android:drawable="@drawable/loadingmore20" android:duration="30"/>
<item android:drawable="@drawable/loadingmore21" android:duration="30"/>
<item android:drawable="@drawable/loadingmore22" android:duration="30"/>
<item android:drawable="@drawable/loadingmore23" android:duration="30"/>
<item android:drawable="@drawable/loadingmore24" android:duration="30"/>
<item android:drawable="@drawable/loadingmore25" android:duration="30"/>
<item android:drawable="@drawable/loadingmore26" android:duration="30"/>
<item android:drawable="@drawable/loadingmore27" android:duration="30"/>
</animation-list>
複製程式碼
使用
animView = new ImageView(context); animView.setImageResource(R.drawable.anim_loading_more); animationDrawable = (AnimationDrawable) animView.getDrawable(); animationDrawable.start();
幀動畫的使用比較簡單,以上就是定義一個幀動畫並使用。
屬性動畫
屬性動畫是Android API11新加入的動畫,屬性動畫能夠對任何物件做動畫,只要物件有這個屬性,並且提供get和set方法即可,因為是API11才加入的動畫,所以如果想向下相容,可以使用nineoldandroids動畫庫來相容。
屬性動畫提供了ValueAnimator、ObjectAnimator和AnimatorSet,通過他們可以使物件實現炫麗的動畫效果。屬性動畫有一個最大的特點就是其可以實現與使用者互動,這也是其他動畫所不具備的特點。
ObjectAnimator
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"translationY",0,2000,0); anim.start();
這是ObjectAnimator的簡單用法,但是已經顯示了動畫效果。
ofFloat方法的引數分析:ofFloat(Object target, String propertyName, float... values) 第一個引數:動畫物件; 第二個引數:物件的屬性,要執行動畫的屬性,這裡我改變的是Y軸上的移動距離; 第三個引數:可變引數,可以有多個值,即物件屬性要執行動畫的值。上例中表示的是屬性動畫在Y軸上先移動到2000px的位置,然後再執行動畫移動到初始位置。
注意:這裡的ObjectAnimator動畫並沒有指定執行時間,是因為屬性動畫的預設時間間隔300ms,預設幀率為10ms/幀。
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"translationY",0,2000,-200,0); anim.setDuration(500); anim.start();
上例執行的動畫會有彈動的效果,動畫執行translationY,先移動到2000px的位置,然後再移動-200px的位置,最後回到初始的位置。而且設定了動畫執行的時間。
旋轉動畫:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"rotation",0,360,-200,0);
anim.setDuration(2000);
anim.start();
複製程式碼
改變透明度:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"alpha",1.0f,0.0f,0.8f);
anim.setDuration(2000);
anim.start();
複製程式碼
縮放:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView, "scaleX",1,0,1);
anim.setDuration(2000);
anim.start();
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTargetView, "scaleY",1,0,1);
anim1.setDuration(2000);
anim1.start();
複製程式碼
第三參數列示縮放的倍數。
改變背景顏色
ObjectAnimator anim = ObjectAnimator.ofInt(mStart, "backgroundColor",0xff0000,0xffff8080,0xff000000);
anim.setDuration(2000);
anim.start();
複製程式碼
ObjectAnimator 同時執行多個屬性動畫:
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1,0,1);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1,0,1);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
anim.setDuration(2000);
anim.start();
複製程式碼
ValueAnimator
ValueAnimator只針對值,只是對值做動畫運算,而不是針對控制元件,沒有跟任何的控制元件相關聯,需要監聽ValueAnimator的動畫過程來自己對控制元件做操作。
ValueAnimator anim = ValueAnimator.ofFloat(0,2000,-200,0);
anim.setDuration(2000);
anim.start();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int curValueFloat = (int)valueAnimator.getAnimatedValue();
//layout(int l, int t, int r, int b)
Log.i("tag","value"+curValueFloat);
}
});
複製程式碼
讀者可以參考自定義控制元件三部曲之動畫篇(四)——ValueAnimator基本使用和 Android 屬性動畫(Property Animation) 完全解析 (上)這兩篇文章,就已足夠了解ValueAnimator了。
AnimatorSet
AnimatorSet是屬性動畫的集合,可以給View設定一組的屬性動畫,也可以指定播放順序,是否一起播放或者是否延遲播放。
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1.0f,0.0f,1.0f);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1.0f,0.0f,1.0f);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
ObjectAnimator scale = ObjectAnimator.ofFloat(mTargetView,"rotation",0.0f,360.0f,-360.0f);
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(anim,scale);//一起播放
set.start();
複製程式碼
使用playTogether方法,可以設定一起播放設定進去的動畫。也可以使用以下語句設定一起播放動畫
set.play(anim).with(scale);
按順序播放動畫:
set.playSequentially(anim,scale);
除了使用playSequentially,還可以使用如下語句來設定按順序播放:
set.play(scale).after(anim);
動畫監聽AnimatorListener
ObjectAnimator translate = ObjectAnimator.ofFloat(goDescri, "translationX", -50);
translate.setInterpolator(new AccelerateInterpolator());
translate.setDuration(1500);
ObjectAnimator alpha1 = ObjectAnimator.ofFloat(goDescri, "alpha", 0.0f,1.0f);
alpha1.setInterpolator(new AccelerateInterpolator());
alpha1.setDuration(1000);
ObjectAnimator alpha2 = ObjectAnimator.ofFloat(goDescri, "alpha", 1.0f,0.0f);
alpha2.setInterpolator(new AccelerateInterpolator());
alpha2.setDuration(500);
AnimatorSet mAnimatorSet = new AnimatorSet();
mAnimatorSet.setInterpolator(new AccelerateInterpolator());
mAnimatorSet.play(translate).with(alpha1);
mAnimatorSet.play(alpha2).after(alpha1);
mAnimatorSet.addListener(new Animator.AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
}
@Override
public void onAnimationEnd(Animator animation)
{
mAnimatorSet.start();
}
@Override
public void onAnimationCancel(Animator animation)
{
}
@Override
public void onAnimationRepeat(Animator animation)
{
}
});
複製程式碼
上述程式碼通過監聽AnimatorListener,在動畫結束時重新啟動動畫,而從達到動畫順序迴圈播放的效果。
迴圈播放還可以如下設定:
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1.0f,0.0f,1.0f);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1.0f,0.0f,1.0f);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
anim.setRepeatCount(ObjectAnimator.INFINITE);//播放次數
anim.setRepeatMode(ObjectAnimator.REVERSE);//播放順序,順序和倒敘
ObjectAnimator scale = ObjectAnimator.ofFloat(mTargetView,"rotation",0.0f,360.0f,-360.0f);
scale.setRepeatCount(ObjectAnimator.INFINITE);
scale.setRepeatMode(ObjectAnimator.REVERSE);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(scale,anim);
set.start();
複製程式碼
通過設定播放次數anim.setRepeatCount(ObjectAnimator.INFINITE); 並且設定播放順序anim.setRepeatMode(ObjectAnimator.REVERSE);來達到迴圈播放動畫,這兩個函式在AnimatorSet時沒有。
插值器和估值器
插值器(Interpolator)可以分為時間插值器(TimeInterpolator)、線性插值器(LinearInterpolator)、加速減速插值器(AccelerateDecerateInterpolator)、減速插值器(DecerateInterpolator)。
時間插值器:根據時間流逝的百分比計算出當前屬性值改變的百分比; 線性插值器:使動畫勻速; 加速減速插值器:使動畫兩頭慢,中間快; 減速插值器:使動畫越來越慢。
時間插值器
根據時間流逝的百分比計算出當前屬性值改變的百分比
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(scale,anim);
set.setInterpolator(new TimeInterpolator() {
@Override
public float getInterpolation(float v) {
Log.i("tag","v----------->"+v);
return v;
}
});
複製程式碼
加速減速估值器
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(scale,anim);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.start();
複製程式碼
自定義估值器
public class DecelerateAccelerateInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
return result;
}
}
複製程式碼
以上自定義插值器實現了先減速後加速。
使用
set.setInterpolator(new DecelerateAccelerateInterpolator());
讀者可以閱讀這篇文章,此文章詳細的介紹了插值器和估值器[Android 動畫:你真的會使用插值器與估值器嗎?(含詳細例項教學)(http://www.jianshu.com/p/2f19fe1e3ca1)
估值器
估值器(TypeEvaluator):根據當前屬性的改變的百分比來計算改變後的屬性值,系統預置的估值器有:IntEvaluator(針對整形屬性)、FloatEvaluator(針對浮點型屬性)和ArgbEvaluator(針對Color屬性)。
對任意物件屬性做動畫
既然屬性動畫能夠對任何物件做動畫,只要物件有這個屬性,並且提供get和set方法即可。 如果這個物件沒有get和set方法,怎麼辦呢? 1)給物件加上get和set方法; 2)用個類來包裝原始物件,提供get和set方法;
private static class ViewWrapper{
private View target;
public ViewWrapper(View target) {
this.target = target;
}
public int getWidth(){
return target.getLayoutParams().width;
}
public void setWidth(int width){
target.getLayoutParams().width = width;
target.requestLayout();
}
}
複製程式碼
使用
ViewWrapper wrapper = new ViewWrapper(mTargetView); ObjectAnimator.ofInt(wrapper,"width",2000).start();
除了以上的方法外還可以使用ValueAnimator來監聽值的改變,然後做改變,達到動畫的效果。
關於這一點知識,讀者可以參考《Android開發藝術探索》的第七章《Android動畫深入分析》。
ViewAnimationUtils
Android5.0新特性增加了一種動畫框架,這就是ViewAnimationUtils,我們可以利用ViewAnimationUtils來做一些動畫效果,比如水波紋,揭露等動畫效果。
public final class ViewAnimationUtils {
private ViewAnimationUtils() {}
public static Animator createCircularReveal(View view,
int centerX, int centerY, float startRadius, float endRadius) {
return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
}
}
複製程式碼
上面程式碼就是ViewAnimationUtils類全部程式碼,該動畫工具只有createCircularReveal方法來建立動畫效果。從方法中,可以知道最後交給了RevealAnimator類來完成動畫效果。
createCircularReveal方法說明:
view:動畫作用的View;
centerX:擴散的中心點的X軸座標;
centerY:擴散的中心點的Y軸座標;
startRadius:開始擴散初始半徑;
endRadius:擴散結束半徑;
使用:
Animator animator = ViewAnimationUtils.createCircularReveal(mStart, mStart.getWidth()/2, mStart.getHeight()/2, 0, mStart.getHeight());
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
複製程式碼
上面程式碼就是mStart做水波紋擴散的效果。
mStart的揭露動畫:
Animator animator = ViewAnimationUtils.createCircularReveal(btn, 0, 0, 0, (float)Math.hypot(btn.getWidth(), btn.getHeight()));
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
複製程式碼
轉場動畫
Android的轉場動畫分為兩種:普通轉場和共享元素轉場。
普通轉場
普通轉場通過overridePendingTransition設定Activity的關閉和顯示動畫,如下所示:
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
複製程式碼
通過以上程式碼,跳轉Activity時:當前的Activity動畫淡出,新的Activity淡入,完成淡出淡入的Activity轉場動畫。
Activity退出時同樣可以設定轉場動畫
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
複製程式碼
這是API21系統自帶的轉場動畫,只要在API21或者以上才有。 fade_in:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@interpolator/decelerate_quad"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_longAnimTime" />
複製程式碼
fade_out:
<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="@android:integer/config_mediumAnimTime"
/>
複製程式碼
滑入滑出轉場
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);
複製程式碼
除了以上系統自帶的轉場動畫,也可以在res的anim目錄下定義動畫xml來實現自定義的轉場動畫。
scale_in.xml
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0%"
android:fromYScale="0%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="100%"/>
複製程式碼
scale_out.xml
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="100%"
android:fromYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0%"
android:toYScale="0%"/>
複製程式碼
使用的時候直接在overridePendingTransition指定動畫即可
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(R.anim.scale_in, R.anim.scale_out);
複製程式碼
底部滑入滑出 slide_in_botton.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="100%"
android:toYDelta="0%"
/>
複製程式碼
slide_out_botton.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="0%"
android:toYDelta="100%"
/>
複製程式碼
共享元素轉場
共享元素轉場是指:可以把兩個Activity當中的相同的元素關聯起來做連貫的變換動畫。 共享元素轉場是Android5.0或以上才顯示的,而使用共享元素轉場需要條件: A、必須給兩個Activity設定Window.FEATURE_CONTENT_TRANSITIONS,讓Activity允許使用轉場動畫。 而設定Window.FEATURE_CONTENT_TRANSITIONS有兩個方法: 1)通過在setContentView方法之前設 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 2)在主題修改 true
B、給兩個Activity當中的共享元素view都設定同一個名字(android:transitionName)
共享元素分為單共享元素和多個共享元素。
單共享元素
public class AnimationActivity extends AppCompatActivity implements View.OnClickListener {
private Button mStart;
private ImageView mTargetView;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_animation);
initView();
}
private void initView() {
mStart = (Button) this.findViewById(R.id.animation_btn);
mStart.setOnClickListener(this);
btn = (Button) this.findViewById(R.id.animation_btn01);
btn.setOnClickListener(this);
mTargetView = (ImageView) this.findViewById(R.id.animation_target);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View view) {
if (view == mStart) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, mTargetView, "transition_iv");
Intent intent = new Intent(this, Animator2Activity.class);
startActivity(intent, optionsCompat.toBundle());
} else if (view == btn) {
}
}
}
複製程式碼
設定getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 然後使用ActivityOptionsCompat.makeSceneTransitionAnimation來建立ActivityOptionsCompat物件。
makeSceneTransitionAnimation方法已經做好版本判斷了,我們看下該方法的引數。
(Activity activity,View sharedElement, String sharedElementName) activity:當前activity的物件 sharedElement:共享元素的View sharedElementName:也就是在sharedElement中設定android:transitionName名字。
activity_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
tools:context="com.main.animation.AnimationActivity">
<Button
android:id="@+id/animation_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="startAnimation"/>
<Button
android:id="@+id/animation_btn01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/animation_btn"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="Text"/>
<ImageView
android:id="@+id/animation_target"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:transitionName="transition_iv"
android:layout_centerHorizontal="true"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"/>
</RelativeLayout>
複製程式碼
在xml中需要注意的就是貢獻元素的View需要設定 android:transitionName。
跳轉的Activity
public class Animator2Activity extends AppCompatActivity {
private ImageView target;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//設定允許使用轉場動畫
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_animator2);
target = (ImageView)this.findViewById(R.id.animation2_target);
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
}
複製程式碼
同樣需要設Window.FEATURE_CONTENT_TRANSITIONS。
activity_animator2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.main.animation.Animator2Activity">
<ImageView
android:id="@+id/animation2_target"
android:layout_width="match_parent"
android:layout_height="500dp"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"
android:transitionName="transition_iv"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="0dp"/>
</RelativeLayout>
複製程式碼
同樣需要在共享的View設定android:transitionName,而且名字需要相同。
說明: 按返回鍵的時候自動實現了返回的共享元素轉場動畫,具體原因看原始碼:
public void onBackPressed() {
finishAfterTransition();
}
public void finishAfterTransition() {
if (!mActivityTransitionState.startExitBackTransition(this)) {
finish();
}
}
複製程式碼
當然你可以自己呼叫finishAfterTransition()來結束activity,也是有動畫。
多元素共享轉場
基本上跟單元素轉場一樣,唯一的不同點就是使用Pair.create來設定多個元素轉場。
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, Pair.create((View)mTargetView, "transition_iv"),
Pair.create((View)text, "transition_text"));
Intent intent = new Intent(this, Animator2Activity.class);
startActivity(intent, optionsCompat.toBundle());
複製程式碼
以上就是多元素共享轉場的核心程式碼。
使用RecyclerView實現共享元素轉場動畫。
TranslationActivity:
public class TranslationActivity extends AppCompatActivity {
private RecyclerView mList;
private List<String> mDatas = new ArrayList<>();
private MyAdapter mMyAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translation);
mList = (RecyclerView)this.findViewById(R.id.translation_list);
mList.setLayoutManager(new LinearLayoutManager(this));
for (int i = 0; i < 30; i++) {
String tx = "呵呵呵"+i;
mDatas.add(tx);
}
mMyAdapter = new MyAdapter(this,mDatas);
mList.setAdapter(mMyAdapter);
mMyAdapter.setOnItemOnclickListener(new MyAdapter.OnItemOnclickListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClickItem(ImageView iv, TextView tv, String text) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(TranslationActivity.this, Pair.create((View)iv, "transition_iv"),
Pair.create((View)tv, "transition_text"));
Intent intent = new Intent(TranslationActivity.this, Animator2Activity.class);
intent.putExtra("tag",text);
startActivity(intent, optionsCompat.toBundle());
}
});
}
private static class MyAdapter extends RecyclerView.Adapter{
private List<String>datas;
private Context mContext;
private OnItemOnclickListener mOnItemOnclickListener;
public void setOnItemOnclickListener(OnItemOnclickListener onItemOnclickListener) {
mOnItemOnclickListener = onItemOnclickListener;
}
public MyAdapter(Context context,List<String> datas) {
this.datas = datas;
mContext = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.transition_item,null);
return new MyHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyHolder myHolder = (MyHolder)holder;
myHolder.text.setText(datas.get(position));
myHolder.tag = datas.get(position);
}
@Override
public int getItemCount() {
if (datas != null && datas.size()>0){
return datas.size();
}
return 0;
}
private class MyHolder extends RecyclerView.ViewHolder{
private TextView text;
private String tag;
private ImageView iv;
public MyHolder(View itemView) {
super(itemView);
text = (TextView)itemView.findViewById(R.id.transition_tx);
iv = (ImageView) itemView.findViewById(R.id.transition_iv);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnItemOnclickListener != null){
mOnItemOnclickListener.onClickItem(iv,text,tag);
}
}
});
}
}
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
}
};
public interface OnItemOnclickListener{
void onClickItem(ImageView iv,TextView tv,String text);
}
}
}
複製程式碼
主要是設定RecyclerView,實現RecyclerView.Adapter,然後設定Item的點選,RecyclerView沒有為Item提供點選,所以要實現點選回撥。 在回撥設定共享元素轉場動畫
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(TranslationActivity.this, Pair.create((View)iv, "transition_iv"),Pair.create((View)tv, "transition_text"));
Intent intent = new Intent(TranslationActivity.this, Animator2Activity.class);
intent.putExtra("tag",text);
startActivity(intent, optionsCompat.toBundle());
複製程式碼
注意: 回撥的時候需要把Item的共享元素作為引數傳遞過來,否則設定不了。
translation_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical"
tools:context="com.main.animation.TranslationActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/translation_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
複製程式碼
Item條目的佈局transition_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp">
<ImageView
android:id="@+id/transition_iv"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="10dp"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"
android:transitionName="transition_iv"/>
<TextView
android:id="@+id/transition_tx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="tv"
android:textColor="#ffffff"
android:textSize="30dp"
android:transitionName="transition_text"/>
</RelativeLayout>
複製程式碼
同樣共享元素需要設定android:transitionName。
這裡的跳轉的頁面直接跳轉到上面的Animator2Activity頁面。
效果介面:
由於沒有錄製視訊,所以讀者很難看出效果,不過讀者可以根據上面程式碼,自行實戰一把,同時我的也希望讀者可以自己動手實戰,鞏固和理解該知識點。