android之屬性動畫和佈局動畫詳解

1、屬性動畫是什麼不廢話,不懂的可以百度一下參考郭霖大神的動畫詳解篇;這裏僅僅提供一個demo說說用法,拋磚引玉,代碼的註釋寫的已經很詳細,不再多說廢話,一下提供的是一個基礎的demo,講解的是objectAnimator的基礎用法,如平移、旋轉、縮放、漸變以及動畫的集合;至於objectAnimator(必須的有set get方法)和valueAnimator的詳細區別也可參考郭霖大神的動畫詳解篇

2、除此基本用法,還有估值器和插值器

  (1)插值器:動畫速率的變換,有點類似物理的加速度,就是該變動畫的速率的

  (2)估值器:一般配合插值器使用,插值器返回因子,起始值,結束值給估值器,估值器根據這個區間數據生成這個區間連續的數值,而這寫數值就是動畫需要的屬性的值,使動畫平滑過渡比如:ValueAnimator.ofFloat()方法就是實現了初始值與結束值之間的平滑過度,那麼這個平滑過度是怎麼做到的呢?其實就是系統內置了一個FloatEvaluator,它通過計算告知動畫系統如何從初始值過度到結束值

這是摘抄自網絡

我們都知道對於屬性動畫可以對某個屬性做動畫,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色,下面先了解下TimeInterpolator和TypeEvaluator。

TimeInterpolator(時間插值器):

  1. 作用:根據時間流逝的百分比計算出當前屬性值改變的百分比。
  2. 系統已有的插值器: 
    ①LinearInterpolator(線性插值器):勻速動畫。 
    ②AccelerateDecelerateInterpolator(加速減速插值器):動畫兩頭慢,中間快。 
    ③DecelerateInterpolator(減速插值器):動畫越來越慢。

TypeEvaluator(類型估值算法,即估值器):

  1. 作用:根據當前屬性改變的百分比來計算改變後的屬性值。
  2. 系統已有的估值器: 
    ①IntEvaluator:針對整型屬性 
    ②FloatEvaluator:針對浮點型屬性 
    ③ArgbEvaluator:針對Color屬性

那麼TimeInterpolator和TypeEvaluator是怎麼協同工作的呢?

答:它們是實現非勻速動畫的重要手段。屬性動畫是對屬性做動畫,屬性要實現動畫,首先由TimeInterpolator(插值器)根據時間流逝的百分比計算出當前屬性值改變的百分比,並且插值器將這個百分比返回,這個時候插值器的工作就完成了。比如插值器返回的值是0.5,很顯然我們要的不是0.5,而是當前屬性的值,即當前屬性變成了什麼值,這就需要估值器根據當前屬性改變的百分比來計算改變後的屬性值,根據這個屬性值,我們就可以設置當前屬

做了一凡說明,直接上代碼:

package com.example.administrator.animationdemo;

import android.animation.FloatEvaluator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.TextView;

