提起影片,無論是哪種語言哪種系統框架,比如說android、iOS、H5、Flash等,影片在其之中都扮演著舉足輕重的角色,Android系統中最常用的影片方式有三種:
- 補間影片(Tween Animation)
- 幀影片(Frame Animation)
- 屬性影片(Property Animation)
本文就總結一下補間影片的相關玩法,
Android影片系列:
- 《天啦嚕!原來Android補間影片可以這么玩》
- 《天啦嚕!原來Android幀影片這么簡單》
- 《天啦嚕!原來Android屬性影片也不過如此》
什么是補間影片
Creates an animation by performing a series of transformations on a single image with an Animation.
Tween Animation,通過 Animation 物件在影像上執行一系列的變換而形成的影片,舉例來說,就是 Tween Animation 可以改變界面上顯示控制元件的狀態,如 Button 的顯示、隱藏,ImageView 的尺寸縮放等等,
補間影片分類
補間影片包括五類影片,分別是:
- AlphaAnimation,主要用于控制 View 的可見性(顯示|隱藏),
- ScaleAnimation,主要用于縮放 View 大小,
- TranslateAnimation,主要用于移動 View 的位置,
- RotateAnimation,主要用于旋轉 View,
- AnimationSet,某些場景僅靠上面單一型別的影片是無法實作的,需要多個型別的影片組合才能達到最終的效果,AnimationSet 的主要作用就是組合各類 Tween Animation,
補間影片的實作形式
補間影片的實作形式有兩種:xml創建和code實作,其中xml創建的xml影片檔案要放在res/anim/目錄下,
- res/anim/
目錄下放的是視圖影片的XML實作和布局影片(LayoutAnimation) - res/animations/
目錄下存放的是屬性影片的XML實作 - res/drawable/
目錄下存放的是幀影片的XML實作
AlphaAnimation
通過XML創建
語法
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="integer"
android:fillAfter="true|false"
android:fillBefore="true|false"
android:fillEnabled="true|false"
android:interpolator="@[package:]anim/interpolator_resource"
android:repeatCount="infinite|integer"
android:repeatMode="reverse|restart"
android:fromAlpha="float"
android:toAlpha="float" />

示例
XML定義:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/integer_one_thousand_and_two_hundred"
android:fillAfter="true"
android:fromAlpha="@integer/integer_one"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toAlpha="@integer/integer_zero" />
代碼呼叫:
Button mButton = (Button) findViewById(R.id.Button);
// 創建影片物件,并傳入設定的影片效果xml檔案
Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation_alpha);
// 播放影片
mButton.startAnimation(alphaAnimation);
其他影片XML定義形式在代碼呼叫這部分也是類似,
通過代碼實作
語法
AlphaAnimation alphaAnimation = new AlphaAnimation(float fromAlpha, float toAlpha);
alphaAnimation.setInterpolator(Interpolator i);
alphaAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(alphaAnimation);
示例
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.1f);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
alphaAnimation.setFillAfter(mIsSaveAnimationState);
alphaAnimation.setDuration(800);
mTarget.startAnimation(alphaAnimation);
ScaleAnimation
通過XML創建
語法
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="integer"
android:fillAfter="true|false"
android:fillBefore="true|false"
android:fillEnabled="true|false"
android:interpolator="@[package:]anim/interpolator_resource"
android:repeatCount="infinite|integer"
android:repeatMode="reverse|restart"
android:fromXScale="float"
android:fromYScale="float"
android:toXScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float"
/>

示例
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/integer_one_thousand_and_two_hundred"
android:fillAfter="true"
android:fromXScale="@fraction/percent_one_hundred"
android:fromYScale="@fraction/percent_one_hundred"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="@fraction/percent_fifty"
android:pivotY="@fraction/percent_fifty"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toXScale="@fraction/percent_two_hundred"
android:toYScale="@fraction/percent_two_hundred" />
通過代碼實作
語法
ScaleAnimation scaleAnimation = new ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
scaleAnimation.setInterpolator(Interpolator i);
scaleAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(scaleAnimation);
示例
ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setInterpolator(new AccelerateInterpolator());
scaleAnimation.setFillAfter(mIsSaveAnimationState);
scaleAnimation.setDuration(800);
mTarget.startAnimation(scaleAnimation);
TranslateAnimation
通過XML創建
語法
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="integer"
android:fillAfter="true|false"
android:fillBefore="true|false"
android:fillEnabled="true|false"
android:interpolator="@[package:]anim/interpolator_resource"
android:repeatCount="infinite|integer"
android:repeatMode="reverse|restart"
android:fromXDelta="float"
android:fromYDelta="float"
android:toXDelta="float"
android:toYDelta="float"
/>

示例
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1200"
android:fillAfter="true"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@anim/overshoot_interpolator"
android:toXDelta="0"
android:toYDelta="50%p" />
通過代碼實作
語法
TranslateAnimation translateAnimation = new TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta);
translateAnimation.setInterpolator(Interpolator i);
translateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(translateAnimation);
示例
TranslateAnimation translateAnimation = new TranslateAnimation(0f, 200f, 0f, 200f);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(mIsSaveAnimationState);
translateAnimation.setDuration(800);
mTarget.startAnimation(translateAnimation);
RotateAnimation
通過XML創建
語法
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="integer"
android:fillAfter="true|false"
android:fillBefore="true|false"
android:fillEnabled="true|false"
android:interpolator="@[package:]anim/interpolator_resource"
android:repeatCount="infinite|integer"
android:repeatMode="reverse|restart"
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float"
/>

