天天看點

Android動畫-屬性動畫筆記

什麼是屬性動畫

見名知義,屬性動畫通過控制對象的屬性,(

修改控件的屬性值

)來實作動畫效果。屬性動畫是在Android3.0之後引進的,它非常的強大,可以比較簡單的實作許多視圖動畫做不到的事情。

在使用屬性動畫之前先來看幾個常用的View屬性成員:

  • translationX,translationY,translationZ

    :控制View的位置,值是相對于View容器左上角坐标的偏移。
  • rotationX,rotationY

    :控制相對于軸心旋轉。(

    0f-> 360f

  • x,y:控制View在容器中的位置,即左上角坐标加上translationX和translationY的值。
  • alpha:控制View對象的alpha透明度值。(

    0f-> 1f

  • 縮放:水準縮放

    scaleX

    ,垂直縮放

    scaleY

ValueAnimator 是ObjectAnimato的父類:

  • ValueAnimator ofInt (int... values)

    :傳回一個int型變化的ValueAnimator。
  • ValueAnimator ofFloat (float... values)

    :傳回一個float型變化的ValueAnimator。
  • ValueAnimator ofObject (TypeEvaluator evaluator, Object... values):

    傳回一個object型變化的ValueAnimator。
  • ValueAnimator ofArgb (int... values)

    :傳回一個顔色值變化的ValueAnimator,API

    LEVEL 21引入。

位移屬性動畫基本使用

<?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="match_parent"
    android:background="@color/colorPrimary">

    <LinearLayout
        android:id="@+id/llAddAccount"
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        android:layout_alignParentRight="true"
        android:layout_marginTop="100dp"
        android:layout_marginRight="-70dp"//将現有視圖藏在螢幕的右邊
        android:background="@drawable/bg_10_10_fff">

        <ImageView
            android:id="@+id/ivMakeNote"
            android:layout_width="35dp"
            android:layout_height="30dp"
            android:layout_gravity="center_vertical"
            android:paddingLeft="2dp"
            android:paddingTop="2dp"
            android:paddingBottom="2dp"
            android:src="@mipmap/ic_account_add" />

        <TextView
            android:id="@+id/tvAddAccount"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:paddingRight="12dp"
            android:text="添加賬戶"
            android:textColor="@color/colorPrimary"
            android:textSize="14sp" />
    </LinearLayout>
</RelativeLayout>

           
mLinearLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ObjectAnimator animator;
                if(!flag){
                    animator = ObjectAnimator.ofFloat(mLinearLayout,"translationX",0f,-70f);
                    flag = true;
                }else {
                    animator = ObjectAnimator.ofFloat(mLinearLayout,"translationX",0f,0f);
                    //animator.setInterpolator(new OvershootInterpolator());
                    flag = false;
                }
                animator.start();
            }
        });
           

通過

ObjectAnimator

的工廠方法

ofFloat

我們得到一個

ObjectAnimator

對象,并通過該對象的

start()

方法,開啟動畫效果。

ofFloat()

方法的第一個參數為要實作動畫效果的View,例如這裡整體效果的

LinearLayout

;第二個參數為屬性名,也就是前面所說的:

translationX,translationY,alpha,rotation,scaleX,scaleY

等,這裡要實作的是水準平移效果,是以我們采用了

translationX

;第三參數為可變長參數,

第一個值為動畫開始的位置,第二個值為結束值得位置

,如果數組大于3位數,那麼前者将是後者的起始位置。

translationX

translationY

這裡涉及到的位移都是相對自身位置而言。

例如: View在點A(x,y)要移動到點B(x1,y1),那麼ofFloat()方法的可變長參數,第一個值應該0f,第二個值應該x1-x。

XML布局實作:

在res/animator檔案夾下,建立animator_translation.xml檔案,内容如下:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="translationX"
    android:valueFrom="0dp"
    android:valueTo="-70dp"
    android:valueType="floatType"
/>
           
ObjectAnimator animator = AnimatorInflater.loadAnimator(this,R.animator.animator_translation);
 animator.setTarget(mLinearLayout);
 animator.start();
           

淡入淡出透明屬性動畫

mTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ObjectAnimator objectAnimator  = ObjectAnimator.ofFloat(mTextView,
                        "alpha",1f,0f,1f);
                objectAnimator.setDuration(3000);
                objectAnimator.start();
            }
        });
           

縮放屬性動畫基本使用

//第一個參數是控件,第二個是選擇什麼動畫屬性 例如 scaleX(x軸縮放),scaleY
                ObjectAnimator objectAnimator  = ObjectAnimator.ofFloat(mTextView,
                        "scaleX",1f,2f,3f,2f,1f);
                objectAnimator.setDuration(3000);//動畫持續時間
                objectAnimator.setRepeatCount(2);//動畫重複次數
                objectAnimator.setRepeatMode(ValueAnimator.REVERSE);//動畫持續模式 上一次效果反着來
                objectAnimator.start();
           

旋轉屬性動畫基本使用

