動(dòng)畫:Interpolator插值器使用和自定義詳解
前言
屬性動(dòng)畫可以對(duì)某個(gè)屬性做動(dòng)畫,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色;
那么今天我們了解下 插值器TimeInterpolator;
一、插值器介紹
1、Interpolator有什么用
- Interpolator 被用來修飾動(dòng)畫效果,定義動(dòng)畫的變化率;
 - 在Android源碼中對(duì)應(yīng)的接口類為TimeInterpolator,通過輸入均勻變化的0~1之間的值;
 - 可以得到勻速、正加速、負(fù)加速、無規(guī)則變加速等0~1之間的變化曲線;
 
2、應(yīng)用場(chǎng)景
- 實(shí)現(xiàn)非線性運(yùn)動(dòng)的動(dòng)畫效果;
 - 非線性運(yùn)動(dòng)是指動(dòng)畫改變的速率不是一成不變的,如加速、減速運(yùn)動(dòng)的動(dòng)畫效果;
 - 實(shí)現(xiàn)復(fù)雜的曲線動(dòng)畫,回彈效果自定義等都需要自定義插值器;
 
3、Android系統(tǒng)提供的插值器類型
- AccelerateDecelerateInterpolator 在動(dòng)畫開始與結(jié)束比較慢,中間加速
 - AccelerateInterpolator 加速
 - AnticipateInterpolator 開始的時(shí)候向后然后向前甩
 - AnticipateOvershootInterpolator 開始的時(shí)候向后然后向前甩一定值后返回最后的值
 - BounceInterpolator 動(dòng)畫結(jié)束的時(shí)候彈起
 - CycleInterpolator 動(dòng)畫循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線
 - DecelerateInterpolator 減速
 - LinearInterpolator 以常量速率改變
 - OvershootInterpolator 向前甩一定值后再回到原來位置
 
二、插值器應(yīng)用
插值器的使用有兩種方式:在XML和代碼中使用
1、xml
XML動(dòng)畫文件使用插值器時(shí),需要設(shè)置系統(tǒng)設(shè)置的對(duì)應(yīng)的插值器資源ID
- <?xml version="1.0" encoding="utf-8"?>
 - <set xmlns:android="http://schemas.android.com/apk/res/android">
 - <alpha
 - android:fromAlpha="1.0"
 - android:toAlpha="0.1"
 - android:duration="2000"
 - android:repeatMode="reverse"
 - android:repeatCount="infinite"
 - android:interpolator="@android:anim/linear_interpolator"/>
 - </set>
 
2、代碼中使用
- 代碼使用插值器時(shí),只需創(chuàng)建對(duì)應(yīng)的插值器對(duì)象,然后設(shè)置給動(dòng)畫對(duì)象;也可以加載xml文件中配置的插值器;
 - 利用view的setInterpolator(Context context, @AnimRes @InterpolatorRes int resID)設(shè)置插值器;
 
- //創(chuàng)建一個(gè)漸變透明度的動(dòng)畫,從透明到完全不透明
 - AlphaAnimation alphaAnimation = new AlphaAnimation(0.1f, 1.0f);
 - //設(shè)置動(dòng)畫時(shí)長(zhǎng)
 - alphaAnimation.setDuration(5000);
 - //設(shè)置動(dòng)畫重復(fù)方式
 - alphaAnimation.setRepeatMode(AlphaAnimation.REVERSE);
 - //設(shè)置動(dòng)畫播放次數(shù)
 - alphaAnimation.setRepeatCount(AlphaAnimation.INFINITE);
 - //設(shè)置勻速插值器
 - alphaAnimation.setInterpolator(new LinearInterpolator());
 - //為View開啟指定類型動(dòng)畫
 - imageView.startAnimation(alphaAnimation)
 
- 使用Android內(nèi)置的插值器能滿足大多數(shù)的動(dòng)畫需求;
 - 如果系統(tǒng)提供的插值器無法滿足需求,還可以自定義插值器;
 
三、自定義插值器
1、實(shí)現(xiàn)方式
- 自定義插值器需要實(shí)現(xiàn)Interpolator或TimeInterpolator接口,并復(fù)寫getInterpolation()方法;
 - 補(bǔ)間動(dòng)畫 實(shí)現(xiàn) Interpolator接口;屬性動(dòng)畫實(shí)現(xiàn)TimeInterpolator接口;
 - TimeInterpolator接口是屬性動(dòng)畫中新增的,用于兼容Interpolator接口,這使得所有過去的Interpolator實(shí)現(xiàn)類都可以直接在屬性動(dòng)畫使用;
 
Interpolator接口和TimeInterpolator接口說明如下:
- // Interpolator接口
 - public interface Interpolator {
 - // 內(nèi)部只有一個(gè)方法:getInterpolation()
 - float getInterpolation(float input) {
 - // 參數(shù)說明
 - // input值值變化范圍是0-1,且隨著動(dòng)畫進(jìn)度(0% - 100% )均勻變化
 - // 即動(dòng)畫開始時(shí),input值 = 0;動(dòng)畫結(jié)束時(shí)input = 1
 - // 而中間的值則是隨著動(dòng)畫的進(jìn)度(0% - 100%)在0到1之間均勻增加
 - ...// 插值器的計(jì)算邏輯
 - return xxx;
 - // 返回的值就是用于估值器繼續(xù)計(jì)算的fraction值,下面會(huì)詳細(xì)說明
 - }
 - // TimeInterpolator接口
 - public interface TimeInterpolator {
 - float getInterpolation(float input){
 - // input值值變化范圍是0-1,且隨著動(dòng)畫進(jìn)度(0% - 100% )均勻變化
 - ...// 插值器的計(jì)算邏輯
 - };
 - }
 
自定義插值器的關(guān)鍵在于:對(duì)input值根據(jù)動(dòng)畫的進(jìn)度(0%-100%)通過邏輯計(jì)算從而計(jì)算出當(dāng)前屬性值改變的百分比;
2、自定義插值器
寫一個(gè)自定義Interpolator:先減速后加速
- /*
 - * 根據(jù)需求實(shí)現(xiàn)Interpolator接口
 - * TestInterpolator.java
 - */
 - public class TestInterpolator implements TimeInterpolator {
 - @Override
 - public float getInterpolation(float input) {
 - float result;
 - if (input <= 0.5) {
 - result = (float) (Math.sin(Math.PI * input)) / 2;
 - // 使用正弦函數(shù)來實(shí)現(xiàn)先減速后加速的功能,邏輯如下:
 - // 因?yàn)檎液瘮?shù)初始弧度變化值非常大,剛好和余弦函數(shù)是相反的
 - // 隨著弧度的增加,正弦函數(shù)的變化值也會(huì)逐漸變小,這樣也就實(shí)現(xiàn)了減速的效果。
 - // 當(dāng)弧度大于π/2之后,整個(gè)過程相反了過來,現(xiàn)在正弦函數(shù)的弧度變化值非常小,漸漸隨著弧度繼續(xù)增加,變化值越來越大,弧度到π時(shí)結(jié)束,這樣從0過度到π,也就實(shí)現(xiàn)了先減速后加速的效果
 - } else {
 - result = (float) (2 - Math.sin(Math.PI * input)) / 2;
 - }
 - return result;
 - // 返回的result值 = 隨著動(dòng)畫進(jìn)度呈先減速后加速的變化趨勢(shì)
 - }
 - }
 - /*
 - * 步驟設(shè)置使用
 - * Test.java
 - */
 - // 創(chuàng)建動(dòng)畫作用對(duì)象:此處以Button為例
 - mButton = (Button) findViewById(R.id.Button);
 - // 獲得當(dāng)前按鈕的位置
 - float curTranslationX = mButton.getTranslationX();
 - // 創(chuàng)建動(dòng)畫對(duì)象 & 設(shè)置動(dòng)畫
 - ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
 - // 表示的是:
 - // 動(dòng)畫作用對(duì)象是mButton
 - // 動(dòng)畫作用的對(duì)象的屬性是X軸平移
 - // 動(dòng)畫效果是:從當(dāng)前位置平移到 x=1500 再平移到初始位置
 - // 設(shè)置步驟1中設(shè)置好的插值器:先減速后加速
 - animator.setInterpolator(new TestInterpolator());
 - // 啟動(dòng)動(dòng)畫
 - animator.start();
 
3、貝塞爾曲線的插值器
(1)先使用貝塞爾曲線數(shù)值生成工具來獲取想要的曲線數(shù)值
- 工具網(wǎng)站:https://cubic-bezier.com/;
 - 拉拽左邊圖像的2個(gè)點(diǎn),調(diào)整出符合效果的圖形;
 - 點(diǎn)擊Go按鍵,可看到紅色與藍(lán)色的方塊運(yùn)動(dòng)狀態(tài),調(diào)節(jié)自己想要的效果;
 - 將4個(gè)參數(shù)運(yùn)用到下面的代碼中;
 
(2)代碼運(yùn)用
- ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
 - EaseCubicInterpolator interpolator = new EaseCubicInterpolator(0.31f, 0.85f,0.77f, 0.14f);
 - animator.setInterpolator(interpolator)
 
(3)貝塞爾曲線插值器
- import android.graphics.PointF;
 - import android.view.animation.Interpolator;
 - /**
 - * 緩動(dòng)三次方曲線插值器.(基于三次方貝塞爾曲線)
 - */
 - public class EaseCubicInterpolator implements Interpolator {
 - private final static int ACCURACY = 4096;
 - private int mLastI = 0;
 - private final PointF mControlPoint1 = new PointF();
 - private final PointF mControlPoint2 = new PointF();
 - /**
 - * 設(shè)置中間兩個(gè)控制點(diǎn)
 - *
 - * 在線工具: http://cubic-bezier.com
 - *
 - * @param x1
 - * @param y1
 - * @param x2
 - * @param y2
 - */
 - public EaseCubicInterpolator(float x1, float y1, float x2, float y2) {
 - mControlPoint1.x = x1;
 - mControlPoint1.y = y1;
 - mControlPoint2.x = x2;
 - mControlPoint2.y = y2;
 - }
 - @Override
 - public float getInterpolation(float input) {
 - float t = input;
 - // 近似求解t的值[0,1]
 - for (int i = mLastI; i < ACCURACY; i++) {
 - t = 1.0f * i / ACCURACY;
 - double x = cubicCurves(t, 0, mControlPoint1.x, mControlPoint2.x, 1);
 - if (x >= input) {
 - mLastI = i;
 - break;
 - }
 - }
 - double value = cubicCurves(t, 0, mControlPoint1.y, mControlPoint2.y, 1);
 - if (value > 0.999d) {
 - value = 1;
 - mLastI = 0;
 - }
 - return (float) value;
 - }
 - /**
 - * 求三次貝塞爾曲線(四個(gè)控制點(diǎn))一個(gè)點(diǎn)某個(gè)維度的值.<br>
 - * <p>
 - *
 - * @param t 取值[0, 1]
 - * @param value0
 - * @param value1
 - * @param value2
 - * @param value3
 - * @return
 - */
 - public static double cubicCurves(double t, double value0, double value1,
 - double value2, double value3) {
 - double value;
 - double u = 1 - t;
 - double tt = t * t;
 - double uu = u * u;
 - double uuu = uu * u;
 - double ttt = tt * t;
 - value = uuu * value0;
 - value += 3 * uu * t * value1;
 - value += 3 * u * tt * value2;
 - value += ttt * value3;
 - return value;
 - }
 - }
 
總結(jié)
要實(shí)現(xiàn)復(fù)雜的動(dòng)畫效果時(shí),就要自定義插值器,其實(shí)插值器還是的學(xué)習(xí)算法;
大家一起加油;
















 
 
 







 
 
 
 