示例
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1200"
android:fillAfter="true"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toDegrees="360" />
通過代碼實作
語法
RotateAnimation rotateAnimation = new RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
rotateAnimation.setInterpolator(Interpolator i);
rotateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(rotateAnimation);
示例
RotateAnimation rotateAnimation = new RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue);
rotateAnimation.setInterpolator(Interpolator i);
rotateAnimation.setDuration(long durationMillis);
AnimationTarget.startAnimation(rotateAnimation);
AnimationSet
通過XML創建
語法
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
<set>
...
</set>
</set>

示例
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/integer_three_thousand"
android:fillAfter="true"
android:shareInterpolator="true">
<translate
android:fromXDelta="@integer/integer_zero"
android:fromYDelta="@integer/integer_zero"
android:toXDelta="@integer/integer_zero"
android:toYDelta="@integer/integer_two_hundred" />
<alpha
android:fromAlpha="@integer/integer_one"
android:toAlpha="@fraction/scale_smaller" />
<rotate
android:fromDegrees="@integer/integer_zero"
android:pivotX="@fraction/percent_fifty"
android:pivotY="@fraction/percent_fifty"
android:toDegrees="@integer/integer_seven_hundred_and_five" />
</set>
通過代碼實作
語法
AnimationSet animationSet = new AnimationSet(boolean shareInterpolator);
animationSet.addAnimation(Animation a)
...
AnimationTarget.startAnimation(animationSet);
示例
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
alphaAnimation.setFillAfter(mIsSaveAnimationState);
alphaAnimation.setDuration(800);
ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 2f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setInterpolator(new AccelerateInterpolator());
scaleAnimation.setFillAfter(mIsSaveAnimationState);
scaleAnimation.setDuration(800);
RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setInterpolator(new AccelerateInterpolator());
rotateAnimation.setFillAfter(mIsSaveAnimationState);
rotateAnimation.setDuration(800);
AnimationSet animationSet = new AnimationSet(false);
animationSet.setFillAfter(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);
mTarget.startAnimation(animationSet);
影片監聽
Animation類通過監聽影片開始 / 結束 / 重復時刻可以進行一系列自定義操作,如跳轉頁面等等,通過在 Java 代碼里setAnimationListener()方法設定:
Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//影片開始時執行
}
@Override
public void onAnimationRepeat(Animation animation) {
//影片重復時執行
}
@Override
public void onAnimationCancel()(Animation animation) {
//影片取消時執行
}
@Override
public void onAnimationEnd(Animation animation) {
//影片結束時執行
}
});
采取上述方法監聽影片,每次監聽都必須重寫4個方法,有些時候我們只需要實作其中一個,因此其余的很累贅,我們可以采用影片配接器AnimatorListenerAdapter來針對化的實作:
// 向addListener()方法中傳入配接器物件AnimatorListenerAdapter()
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// 如想只想監聽影片開始時刻,就只需要單獨重寫該方法就可以
}
});
插值器
細心的同學觀察到以上影片屬性中幾乎都存在一個插值器屬性,插值器是影片執行速率調節器,主要用來控制影片的變化率,
常用的插值器匯總

自定義插值器
自定義插值器同樣可以有兩種方式:XML和CODE,
通過XML自定義
通過 XML 自定義插值器的時候,限制性比較大,因為系統只提供了部分插值器的自定義,如 AccelerateInterpolator,有些插值器是不支持自定義的,如 AccelerateDecelerateInterpolator,
我們看看AccelerateInterpolator如何自定義,AccelerateInterpolator中可以自定義的屬性只有:android:factor,表示加速的比率(The acceleration rate),默認值為 1,
<!-- custom accelerateInterpolator -->
<?xml version="1.0" encoding="utf-8"?>
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="4.0" />
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1200"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="false"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/custom_accelerate_interpolator"
android:toXDelta="40%p"
android:toYDelta="0" />
可以通過 XML 自定義插值器,除了 AccelerateInterpolator,還有很多,以下是具體串列:

通過代碼自定義
通過 CODE 自定義插值器就沒有那么多限制, 也很簡單,只要實作 Interpolator 介面,并實作其中的方法(getInterpolation)就好了,接下來,我們先看下 Google 官方是如何實作插值器的,
//AccelerateDecelerateInterpolator
package android.view.animation;
import android.content.Context;
import android.util.AttributeSet;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}
通過代碼可知,AccelerateDecelerateInterpolator 是通過余弦函式實作的,在 AccelerateDecelerateInterpolator 中,加速的程序是函式曲線斜率逐漸增大的程序,減速的程序是函式曲線斜率逐漸減小的程序,

明白了AccelerateDecelerateInterpolator插值器原理之后,我們用正切函式實作一個 DecelerateAccelerateInterpolator,(快-慢-快)