//ofFloat()方法的可變長參數,如果後者的值大于前者,
                // 那麼順時針旋轉,小于前者,則逆時針旋轉。
                ObjectAnimator objectAnimator  = ObjectAnimator.ofFloat(mTextView,
                        "rotation",0f,180f,0f,-360f,0f);
                objectAnimator.setDuration(3000);//動畫持續時間
                objectAnimator.start();
           

AnimatorSet

代碼直接利用 AnimatorSet() 如果是XML定義AnimatorSet 利用AnimatorInflater.loadAnimator()加載(使用較少)
<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>
           
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.animator.property_animator);
set.setTarget(mTextView);
set.start();
           
AnimatorSet可以作用于ObjectAnimator和ValueAnimator,但通常ObjectAnimator用的比較多。

AnimatorSet的相關函數:

  • setInterpolator (TimeInterpolator interpolator)

    ,設定之後内部子動畫的插值器都是這個
  • setTarget(Object target)

    ,設定之後所有内部子動畫都作用于相同的target目标對象
  • setStartDelay(long startDelay)

    ,它不會覆寫子動畫開始延遲,隻對AnimatorSet的開始時間起作用,是以它會延後AnimatorSet激活整體動畫的開始時間
  • cancle()

    取消動畫,會取消AnimatorSet中的所有子動畫。
  • end()

    結束動畫,會結束AnimatorSet中的所有子動畫。
  • getChildAnimations()

    擷取所有受AnimatorSet控制的動畫
  • isStarted()

    ,AnimationSet動畫是否開始了,true開始
  • isRunning()

    ,AnimationSet開始之後(isStarted =

    true),内部是否有子動畫正在執行,有動畫執行傳回true

  • pause()

    暫停動畫,
  • resume()

    恢複暫停的動畫
  • play(Animator anim)

    擷取Builder對象

函數

playTogether

playSequentially

的差別:

animatorSet.playTogether():

多個動畫同時執行,可以是對一個對象執行的多個動畫,也可以是對多個對象的多個動畫。

playTogether(Collection<Animator> items)

//利用集合添加動畫

playTogether(Animator... items)

//利用可變參數添加動畫
ObjectAnimator objectAnimator1 = ObjectAnimator.ofArgb(mTextView, "backgroundColor", Color.WHITE, Color.GREEN);
                ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(mTextView, "scaleX", 0.1f, 1.2f);
                ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(mTextView, "scaleY", 0.5f, 1.0f);
                ObjectAnimator objectAnimator4 = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 50);

                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4);
                animatorSet.setDuration(3000);
                animatorSet.start();
           
效果和animatorSet.playTogether()差不多
 PropertyValuesHolder holder1=PropertyValuesHolder.ofFloat("rotation",0f,360f,0f);
                PropertyValuesHolder holder2=PropertyValuesHolder.ofFloat("translationX",0f,600f);
                PropertyValuesHolder holder3=PropertyValuesHolder.ofFloat("alpha",1f,0f,1f);
                ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(sun,holder1,holder2,holder3);
                objectAnimator.setDuration(3000);
                objectAnimator.setInterpolator(new OvershootInterpolator());//插值器
                objectAnimator.start();
           
如果多個動畫同時對控件的同一個屬性進行操作,會按照playTogether添加的最後一個動畫覆寫前面操作相同屬性的動畫,也可能沒有覆寫,但确實是最後一個添加的動畫起了作用。

playSequentially

順序播放動畫:

playSequentially(List<Animator> items)

playSequentially(Animator... items)

playSequentially

是一個動畫執行完後執行下一個動畫,但如果前一個動畫是無限循環,下一個動畫永遠不會執行。
ObjectAnimator objectAnimator5 = ObjectAnimator.ofFloat(mTextView, "translationY", 200, 450);
                ObjectAnimator objectAnimator6 = ObjectAnimator.ofFloat(mTextView, "translationY", 300, 600);
                ObjectAnimator objectAnimator1 = ObjectAnimator.ofArgb(mTextView, "backgroundColor", Color.WHITE, Color.GREEN);
                ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(mTextView, "scaleX", 0.1f, 1.2f);
                ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(mTextView, "scaleY", 0.5f, 1.0f);
                ObjectAnimator objectAnimator4 = ObjectAnimator.ofFloat(mTextView, "translationY", 0, 250);
                AnimatorSet animatorSet = new AnimatorSet();
                //按順序執行動畫
                animatorSet.playSequentially(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4,objectAnimator5,objectAnimator6);
                animatorSet.setDuration(5000);
                animatorSet.start();
           
//實作動畫無限循環
                objectAnimator1.setRepeatCount(-1);
                animatorSet.playTogether(objectAnimator1,objectAnimator2,objectAnimator3,objectAnimator4,objectAnimator5,objectAnimator6);
           

利用play(Animator)建構Builder對象

Builder play(Animator anim);生成builder對象,Builder能夠控制動畫的執行順序和互相之間的依賴。

