1、屬性動畫是什麼不廢話,不懂的可以百度一下參考郭霖大神的動畫詳解篇;這裏僅僅提供一個demo說說用法,拋磚引玉,代碼的註釋寫的已經很詳細,不再多說廢話,一下提供的是一個基礎的demo,講解的是objectAnimator的基礎用法,如平移、旋轉、縮放、漸變以及動畫的集合;至於objectAnimator(必須的有set get方法)和valueAnimator的詳細區別也可參考郭霖大神的動畫詳解篇
2、除此基本用法,還有估值器和插值器
(1)插值器:動畫速率的變換,有點類似物理的加速度,就是該變動畫的速率的
(2)估值器:一般配合插值器使用,插值器返回因子,起始值,結束值給估值器,估值器根據這個區間數據生成這個區間連續的數值,而這寫數值就是動畫需要的屬性的值,使動畫平滑過渡比如:ValueAnimator.ofFloat()方法就是實現了初始值與結束值之間的平滑過度,那麼這個平滑過度是怎麼做到的呢?其實就是系統內置了一個FloatEvaluator,它通過計算告知動畫系統如何從初始值過度到結束值
這是摘抄自網絡
我們都知道對於屬性動畫可以對某個屬性做動畫,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色,下面先了解下TimeInterpolator和TypeEvaluator。
TimeInterpolator(時間插值器):
- 作用:根據時間流逝的百分比計算出當前屬性值改變的百分比。
- 系統已有的插值器:
①LinearInterpolator(線性插值器):勻速動畫。
②AccelerateDecelerateInterpolator(加速減速插值器):動畫兩頭慢,中間快。
③DecelerateInterpolator(減速插值器):動畫越來越慢。
TypeEvaluator(類型估值算法,即估值器):
- 作用:根據當前屬性改變的百分比來計算改變後的屬性值。
- 系統已有的估值器:
①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