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