Builder的函數:

  • public Builder with(Animator anim)

    和前面動畫一起執行
  • public Builder before(Animator anim)

    執行前面的動畫後再執行該動畫
  • public Builder after(Animator anim)

    先執行這個動畫再執行前面動畫
  • public Builder after(long delay)

    延遲n毫秒之後執行動畫
//按函數性質執行動畫
                animatorSet.play(objectAnimator1).with(objectAnimator2).before(objectAnimator3);
                animatorSet.setDuration(5000);
                animatorSet.start();
                   /*
                * 鍊式調用動畫執行順序總結如下:
                    Builder鍊式調用中會先執行after函數中的動畫(有多個同時執行),
                * 然後執行play和with函數(有多個同時執行)中的動畫,
                * 最後執行before函數中的動畫(有多個同時執行)
                * */

           
動畫監聽
animatorSet.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        
    }

    @Override
    public void onAnimationEnd(Animator animation) {

    }

    @Override
    public void onAnimationCancel(Animator animation) {

    }

    @Override
    public void onAnimationRepeat(Animator animation) {

    }
});

//需要api19
animatorSet.addPauseListener(new Animator.AnimatorPauseListener() {
    @Override
    public void onAnimationPause(Animator animation) {
        
    }

    @Override
    public void onAnimationResume(Animator animation) {

    }
});

           

ViewPropertyAnimator 動畫

//ViewPropertyAnimator 動畫,隻是針對View對象的特定屬性同時播放動畫
                /*
                * 支援屬性:
                    translationX、translationY、translationZ
                    x、y、z
                    alpha
                    scaleX、scaleY
                *
                * */
                mTextView.animate().translationX(100f).translationY(100f).translationZ(20f).
                        setInterpolator(new OvershootInterpolator()).start();
           
//ViewPropertyAnimator 動畫,隻是針對View對象的特定屬性同時播放動畫
                ViewPropertyAnimator viewPropertyAnimator  = mTextView.animate();
                viewPropertyAnimator.setDuration(2000);
                viewPropertyAnimator.translationX(100f);//點選一次向右偏移,之後點選無效果(一次性)
               //viewPropertyAnimator.translationXBy(100f);//每次點選都會向右偏移,重複利用
                viewPropertyAnimator.start();
           

估值器與插值器

插值器:根據時間流逝的百分比計算出目前屬性值改變的百分比。

正常情況下,預設的插值器已經夠用,如果自己數學厲害,想顯擺一下,

也是通過實作TimeInterpolator接口的getInterpolation()自定義

的。

Android動畫-屬性動畫筆記
/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
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
     * @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.
     */
    float getInterpolation(float input);
}

           

估值器:根據目前屬性改變的百分比來計算改變後的屬性值,移動的位置;

自定義估值器:抛物線

//自定義估值器,重寫計算規則,多利用數學公式
    class BallDownEvaluator implements TypeEvaluator<PointF>{

        /** * @param fraction 動畫執行了的百分比,
         *  * @param startValue 點的起始值 *
         *  @param endValue 點的最終值 * @return
         *  */
        @Override
        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
            //以後可以利用估值器實作更多的曲線動畫,比如貝塞爾、正弦餘弦動畫等。
            //抛物線方程式 s=1/2*g*t*t
            //定義初速度150,加速g=9.8,時間t=5s,就是動畫設定的執行時間
            float time = 5* fraction* 1.0f;
            PointF pointF = new PointF();
            pointF.x = 150 * time;
            pointF.y = 0.5f * 98.0f *time *time;
            return pointF;
        }
    }
           
mTextView.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View view) {
                //ValueAnimator 是  ObjectAnimator 的父類
                //太陽下山抛物線
                ObjectAnimator objectAnimator  = new ObjectAnimator();
                BallDownEvaluator ball = new BallDownEvaluator();
                objectAnimator.setDuration(5000);
                //1.先設定value  位置(0,0)
                objectAnimator.setObjectValues(new PointF(0,0));
                //2.再設定估值器
                objectAnimator.setEvaluator(ball);
                objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        PointF pointF = (PointF) valueAnimator.getAnimatedValue();
                        //通過不斷的改變view(sun)的坐标來實作抛物線動畫
                        sun.setX(pointF.x);
                        sun.setY(pointF.y);
                    }
                });
                objectAnimator.start();
            }
        });
           

keyFrame(關鍵幀)

假如覺得自定義自定義插值器或估值器有難度,也可以使用關鍵幀

Keyframe對象

來實作

。Keyframe

讓我們可以指定某個屬性百分比時對象的屬性值。

Keyframe start = Keyframe.ofFloat(0f,0f);
                Keyframe middle1 = Keyframe.ofFloat(0.3f,300f);
                Keyframe middle2 = Keyframe.ofFloat(0.7f,600f);
                Keyframe end = Keyframe.ofFloat(1f,900f);
                PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",
                        start,middle1,middle2,end);
                ObjectAnimator.ofPropertyValuesHolder(sun,holder).setDuration(3000).start();

           

連結:各種動畫合集

連結:新小夢 https://juejin.cn/post/6846687601118691341