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


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值