标签:
1、属性动画是什么不废话,不懂的可以百度一下参考郭霖大神的动画详解篇;这里仅仅提供一个demo说说用法,抛砖引玉,代码的注释写的已经很详细,不再多说废话,一下提供的是一个基础的demo,讲解的是objectAnimator的基础用法,如平移、旋转、缩放、渐变以及动画的集合;至于objectAnimator(必须的有set get方法)和valueAnimator的详细区别也可参考郭霖大神的动画详解篇
2、除此基本用法,还有估值器和插值器
(1)插值器:动画速率的变换,有点类似物理的加速度,就是该变动画的速率的
(2)估值器:一般配合插值器使用,插值器返回因子,起始值,结束值给估值器,估值器根据这个区间数据生成这个区间连续的数值,而这写数值就是动画需要的属性的值,使动画平滑过渡比如:ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值
这是摘抄自网络
我们都知道对于属性动画可以对某个属性做动画,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色,下面先了解下TimeInterpolator和TypeEvaluator。
TimeInterpolator(时间插值器):
TypeEvaluator(类型估值算法,即估值器):
那么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
标签:
原文地址:http://blog.csdn.net/zhongwn/article/details/51880723