转载请注明出处:http://blog.csdn.net/yegongheng/article/details/38435553
上一篇博文我们初步地了解了Property Animation的工作方式及流程,并通过实验加深了对对象属性计算过程的理解,同时,我在文章的最后罗列一些实现Property Animation比较重要的类、接口和方法,并做了比较详细地说明。那今天这篇文章,我将带领大家使用这些Android系统提供的API来实现一些比较炫的动画效果。
使用ValueAnimator实现动画
ValueAnimator是Property Animation系统的核心类,它包含了配置Property Animation属性的大部分方法,那要实现一个Property Animation,都需要直接或间接使用ValueAnimator类。那接下来我们将借助ValueAnimator类提供的方法来实现各种各样的动画效果,不过在此之前我们来学习一下实现动画的几个关键知识点。
Animator监听器介绍
使用及自定义Interpolator
- public class CustomInterpolator implements TimeInterpolator {
- @Override
- public float getInterpolation(float input) {
- // 编写相关的逻辑计算
- //input *= 0.8f;
- return input * input;
- }
- }
使用及自定义TypeEvaluator
- import android.animation.TypeEvaluator;
- import android.annotation.SuppressLint;
- @SuppressLint("NewApi")
- public class CustomEvaluator implements TypeEvaluator<Number> {
- @Override
- public Float evaluate(float fraction, Number startValue, Number endValue) {
- // TODO Auto-generated method stub
- float propertyResult = 0;
- /*float startFloat = startValue.floatValue();
- return (startFloat + fraction * (endValue.floatValue() - startFloat));*/
- return propertyResult;
- }
- }
使用ValueAnimator实现动画的步骤及实践
那一般使用ValueAnimator实现动画分为以下七个步骤:
1. 调用ValueAnimation类中的ofInt(int...values)、ofFloat(String propertyName,float...values)等静态方法实例化ValueAnimator对象,并设置目标属性的属性名、初始值或结束值等值;
2.调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器;
3.创建自定义的Interpolator,调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值)
4.创建自定义的TypeEvaluator,调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值)
5.在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。
6.设置动画的持续时间、是否重复及重复次数等属性;
7.为ValueAnimator设置目标对象并开始执行动画。
- /**
- * 使用ValueAnimator改变Imageview的margin的值
- */
- public void marginValueAnimator(){
- //1.调用ofInt(int...values)方法创建ValueAnimator对象
- ValueAnimator mAnimator = ValueAnimator.ofInt(0,screenWidth - mImageViewTest.getWidth());
- //2.为目标对象的属性变化设置监听器
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // 3.为目标对象的属性设置计算好的属性值
- int animatorValue = (int)animation.getAnimatedValue();
- MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mImageViewTest.getLayoutParams();
- marginLayoutParams.leftMargin = animatorValue;
- mImageViewTest.setLayoutParams(marginLayoutParams);
- }
- });
- //4.设置动画的持续时间、是否重复及重复次数等属性
- mAnimator.setDuration(2000);
- mAnimator.setRepeatCount(3);
- mAnimator.setRepeatMode(ValueAnimator.REVERSE);
- //5.为ValueAnimator设置目标对象并开始执行动画
- mAnimator.setTarget(mImageViewTest);
- mAnimator.start();
- }
- /**
- * 使用ValueAnimator实现图片缩放动画
- */
- public void scaleValueAnimator(){
- //1.设置目标属性名及属性变化的初始值和结束值
- PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,0.0f);
- PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,0.0f);
- ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX,mPropertyValuesHolderScaleY);
- //2.为目标对象的属性变化设置监听器
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // 3.根据属性名获取属性变化的值分别为ImageView目标对象设置X和Y轴的缩放值
- float animatorValueScaleX = (float) animation.getAnimatedValue("scaleX");
- float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY");
- mImageViewTest.setScaleX(animatorValueScaleX);
- mImageViewTest.setScaleY(animatorValueScaleY);
- }
- });
- //4.为ValueAnimator设置自定义的Interpolator
- mAnimator.setInterpolator(new CustomInterpolator());
- //5.设置动画的持续时间、是否重复及重复次数等属性
- mAnimator.setDuration(2000);
- mAnimator.setRepeatCount(3);
- mAnimator.setRepeatMode(ValueAnimator.REVERSE);
- //6.为ValueAnimator设置目标对象并开始执行动画
- mAnimator.setTarget(mImageViewTest);
- mAnimator.start();
- }
- package com.androidleaf.animation.customproperty;
- import android.animation.TimeInterpolator;
- import android.annotation.SuppressLint;
- @SuppressLint("NewApi")
- public class CustomInterpolator implements TimeInterpolator {
- @Override
- public float getInterpolation(float input) {
- input *= 0.8f;
- return input * input;
- }
- }
观察上面的运行效果图,我们可以发现图片不仅实现了收缩动画,而且还实现了一个迅速的收缩和回弹的效果,原因是我们通过自定义Interpolator,使得ScaleX和ScaleY属性值的变化呈非线性的变化,那涉及到自定义Interpolator的知识在上面我们已经学习过了,在此就不重复讲解了。
- /**
- * 隐藏或显示ListView的动画
- */
- public void hideOrShowListViewAnimator(final int startValue,final int endValue){
- //1.设置属性的初始值和结束值
- ValueAnimator mAnimator = ValueAnimator.ofInt(0,100);
- //2.为目标对象的属性变化设置监听器
- mAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- // TODO Auto-generated method stub
- int animatorValue = (int)animation.getAnimatedValue();
- float fraction = animatorValue/100f;
- IntEvaluator mEvaluator = new IntEvaluator();
- //3.使用IntEvaluator计算属性值并赋值给ListView的高
- mListView.getLayoutParams().height = mEvaluator.evaluate(fraction, startValue, endValue);
- mListView.requestLayout();
- }
- });
- //4.为ValueAnimator设置LinearInterpolator
- mAnimator.setInterpolator(new LinearInterpolator());
- //5.设置动画的持续时间
- mAnimator.setDuration(500);
- //6.为ValueAnimator设置目标对象并开始执行动画
- mAnimator.setTarget(mListView);
- mAnimator.start();
- }
使用ObjectAnimator实现动画
(2) rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度;
(3) scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例;
(4) pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的,缺省的枢轴点是 View 对象的中心点;
(5) x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值;
(6)alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见);
要动画显示 View 对象的某个属性,比如颜色或旋转值,我们所有要做的事情就是创建一个 Property animation,并设定对应的 View 属性。那接下来我们就用ObjectAnimator类来分别实现View的透明度渐变、收缩、移动和旋转等动画效果,那在此之前我们也来总结下使用ObjectAnimator实现动画的几个步骤,如下:
- public class ObjectAnimatorFragment extends Fragment implements OnClickListener{
- private ListView mListViewFront;
- private ListView mListViewReverse;
- private Button mButtonFlip;
- private Button mButtonAlpha;
- private Button mButtonScale;
- private Button mButtonTranslate;
- private Button mButtonRotate;
- private Button mButtonSet;
- private ImageView mImageView;
- private int screenWidth = 0;
- private int screenHeight = 0;
- String[] frontStrs = {
- "Front Page 1",
- "Front Page 2",
- "Front Page 3",
- "Front Page 4",
- "Front Page 5",
- "Front Page 6",
- };
- String[] reverseStrs = {
- "Reverse Page 1",
- "Reverse Page 2",
- "Reverse Page 3",
- "Reverse Page 4",
- "Reverse Page 5",
- "Reverse Page 6",
- };
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onActivityCreated(savedInstanceState);
- DisplayMetrics metrics = new DisplayMetrics();
- getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
- float density = metrics.density;
- //screenWidth = (int)(metrics.widthPixels * density + 0.5f);
- //screenHeight = (int)(metrics.heightPixels * density + 0.5f);
- screenWidth = metrics.widthPixels;
- screenHeight = metrics.heightPixels;
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- View rootView = inflater.inflate(R.layout.fragment_objectanimator, container, false);
- mListViewFront = (ListView) rootView.findViewById(R.id.front_page_listview);
- mListViewReverse = (ListView) rootView.findViewById(R.id.reverse_page_listview);
- mButtonFlip = (Button)rootView.findViewById(R.id.button_flip);
- mButtonFlip.setOnClickListener(this);
- mButtonAlpha = (Button)rootView.findViewById(R.id.button_alpha);
- mButtonAlpha.setOnClickListener(this);
- mButtonScale = (Button)rootView.findViewById(R.id.button_scale);
- mButtonScale.setOnClickListener(this);
- mButtonTranslate = (Button)rootView.findViewById(R.id.button_translate);
- mButtonTranslate.setOnClickListener(this);
- mButtonRotate = (Button)rootView.findViewById(R.id.button_rotate);
- mButtonRotate.setOnClickListener(this);
- mButtonSet = (Button)rootView.findViewById(R.id.button_set);
- mButtonSet.setOnClickListener(this);
- mImageView = (ImageView)rootView.findViewById(R.id.objectanimator_imageview);
- mImageView.setOnClickListener(this);
- initData();
- return rootView;
- }
- public void initData(){
- ArrayAdapter<String> frontListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, frontStrs);
- ArrayAdapter<String> reverseListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, reverseStrs);
- mListViewFront.setAdapter(frontListData);
- mListViewReverse.setAdapter(reverseListData);
- mListViewReverse.setRotationX(-90.0f);
- }
- @Override
- public void onPause() {
- // TODO Auto-generated method stub
- super.onPause();
- }
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch (v.getId()) {
- case R.id.button_flip:
- flip();
- break;
- case R.id.button_alpha:
- alphaAnimator();
- break;
- case R.id.button_scale:
- scaleAnimator();
- break;
- case R.id.button_translate:
- translateAniamtor();
- break;
- case R.id.button_rotate:
- rotateAniamtor();
- break;
- case R.id.button_set:
- setAnimator();
- break;
- case R.id.objectanimator_imageview:
- mListViewFront.setVisibility(View.VISIBLE);
- mImageView.setVisibility(View.GONE);
- break;
- default:
- break;
- }
- }
- /**
- * 翻转动画效果
- */
- public void flip(){
- final ListView visibleView;
- final ListView invisibleView;
- if(mListViewFront.getVisibility() == View.GONE){
- visibleView = mListViewReverse;
- invisibleView = mListViewFront;
- }else{
- visibleView = mListViewFront;
- invisibleView = mListViewReverse;
- }
- //创建ListView从Visible到Gone的动画
- ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(visibleView, "rotationX", 0.0f,90.0f);
- //设置插值器
- visibleToInVisable.setInterpolator(new AccelerateInterpolator());
- visibleToInVisable.setDuration(500);
- //创建ListView从Gone到Visible的动画
- final ObjectAnimator invisibleToVisible = ObjectAnimator.ofFloat(invisibleView, "rotationX", -90.0f,0.0f);
- //设置插值器
- invisibleToVisible.setInterpolator(new DecelerateInterpolator());
- invisibleToVisible.setDuration(500);
- visibleToInVisable.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // TODO Auto-generated method stub
- super.onAnimationEnd(animation);
- visibleView.setVisibility(View.GONE);
- invisibleToVisible.start();
- invisibleView.setVisibility(View.VISIBLE);
- }
- });
- visibleToInVisable.start();
- }
- /**
- * 渐变动画效果
- */
- public void alphaAnimator(){
- ListView alphaListView = null;
- if(mListViewFront.getVisibility() == View.GONE){
- alphaListView = mListViewReverse;
- }else{
- alphaListView = mListViewFront;
- }
- //1、通过调用ofFloat()方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
- ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(alphaListView, "alpha", 1.0f,0.0f);
- //2、设置动画的持续时间、是否重复及重复次数属性;
- mAnimatorAlpha.setRepeatMode(Animation.REVERSE);
- mAnimatorAlpha.setRepeatCount(3);
- mAnimatorAlpha.setDuration(1000);
- //3、启动动画
- mAnimatorAlpha.start();
- }
- /**
- * 伸缩动画效果
- */
- public void scaleAnimator(){
- ListView scaleListView = null;
- if(mListViewFront.getVisibility() == View.GONE){
- scaleListView = mListViewReverse;
- }else{
- scaleListView = mListViewFront;
- }
- ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(scaleListView, "scaleX", 1.0f,0.0f);
- mAnimatorScaleX.setRepeatMode(Animation.REVERSE);
- mAnimatorScaleX.setRepeatCount(3);
- mAnimatorScaleX.setDuration(1000);
- ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(scaleListView, "scaleY", 1.0f,0.0f);
- mAnimatorScaleY.setRepeatMode(Animation.REVERSE);
- mAnimatorScaleY.setRepeatCount(3);
- mAnimatorScaleY.setDuration(1000);
- mAnimatorScaleX.start();
- mAnimatorScaleY.start();
- }
- /**
- * 位移动画效果
- */
- public void translateAniamtor(){
- ListView translateListView = null;
- if(mListViewFront.getVisibility() == View.GONE){
- translateListView = mListViewReverse;
- }else{
- translateListView = mListViewFront;
- }
- ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(translateListView, "translationX", 0.0f,screenWidth/2);
- mAnimatorTranslateX.setRepeatMode(Animation.REVERSE);
- mAnimatorTranslateX.setRepeatCount(3);
- mAnimatorTranslateX.setDuration(1000);
- ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(translateListView, "translationY", 0.0f,screenHeight/2);
- mAnimatorTranslateY.setRepeatMode(Animation.REVERSE);
- mAnimatorTranslateY.setRepeatCount(3);
- mAnimatorTranslateY.setDuration(1000);
- mAnimatorTranslateX.start();
- mAnimatorTranslateY.start();
- }
- /**
- * 旋转动画效果
- */
- public void rotateAniamtor(){
- ListView rotateListView = null;
- if(mListViewFront.getVisibility() == View.GONE){
- rotateListView = mListViewReverse;
- }else{
- rotateListView = mListViewFront;
- }
- ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(rotateListView, "rotation", 0.0f,360.0f);
- mAnimatorRotate.setRepeatMode(Animation.REVERSE);
- mAnimatorRotate.setRepeatCount(2);
- mAnimatorRotate.setDuration(2000);
- mAnimatorRotate.start();
- }
- /**
- * 动画集合
- */
- public void setAnimator(){
- ListView setListView = null;
- if(mListViewFront.getVisibility() == View.GONE){
- setListView = mListViewReverse;
- }else{
- setListView = mListViewFront;
- }
- setListView.setVisibility(View.GONE);
- if(mImageView.getVisibility() == View.GONE){
- mImageView.setVisibility(View.VISIBLE);
- }
- //代码方式设置动画
- codeAnimatorSet(mImageView);
- //用ViewPropertyAnimator实现动画
- //viewPropertyAnimator(setListView);
- //加载XML文件中的动画
- /*AnimatorSet mAnimatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(getActivity(), R.animator.property_animation_animatorset);
- mAnimatorSet.setTarget(mImageView);
- mAnimatorSet.start();*/
- }
- /**
- * 使用编码方式实现动画效果
- * @param mImageView
- */
- public void codeAnimatorSet(ImageView mImageView){
- AnimatorSet mAnimatorSet = new AnimatorSet();
- ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f,360.0f);
- mAnimatorSetRotateX.setDuration(3000);
- ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0f,360.0f);
- mAnimatorSetRotateY.setDuration(3000);
- ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX", 1.0f,0.5f);
- mAnimatorScaleX.setRepeatCount(1);
- mAnimatorScaleX.setRepeatMode(Animation.REVERSE);
- mAnimatorScaleX.setDuration(1500);
- ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f,0.5f);
- mAnimatorScaleY.setRepeatCount(1);
- mAnimatorScaleY.setRepeatMode(Animation.REVERSE);
- mAnimatorScaleY.setDuration(1500);
- mAnimatorSet.play(mAnimatorSetRotateY).with(mAnimatorScaleX);
- mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY);
- mAnimatorSet.play(mAnimatorSetRotateY).before(mAnimatorSetRotateX);
- mAnimatorSet.start();
- }
- public void viewPropertyAnimator(ListView mListViewHolder){
- mListViewHolder.animate().cancel();
- mListViewHolder.animate().rotationX(360.0f).setDuration(3000).start();
- }
- }
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@color/view_animation_background"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="翻转"
- android:id="@+id/button_flip"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Alpha"
- android:id="@+id/button_alpha"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Scale"
- android:id="@+id/button_scale"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Translate"
- android:id="@+id/button_translate"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Rotate"
- android:id="@+id/button_rotate"
- />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Set"
- android:id="@+id/button_set"
- />
- </LinearLayout>
- <ListView
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:divider="@color/propertyanimator_background"
- android:dividerHeight="0.5dp"
- android:id="@+id/front_page_listview"
- />
- <ListView
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:divider="@color/propertyanimator_background"
- android:visibility="gone"
- android:dividerHeight="0.5dp"
- android:id="@+id/reverse_page_listview"
- />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="fitStart"
- android:visibility="visible"
- android:src="@drawable/liduohai"
- android:id="@+id/objectanimator_imageview"
- />
- </LinearLayout>
<1>如果权限允许的话,直接在类中增加此 setter 方法;
<2>修改封装类来增加此 setter 方法,并让该封装类来接收属性值并传给初始的对象;
<3>换用 ValueAnimator。
2.如果在调用 ObjectAnimator 的某个工厂方法时,我们只为 values... 参数指定了一个值,那此值将被认定为动画属性的结束值。 这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。 此 getter 方法必须以get<propertyName>()的格式命名。 例如:假设属性名为foo,则需要有一个getFoo()方法。
3.动画属性的 getter 方法(如果必要的话)和 setter 方法所操作数据的类型必须与 ObjectAnimator 中设定的起始和结束值相同。
小结:今天我们主要了解了ValueAnimator和ObjectAnimator的概念及其它们关系和特点,并通过实例学习了如何使用这两个类实现了非常炫的动画效果。总结一下本文我们需要掌握的知识点:(1)了解并学会使用动画监听器;(2)了解Interpolator的概念及作用,并学会自定义Interpolator;(3)解TypeEvaluator的概念及作用,并学会自定义TypeEvaluator;(4)熟练使用ValueAnimator实现动画;(5)熟练使用ObjectAnimator实现动画;(6)使用ObjectAnimator实现动画的一些限制和需要注意的事项。由于篇幅关系,我们下一章还会学习如何为ViewGroup中的Layout设置动画效果,敬请期待!
源代码下载,请戳下面: