Android的屬性動畫(Property Animation)詳細教程

1. 簡介

在引入屬性動畫以前,Android已經有了Tween animation(補間動畫)。引入新的Animator的緣由是由於補間動畫有不少侷限性。 補間動畫的分類:java

  1. AlphaAnimation
  2. ScaleAnimation
  3. TranslateAnimation
  4. RotateAnimation
  5. AnimationSet

補間動畫的侷限性:android

  1. 可設置動畫的對象有限
  2. 顯示區域和可點擊區域可能會不一樣

爲了補充和解決上述補間動畫的不足,因此在Android3的時候引入了ValueAnimatorObjectAnimatorgit

2. 屬性動畫

2.1 屬性動畫的結成結構

序號 類名 說明
1 Animator 全部 Animator 的父類,主要用於定義通用的接口
2 AnimatorSet 多個屬性動畫組合
3 ValueAnimator 主要用於根據起始值和終止值產生動畫,只負責產生在起始值和終止值之間的值
4 ObjectAnimator 主要用於根據起始值和終止值產生動畫,並將動畫產生的值設置在目標對象上
5 TimeAnimator 提供了一個簡單的回調機制,經過 TimeAnimator.TimeListener,在動畫的每一幀處通知你。(本文不會介紹)

繼承結構:github

2.2 定義屬性動畫方法

跟補間動畫同樣,可使用Xml和Code的方式建立動畫。canvas

在animation文件夾中建立xml動畫文件。(若是沒有animation文件夾,須要手動建立)ide

<objectAnimator android:duration="int" android:interpolator="@[package:]anim/interpolator_resource" android:propertyName="string" android:valueType=["intType" | "floatType"] android:valueFrom="float | int | color" android:valueTo="float | int | color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["repeat" | "reverse"] />
複製代碼
屬性 含義 取值範圍
duration 動畫執行時間 整型,須要大於0。
interpolator 差值器,改變更畫或者數值變化的速率 安卓自帶,自定義
propertyName 動畫目標對象要改變的屬性 字符串,填入屬性名稱
valueType 值類型 整點型,浮點型。當屬性動畫值的類型爲顏色值時能夠省略
valueFrom 動畫值的起始值 浮點數,整型數或者顏色值。當爲顏色值時,必須符合顏色的定義方式(# + 六位十六進制數)
valueTo 動畫值的結束值 浮點數,整型數或者顏色值。當爲顏色值時,必須符合顏色的定義方式(# + 六位十六進制數)
startOffset 動畫開始偏移時間 整型數,默認爲 0。當爲負數時,效果和默認值同樣
repeatCount 動畫重複的次數 整型數字,默認爲 0。當爲負數時,表示無限循環
repeatMode 下一次動畫執行的方式 默認:從新開始播放。還有倒播
示例
//1. 在animation文件夾中建立xml文件 
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:propertyName="rotation"
    android:valueType="floatType"
    android:valueFrom="0"
    android:valueTo="360"
    android:startOffset="0"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    />
    
//2. 在代碼中導入動畫xml
val objectAnimator = AnimatorInflater.loadAnimator(this, R.animator.object_animator) as ObjectAnimator
objectAnimator.setTarget(target)
objectAnimator.start()

複製代碼

代碼的建立方式在下面介紹。post

3. ValueAnimator

ValueAnimator正如其名字,它是關於數字的計算動畫。它不會與View直接交互,而是經過ValueAnimator#addUpdateListener來設置動畫效果。好比在TextView上顯示從0到50時,就能夠用ValueAnimator測試

3.1 ValueAnimator的建立

在代碼中建立ValueAnimator動畫

// 建立Animator,同時傳入取值範圍
val animator = ValueAnimator.ofFloat(0F, 5000F) 
// 設置差值器, 這裏選擇的是線性差值器(默認)
animator.interpolator = LinearInterpolator()
// 設置動畫執行時間
animator.setDuration(2000)
複製代碼

3.2 添加UpdateListener

在ValueAnimator中有數值變化時,會調用onAnimationUpdate接口。因此咱們須要實現這個Listener。ui

animator.addUpdateListener {
    // 在TextView中更新text
    binding.textView.text = ((it.animatedValue as Float).toInt() / 100).toString()
    // 把值傳入自定義貝塞爾View,是其產生動畫效果
    binding.bezierView.setValue(it.animatedValue as Float)
}
複製代碼

自定義貝塞爾View的源碼以下。 關於貝塞爾曲線,能夠參考個人另外一篇文章。

class BezierView : View {

    private var path: Path = Path()

    private lateinit var paint: Paint = Paint()

    private var h: Int = 0
    private var w: Int = 0

    private var controlPoint1: PointF = PointF()
    private var controlPoint2: PointF = PointF()

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet)

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        // 設置當前view的高和寬
        this.h = h
        this.w = w

        controlPoint1 = PointF(this.w.toFloat() / 4, 0F)
        controlPoint2 = PointF(this.w.toFloat() / 4 * 3, this.h.toFloat())
    }

    fun setValue(degree: Float) {
        val controlY = degree / 5000 * h

        controlPoint1 = PointF(this.w.toFloat() / 4, controlY)
        controlPoint2 = PointF(this.w.toFloat() / 4 * 3, this.h.toFloat() - controlY)

        invalidate()
    }
    
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        // 重置path, 爲的是防止重複繪製貝塞爾曲線,使畫布上殘留多條曲線
        path.reset()

        // 配置畫筆paint
        paint.color = context.getColor(R.color.colorAccent)
        paint.strokeWidth = 2F
        paint.style = Paint.Style.STROKE

        // 設置左右兩個基準點
        val pointLeft = PointF(0F, h / 2.toFloat())
        val pointRight = PointF(w.toFloat(), h / 2.toFloat())

        // 繪製左右基準點
        canvas?.drawPoint(pointLeft.x, pointLeft.y, paint)
        canvas?.drawPoint(pointRight.x, pointRight.y, paint)

        paint.color = context.getColor(R.color.colorPrimaryDark)

        // 爲了繪製貝塞爾曲線,須要移動到其中一個基準點
        path.moveTo(pointLeft.x, pointLeft.y)

        // 根據基準點和控制點,繪製貝塞爾曲線
        path.cubicTo(
            controlPoint1.x,
            controlPoint1.y,
            controlPoint2.x,
            controlPoint2.y,
            pointRight.x,
            pointRight.y
        )

        // 在畫布上畫path
        canvas?.drawPath(path, paint)
    }

}
複製代碼