1.Interpolator 介面中 getInterpolation 方法中 input 的取值范圍為 [0,1],而藍色框圈出的 X 的取值范圍為 [-π/4,π/4],所以,需要將 [0,1] 轉換為 [-π/4,π/4]:
π/2 * input - π/4
2.正切函式在 [-π/4,π/4] 取值范圍內,相應的函式值的取值范圍為[-1,1],而 getInterpolation 最侄訓傳值的取值范圍為 [0,1],所以,需要將 [-1,1] 轉換為 [0,1]:
(tan(π/2 * input - π/4) + 1)/2
所以代碼實作程序:
//自定義
public class DecelerateAccelerateInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return (float) ((Math.tan(Math.PI/2 * input - Math.PI/4) + 1)/2);
}
}
//呼叫
TranslateAnimation translateAnimation = new TranslateAnimation(0f, 0f, 0f, 800f);
translateAnimation.setInterpolator(new DecelerateAccelerateInterpolator());
translateAnimation.setFillAfter(mIsSaveAnimationState);
translateAnimation.setDuration(1800);
mTarget.startAnimation(translateAnimation);
使用場景
補間影片常用于視圖View的一些標準影片效果:平移、旋轉、縮放,透明度等,除了常規的影片使用,補間影片還有一些特殊的應用場景,例如Activity和Fragment的切換動效以及視圖組(ViewGroup)中子元素的出場效果,
Activity 的切換效果
頁面進入影片:
Intent intent = new Intent (this,Acvtivity.class);
startActivity(intent);
// enter_anim(b頁面的進場影片),exit_anim(a頁面的消失影片)
// 特別注意:overridePendingTransition()必須要在startActivity(intent)后被呼叫才能生效
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
頁面退出影片:
@Override
public void finish(){
super.finish();
// 特別注意: overridePendingTransition()必須要在finish()后被呼叫才能生效
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
系統自帶的效果android.R.anim.xxx:
// 淡入淡出的影片效果
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
// 從左向右滑動的效果
overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
Fragment 切換效果
系統自帶的切換效果:
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 通過setTransition(int transit)進行設定
// transit引數說明
// 1. FragmentTransaction.TRANSIT_NONE:無影片
// 2. FragmentTransaction.TRANSIT_FRAGMENT_OPEN:標準的打開影片效果
// 3. FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:標準的關閉影片效果
// 標準影片設定好后,在Fragment添加和移除的時候都會有,
fragmentTransaction.setTransition(int transit);
自定義影片效果:
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 采用`FragmentTransavtion`的 `setCustomAnimations()`進行設定
fragmentTransaction.setCustomAnimations(R.anim.in_from_right,R.anim.out_to_left);
視圖組(ViewGroup)中子元素的出場效果
有些時候我們想為ViewGroup中的子元素的出場統一影片規則,那么我們可以這樣做:
1.定義子元素統一出場影片:
<?xml version="1.0" encoding="utf-8"?>
// 此處采用了組合影片
<set xmlns:android="http://schemas.android.com/apk/res/android" >
android:duration="3000"
<alpha
android:duration="1500"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
<translate
android:fromXDelta="500"
android:toXDelta="0"
/>
</set>
2.定義視圖組(ViewGroup)影片管理規則:
<?xml version="1.0" encoding="utf-8"?>
// 采用LayoutAnimation標簽
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
// 子元素開始影片的時間延遲
// 如子元素入場影片的時間總長設定為300ms
// 那么 delay = "0.5" 表示每個子元素都會延遲150ms才會播放影片效果
// 第一個子元素延遲150ms播放入場效果;第二個延遲300ms,以此類推
android:delay="0.5"
// 表示子元素影片的順序
// 可設定屬性為:
// 1. normal :順序顯示,即排在前面的子元素先播放入場影片
// 2. reverse:倒序顯示,即排在后面的子元素先播放入場影片
// 3. random:隨機播放入場影片
android:animationOrder="normal"
// 設定入場的具體影片效果
// 將步驟1的子元素出場影片設定到這里
android:animation="@anim/view_animation"
/>
3.為視圖組(ViewGroup)指定andorid:layoutAnimation屬性,這里有兩種方式:xml設定和code設定,
方式1:在 XML 中指定:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >
<ListView
android:id="@+id/listView1"
android:layoutAnimation="@anim/anim_layout"
// 指定layoutAnimation屬性用以指定子元素的入場影片
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
方式2:在Java代碼中指定【這樣就不用額外設定res/ anim /anim_layout.xml該xml檔案了】:
ListView lv = (ListView) findViewById(R.id.listView1);
// 加載子元素的出場影片
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_item);
// 設定LayoutAnimation的屬性
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 為ListView設定LayoutAnimation的屬性
lv.setLayoutAnimation(controller);
參考
- https://developer.android.google.cn/guide/topics/resources/animation-resource#Tween
- https://juejin.cn/post/6844903793713233927#heading-64
- https://blog.csdn.net/carson_ho/article/details/72827747
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/264855.html
標籤:其他
上一篇:axios攔截器