public class PropertyAnimatorAct extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    private TextView tvExeAnima;
    private TextView tvAlpha;
    private TextView tvScale;
    private TextView tvTranslation;
    private TextView tvRotation;
    private TextView tvSet;

    private ObjectAnimator objAnimator;

    public static void startInstance(Context context) {
        context.startActivity(new Intent(context, PropertyAnimatorAct.class));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.property_animator_layout);
        initView();
        initListener();
    }

    private void initListener() {
        tvAlpha.setOnClickListener(this);
        tvRotation.setOnClickListener(this);
        tvScale.setOnClickListener(this);
        tvTranslation.setOnClickListener(this);
        tvSet.setOnClickListener(this);
    }

    private void initView() {
        tvAlpha = (TextView) findViewById(R.id.tv_alpha);
        tvExeAnima = (TextView) findViewById(R.id.tv_execute_animtor);
        tvRotation = (TextView) findViewById(R.id.tv_rotation);
        tvScale = (TextView) findViewById(R.id.tv_scale);
        tvSet = (TextView) findViewById(R.id.tv_set);
        tvTranslation = (TextView) findViewById(R.id.tv_translation);
    }

    @Override
    public void onClick(View v) {
        if (v == tvAlpha) {// 漸變
            startAlphaAnimtor();
        } else if (v == tvSet) {//集合
            startSetAnimtor();
        } else if (v == tvRotation) {//旋轉
            startRotationAnimtor();
        } else if (v == tvScale) {//縮放
            startScaleAnimtor();
        } else if (v == tvTranslation) {//平移
            startTranslationAnimtor();
        }
    }

    /**
     * 漸變 從0-1-0-1變化,完全透明到不透明再到完全透明再到不透明
     * tvExeAnima 漸變的對象
     * alpha 漸變的set屬性
     * 後邊是可變參數,變化過程從這取值
     */
    private void startAlphaAnimtor() {
        objAnimator = ObjectAnimator.ofFloat(tvExeAnima, "alpha", 0.0f, 1.0f, 0.5f, 1f);
        objAnimator.setDuration(500);
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.d(TAG, "alpha = " + animation.getAnimatedValue());
            }
        });
        objAnimator.start();
    }

    /**
     * 縮放
     **/
    private void startScaleAnimtor() {
        objAnimator = ObjectAnimator.ofFloat(tvExeAnima, "scaleX", 0.0f, 1f, 0.5f, 1.0f);
        objAnimator.setDuration(500);
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.d(TAG, "scaleX = " + animation.getAnimatedValue());
            }
        });
        objAnimator.start();
    }

    /**
     * 平移
     */
    private void startTranslationAnimtor() {

        objAnimator = ObjectAnimator.ofFloat(tvExeAnima, "TranslationX", 100f, 50f, 60.0f, 150f);
        objAnimator.setDuration(500);
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.d(TAG, "TranslationX = " + animation.getAnimatedValue());
            }
        });
        objAnimator.start();
    }

    /**
     * 旋轉
     **/
    private void startRotationAnimtor() {
        objAnimator = ObjectAnimator.ofFloat(tvExeAnima, "Rotation", 100f, 50f, 60.0f, 150f, 300f);
        objAnimator.setDuration(500);
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.d(TAG, "Rotation = " + animation.getAnimatedValue());
            }
        });
        objAnimator.start();
    }

    /**
     * 動畫集合,一起播放
     * r如:平移過程中同時改變X y周縮放並且旋轉
     */
    private void startSetAnimtor() {
        //        PropertyValuesHolder pvhTransX = PropertyValuesHolder.ofFloat("translationX", 300f);
        PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 0.5f);
        PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.5f, 1f);
        PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofFloat("Rotation", 100f, 300f, 360f);
        objAnimator = ObjectAnimator.ofPropertyValuesHolder(tvExeAnima, /*pvhTransX,*/ pvhScaleX, pvhScaleY, pvhRotation);
        objAnimator.setDuration(500);
        //插值器,動畫速率變換,類似物理的加速度,改變動畫的速度,可以自定義也可以使用系統的
        objAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        //變速的估值器,一個時間段估值,系統默認的也是FloatEvaluator,使OfFloat能夠平滑
        objAnimator.setEvaluator(new FloatEvaluator());
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Log.d(TAG, "Set = " + animation.getAnimatedValue());//打印的是第一個位置參數的動畫的變化值
            }
        });
        objAnimator.start();
    }
}


二 屬性動畫自3.0以後就有一種簡寫方式,叫做view直接驅動動畫吧

 

package com.example.administrator.animationdemo;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

/***
 * 3.0後提供的直接驅動動畫
 */