3.3 播放動畫

最後須要在合適的節點開始播放動畫。

// 播放動畫
animator.start()
// 動畫暫定
animator.pause()
// 播放結束
animator.end()
複製代碼

4. ObjectAnimator

ValueAnimator同樣的方式建立ObjectAnimator

// 建立動畫,
val objectAnimator = ObjectAnimator.ofFloat(binding.imageView, "rotation", 0F, 360F, 0F)
// 設置動畫執行時間
objectAnimator.setDuration(2000)
// 開始播放
objectAnimator.start()
複製代碼

建立動畫時的第一個傳參是被動畫對象View,第二個是properyName,第三個是變長參數的起始數值。

其中propertyName的類型一共有如下幾種:

名稱 做用
alpha 透明化
rotation 旋轉
rotationX 以X軸旋轉
rotationY 以Y軸旋轉
translationX 以X軸平移
translationY 以Y軸平移
scaleX 以X軸拉伸
scaleY 以Y軸拉伸

5. 屬性動畫的Listener

屬性動畫的監聽器一共有三種。

  1. AnimatorListener
  2. AnimatorPauseListener
  3. AnimatorUpdateListener

5.1 AnimatorListener

AnimatorListener的監聽器主要監聽的是屬性動畫的開始,結束,取消,重複。

public static interface AnimatorListener{
    void onAnimationStart(Animator animation, boolean isReverse) {}
    
    void onAnimationEnd(Animator animation, boolean isReverse) {}
    
    void onAnimationCancel(Animator animation, boolean isReverse) {}
    
    void onAnimationRepeat(Animator animation, boolean isReverse) {}
}
複製代碼

5.2 AnimatorPauseListener

AnimatorPauseListener的監聽器主要監聽的是屬性動畫的暫停,恢復狀態。

public static interface AnimatorPauseListener {
    void onAnimationPause(Animator animation);
    
    void onAnimationResume(Animator animation);
}
複製代碼

5.3 AnimatorUpdateListener

AnimatorUpdateListener的監聽器主要監聽的是屬性動畫中值得變化。

public static interface AnimatorUpdateListener {
    void onAnimationUpdate(ValueAnimator animation);
}
複製代碼

6. 插值器(Interpolator)

6.1 插值器類型

屬性動畫的Interpolator和補間動畫的的是同樣的。 插值器一共有一下幾種:

名稱 做用
AccelerateDecelerateInterpolator 先加速,後減速
AccelerateInterpolator 一直加速
AnticipateInterpolator 迂迴,加速
OvershootInterpolator 加速超出,返回終點
AnticipateOvershootInterpolator 迂迴,加速超出,返回終點
BounceInterpolator 彈簧效果
CycleInterpolator 正弦曲線
DecelerateInterpolator 一直減速
LinearInterpolator 線性速度

6.2 自定義插值器

若是上面的插值器不符合要求能夠自定義一個新的插值器。 自定義插值器時須要繼承BaseInterpolator。 重寫public float getInterpolation(float input)

線性插值器是以下的代碼,能夠做爲參考。

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

    public float getInterpolation(float input) {
        return input;
    }

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

複製代碼

7. 自定義估值器(TypeEvaluator)

估值器是用於計算ValueAnimator中的數值變化。ValueAnimator的默認的估值器是線性的。 若是想要自定義估值器須要繼承TypeEvaluator,以及重寫public T evaluate(float fraction, T startValue, T endValue);

示例以下:

class CustomTypeEvaluator : TypeEvaluator<Float> {
    override fun evaluate(fraction: Float, startValue: Float?, endValue: Float?): Float {
        return fraction * abs(endValue ?: 0F - startValue!!)
    }
}
複製代碼

8. Github

本文的示例: github.com/HyejeanMOON…

其餘教程:
Google的MergeAdapter的使用: juejin.im/post/5e903f…
Paging在Android中的應用: juejin.im/post/5e75db…
Android UI測試之Espresso: juejin.im/post/5e6caa…
Android ConstraintLayout的易懂教程: juejin.im/post/5ea50a…
在RecyclerView中能夠應對多個ViewType的庫--Groupie: juejin.im/post/5e9059…

相關文章
相關標籤/搜索