public class ViewDriverAnimatorAct extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "ViewDriverAnimatorAct";
    private TextView tvAlpha;

    public static void startInstance(Context context) {
        context.startActivity(new Intent(context, ViewDriverAnimatorAct.class));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.property_animator_layout);
        initView();
        initListener();
    }

    private void initListener() {
        tvAlpha.setOnClickListener(this);
    }

    private void initView() {
        tvAlpha = (TextView) findViewById(R.id.tv_alpha);
        findViewById(R.id.tv_execute_animtor).setVisibility(View.GONE);
        findViewById(R.id.tv_rotation).setVisibility(View.GONE);
        findViewById(R.id.tv_scale).setVisibility(View.GONE);
        findViewById(R.id.tv_set).setVisibility(View.GONE);
        findViewById(R.id.tv_translation).setVisibility(View.GONE);
    }

    @Override
    public void onClick(View v) {
        if (v == tvAlpha) {// 漸變
            startAlphaAnimtor(tvAlpha);
        }
    }

    /**
     * 動畫漸變的同時y軸移動到座標200處
     *
     * @param view
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void startAlphaAnimtor(final View view) {
        view.animate()
                .alpha(0)
                .y(200)
                .setDuration(1000)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {//動畫開始執行時
                        Log.d(TAG, "動畫開始執行時");
                    }
                })
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "動畫結束時執行");
                        //移動到座標(0,300)同時漸變,即漸變平移一起執行
                        view.animate().x(0).y(300).alpha(1).start();
                    }
                })
                .setListener(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) {

                    }
                }).start();
    }

}


三、佈局動畫:接下來看看佈局動畫,主要是針對viewGroup顯示子view比如添加刪除或顯示子view時以什麼動畫過渡平滑的顯示

      主要用到兩個類,LayoutAnimator和LayoutTransition

      (1)LayoutAnimator設置可控制viewGroup顯示子view時以什麼樣的動畫顯示平滑出來,但無法控制添加刪除子view時僅僅這個子view的動畫,這時就要用到(2)

     (2)LayoutTransition:可控制子view添加刪除時 這個子view平滑過渡的顯示

       以下代碼註釋講解的已經夠清楚了,基本沒什麼邏輯,自己看應該一目瞭然,很容易理解

    

package com.example.administrator.animationdemo;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.Keyframe;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.LayoutAnimationController;
import android.view.animation.ScaleAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * 佈局動畫 :給viewGroup增加子view時添加一個過渡的動畫效果,比如旋轉出來等等,
 * 在佈局中加入:android:animateLayoutChanges="true"不過這個是android的默認淡化效果,而且無法修改
 * 但是可以通過使用LayoutAnimationController類來自定義的一個子view的過渡效果,
 * 比如控件出場的順序(該順序主要有隨機,從頭到尾,反向3種),控件出現的間隔時間
 */
public class LayoutAnimtorAct extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "LayoutAnimtorAct";
    private TextView tvAddOneView;
    private LinearLayout llAddViewContainer;

    private ObjectAnimator objAnimator;
    private Context mContext;
    /**
     * 添加
     */
    private LayoutTransition mTransitioner;
    private TextView tvDelOneView;
    private TextView updateViewGroup;

    public static void startInstance(Context context) {
        context.startActivity(new Intent(context, LayoutAnimtorAct.class));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_animator_layout);
        initView();
        initListener();
        initData();
    }

    private void initData() {
        mContext = this;
        //設置佈局動畫:已經有的view出現的方式,動態添加刪除就無效了
        initLayoutAnim();
        //動態添加刪除一個view時,view顯示的過渡動畫效果
        initLayoutTransition();
    }


    /**
     * 設置viewGroup加載子view控件顯示時的過載動畫,就是以什麼方式顯示比如旋轉、平移、淡化等等
     * 但是動態添加刪除一個view這個不生效,這時就用到LayoutTranstion了
     * setOrder:有三個取值
     * LayoutAnimationController.ORDER_NORMAL; //順序顯示
     * LayoutAnimationController.ORDER_REVERSE;//反顯示
     * LayoutAnimationController.ORDER_RANDOM//隨機顯示
     */
    private void initLayoutAnim() {
        //設置過渡動畫
        ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
        //根據具體需求具體定義,也支持集合動畫
//        AlphaAnimation
//        RotateAnimation
//        ScaleAnimation
//        AnimationSet
        sa.setDuration(800);//動畫執行時長
        //設置動畫顯示的屬性,0.5f是子view間隔顯示時的一個延時偏移量單位是s,默認也是0.5f
        //即控件動畫執行起始的時間間隔了0.5s
        LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5f);
        //按順序顯示,其它兩種可查看註釋
//        lac.setOrder(LayoutAnimationController.ORDER_NORMAL);//控件按順序顯示
        lac.setOrder(LayoutAnimationController.ORDER_RANDOM);//隨機
//        lac.setOrder(LayoutAnimationController.ORDER_REVERSE);//反轉
        llAddViewContainer.setLayoutAnimation(lac);
    }

    /**
     * 動態添加刪除一個view時,view顯示的過渡動畫效果
     *
     * @author zhongwr
     * ViewGroup中Layout的動畫實現
     * 調用 LayoutTransition 對象的 setAnimator() 方法來定義下列動畫方式,調用參數是 Animator
     * 對象和以下 LayoutTransition 常量:
     * (1)APPEARING —— 元素在容器中顯現時需要動畫顯示。
     * (2)CHANGE_APPEARING —— 由於容器中要顯現一個新的元素,其它元素的變化需要動畫顯示。
     * (3)DISAPPEARING —— 元素在容器中消失時需要動畫顯示。
     * (4)CHANGE_DISAPPEARING —— 由於容器中某個元素要消失,其它元素的變化需要動畫顯示。
     */
    private void initLayoutTransition() {
        mTransitioner = new LayoutTransition();
        llAddViewContainer.setLayoutTransition(mTransitioner);
        //view出現時 view自身的動畫效果
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(null, "rotationY", 90F, 0F).
                setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
        mTransitioner.setAnimator(LayoutTransition.APPEARING, animator1);
        //view 消失時,view自身的動畫效果
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(null, "rotationX", 0F, 90F, 0F).
                setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
        mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, animator2);

        //view 動畫改變時,佈局中的每個子view動畫的時間間隔
        mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
        mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);

        //爲什麼這裏要這麼寫?具體我也不清楚,ViewGroup源碼裏面是這麼寫的,我只是模仿而已 不這麼寫貌似就沒有動畫效果了,所以你懂的!
        //view出現時,導致整個佈局改變的動畫
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);
        PropertyValuesHolder animator3 = PropertyValuesHolder.ofFloat("scaleX", 1F, 2F, 1F);
        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, animator3).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
        changeIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);


        // view消失,導致整個佈局改變時的動畫
        //Keyframe 對象中包含了一個時間/屬性值的鍵值對,用於定義某個時刻的動畫狀態。
        Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
        Keyframe kf1 = Keyframe.ofFloat(0.5f, 2f);
        Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
        PropertyValuesHolder pvhRotation =
                PropertyValuesHolder.ofKeyframe("scaleX", kf0, kf1, kf2);
        final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhRotation).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        changeOut.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1.0f);
            }
        });
        mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
    }

    private void initListener() {
        tvAddOneView.setOnClickListener(this);
        tvDelOneView.setOnClickListener(this);
        updateViewGroup.setOnClickListener(this);
    }

    private void initView() {
        tvAddOneView = (TextView) findViewById(R.id.tv_add_layout);
        tvDelOneView = (TextView) findViewById(R.id.tv_delete_layout);
        updateViewGroup = (TextView) findViewById(R.id.tv_update_layout);
        llAddViewContainer = (LinearLayout) findViewById(R.id.ll_add_view_container);
    }

    @Override
    public void onClick(View v) {
        //initLayoutTransition()由於設置了Transition所以添加刪除view都會有動畫,
        // 而layoutAnimator只對默認有的動畫才生效,即只生效一次,下次還需要重寫設置initLayoutAnim();才能生效,而且是所有子view都會生效
        if (v == tvAddOneView) {// 點擊給LinearLayout添加一個view
            addOneView();
            //設置佈局動畫,每次添加需要重新設置,否則不會執行動畫:即viewgroup先添加view然後設置這個纔有效果
//            initLayoutAnim();
        } else if (v == tvDelOneView) {//刪除最後一個view
            llAddViewContainer.removeViewAt(llAddViewContainer.getChildCount() - 1);
        } else if (v == updateViewGroup) {
            if (llAddViewContainer.getAlpha() == 0.8f) {//讓其更新
                llAddViewContainer.setAlpha(1f);
            } else {
                llAddViewContainer.setAlpha(0.8f);
            }
            llAddViewContainer.requestLayout();
            initLayoutAnim();
        }
    }

    /**
     * 添加一個view
     */
    private void addOneView() {
        TextView tvOneView = new TextView(mContext);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        lp.topMargin = 20;
        tvOneView.setLayoutParams(lp);
        //注意參數別寫反了,寫反了就不顯示了文本
        tvOneView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 25);
        tvOneView.setTextColor(Color.GRAY);
        tvOneView.setText("哈哈,我進來啦!");
        llAddViewContainer.addView(tvOneView);
    }


}


demo:http://download.csdn.net/detail/zhongwn/9572965


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章