Android 属性动画特效

来源:互联网 发布:阿迪达斯童鞋淘宝官网 编辑:程序博客网 时间:2024/05/16 18:13

        在 Android 3.0 之前已有的动画框架 Animation 存在一些局限性—— 动画改变的只是显示,并不能响应事件(相比属性动画,视图动画的一个非常大的缺陷就是不具备交互性,当某个元素发生视图动画后,其响应事件的位置依然在动画前的地方,所以视图动画只能做普通的动画效果,避免交互的发生。它的优点是效率比较高使用方便)。

本文要实现的效果如下:




在这之前先简单的介绍一下属性动画。


       1.   属性动画涉及的 API :

      (1)Animator :它提供了创建属性动画的基类,基本上不会直接使用该类。通常该类只用于被继承并重写它的相关方法。

      (2) ValueAnimator :属性动画主要的时间引擎,它负责计算各个帧的属性值。属性动画主要有两方面组成:

        ——> 1)计算各帧的相关属性值;

        ——> 2)为指定对象设置这些计算后的值;

        ValueAnimator 只负责第一方面内容,因此程序员必须根据 ValueAnimator  计算并监听值更新来更新对象的相关属性值。使用 ValueAnimator 需要注册 AnimatorUpdateListener 监听器。

      (3)ObjectAnimator :它是 ValueAnimator  的子类,允许程序员对指定对象的属性执行动画。在实际应用中,它比 ValueAnimator  使用起来更加简单。使用 ObjectAnimator 就不需要注册 AnimatorUpdateListener 监听器了。

      (4) AnimatorSet :它是 Animator  的子类,组合多个 Animator ,并指定多个 Animator  是按次序播放,还是同时播放。


        在 Animator 框架中使用最多的就是 AnimatorSet 和 ObjectAnimator ,使用 ObjectAnimator  进行更精细化的控制,只控制一个对象的一个属性值,而使用多个 ObjectAnimator  组合到 AnimatorSet 形成一个动画。属性动画通过调用属性的 get 、set 方法来真实地控制一个 View 的属性值,因此强大的动画框架,基本可以实现所有的动画效果。



       2.  使用属性动画的步骤如下:

      (1)创建 ObjectAnimator 或  ValueAnimator 对象——即可从 XML 资源文件加载该动画资源,也可以直接调用  ObjectAnimator 或  ValueAnimator 的静态工厂方法来创建动画。

        ——> 静态方法 ofInt() 、ofFloat() 或者 ofObject();

——> 注意使用 ObjectAnimator 的静态方法创建 ObjectAnimator 对象时,需要指定具体的对象,以及对象的属性名。如: ObjectAnimator animator0 = ObjectAnimator.ofFloat(foo, "alpha", 0.5F, 1F);

       (2)根据需要为 Animator  对象设置属性。

       (3)如果需要监听 Animator  的动画开始事件、动画结束事件、动画重复事件、动画值改变事件,并根据事件提供相应的处理代码,则应该为 Animator  对象设置事件监听器。

       (4)如果有多个动画需要按次序或者同时播放,则应使用 AnimatorSet 组合这些动画。

       (5)调用 Animator  对象的 start() 方法启动动画。



         ObjectAnimator 

         ObjectAnimator  参数包括一个对象和对象的属性名,但这个属性必须有 get() 和 set() 函数,内部会通过 Java 反射机制来调用 set 函数修改对象属性。

         前文的使用 ObjectAnimator animator0 = ObjectAnimator.ofFloat(foo, "alpha", 0.5F, 1F); 创建一个 ObjectAnimator 对象。其中第一个参数时需要操纵的 View;第二个参数则是操纵的属性;而最后一个参数是一个可变数组参数,需要传进去该属性变化的一个取值过程。

第二个参数的属性值如下:

      (1)translationX 和 translationY:这两个属性作为一种增量来控制着 View 对象从它布局容器的左上角坐标偏移的位置。

      (2)rotation 、rotationX 和 rotationY:这三个属性控制着 View 对象围绕支点进行 2D 和 3D 旋转。

      (3)scaleX 和 scaleY:这两个属性控制着 View 对象围绕它的支点进行 2D 缩放。

      (4)pivotX 和 pivotY:这两个属性控制着 View 对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下,该支点的位置就是 View 对象的中心点。

      (5)x 和 y:它描述了 View 对象在它的容器中的最终位置,它是最初的左上角坐标和 translationX 、 translationY 值得累计和。

      (6)alpha:它表示 View 对象的 alpha 透明度。默认值为 1 (不透明),0 代表完全透明(不可见)。


属性的 get() 和 set() 方法也可以从如下两个方案来解决:

        ——> 通过自定义一个属性或者包装类,来间接地给这个属性增加 get() 、set() 方法。

——> 通过 ValueAnimator  来实现。


        AnimatorSet 

        对于一个属性同时作用多个属性动画效果,可以使用 PropertyValuesHolder 实现这样的效果,但是通过 AnimatorSet 不仅能实现这样的效果,同行也能实现更为精确的顺序控制。代码如下:

 ObjectAnimator animator1 = ObjectAnimator.ofFloat(foo, "translationY", -200F, 0); ObjectAnimator animator1 = ObjectAnimator.ofFloat(foo, "translationX", -200F, 0); AnimatorSet set = new AnimatorSet(); set.setDuration(500); set.setInterpolator(new BounceInterpolator()); set.playTogether(animator1, animator2); set.start();

        在属性动画中,AnimatorSet  正是通过 playTogether()、playSequentially()、animSet.play().with()、before()、after()这些方法来控制多个动画的协同工作方式。



第一个效果代码:

        forclick_layout.xml :

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/imageView_b"        android:src="@drawable/b"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/imageView_c"        android:src="@drawable/c"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/imageView_d"        android:src="@drawable/d"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/imageView_e"        android:src="@drawable/e"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" />    <ImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:id="@+id/imageView_a"        android:src="@drawable/a"        android:layout_centerVertical="true"        android:layout_centerHorizontal="true" /></RelativeLayout>


PropertyTest.java :

package com.imooc.anim;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.animation.BounceInterpolator;import android.widget.ImageView;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class PropertyTest extends Activity implements View.OnClickListener {    private int[] mRes = {R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,            R.id.imageView_d, R.id.imageView_e};    private List<ImageView> mImageViews = new ArrayList<>();    private boolean mFlag = true;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.forclick_layout);        int sum = mRes.length;        for (int i = 0; i < sum; i++) {            ImageView imageView = (ImageView) findViewById(mRes[i]);            imageView.setOnClickListener(this);            mImageViews.add(imageView);        }    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.imageView_a:                if (mFlag) {                    startAnim();                } else {                    closeAnim();                }                break;            case R.id.imageView_b:                Toast.makeText(PropertyTest.this, "相机", Toast.LENGTH_SHORT).show();                break;            case R.id.imageView_c:                Toast.makeText(PropertyTest.this, "音乐", Toast.LENGTH_SHORT).show();                break;            case R.id.imageView_d:                Toast.makeText(PropertyTest.this, "定位", Toast.LENGTH_SHORT).show();                break;            case R.id.imageView_e:                Toast.makeText(PropertyTest.this, "夜晚", Toast.LENGTH_SHORT).show();                break;        }    }    private void closeAnim() {        ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),                "alpha", 0.5F, 1F);        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),                "translationY", 200F, 0);        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),                "translationX", 200F, 0);        ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),                "translationY", -200F, 0);        ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),                "translationX", -200F, 0);        AnimatorSet set = new AnimatorSet();        set.setDuration(500);        set.setInterpolator(new BounceInterpolator());        set.playTogether(animator0, animator1, animator2, animator3, animator4);        set.start();        mFlag = true;    }    private void startAnim() {        ObjectAnimator animator0 = ObjectAnimator.ofFloat(                mImageViews.get(0),                "alpha",                1F,                0.5F);        ObjectAnimator animator1 = ObjectAnimator.ofFloat(                mImageViews.get(1),                "translationY",                200F);        ObjectAnimator animator2 = ObjectAnimator.ofFloat(                mImageViews.get(2),                "translationX",                200F);        ObjectAnimator animator3 = ObjectAnimator.ofFloat(                mImageViews.get(3),                "translationY",                -200F);        ObjectAnimator animator4 = ObjectAnimator.ofFloat(                mImageViews.get(4),                "translationX",                -200F);        AnimatorSet set = new AnimatorSet();        set.setDuration(500);        set.setInterpolator(new BounceInterpolator());        set.playTogether(                animator0,                animator1,                animator2,                animator3,                animator4);        set.start();        mFlag = false;    }}



第二个效果的实现:

drop.xml :

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical">    <LinearLayout        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:gravity="center_vertical"        android:onClick="llClick"        android:background="@android:color/holo_blue_bright"        android:orientation="horizontal">        <ImageView            android:id="@+id/app_icon"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center"            android:src="@drawable/ic_launcher" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="5dp"            android:gravity="left"            android:text="Click Me"            android:textSize="30sp" />    </LinearLayout>    <LinearLayout        android:id="@+id/hidden_view"        android:layout_width="match_parent"        android:layout_height="40dp"        android:background="@android:color/holo_orange_light"        android:gravity="center_vertical"        android:orientation="horizontal"        android:visibility="gone">        <ImageView            android:src="@drawable/ic_launcher"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center" />        <TextView            android:id="@+id/tv_hidden"            android:layout_width="wrap_content"            android:layout_height="match_parent"            android:gravity="center"            android:textSize="20sp"            android:text="我是隐藏着的!" />    </LinearLayout></LinearLayout>

点击上面的 LinearLayout 时,需要获取到隐藏的 LinearLayout 最终需要达到的一个高度,即是目标值,通过将布局文件文件中的 dp 值转化为像素值即可。

        // 获取像素密度        mDensity = getResources().getDisplayMetrics().density;        // 获取布局的高度        mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);
40 就是在 XML 文件中定义的布局高度。


需要使用 ValueAnimator  来创建一个从 0 到目标值的数值发生器,并由此来改变 View 的布局属性。

 ValueAnimator animator = ValueAnimator.ofInt(start, end);        animator.addUpdateListener(                new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                int value = (Integer) valueAnimator.getAnimatedValue();                ViewGroup.LayoutParams layoutParams =                        view.getLayoutParams();                layoutParams.height = value;                view.setLayoutParams(layoutParams);            }        });



DropTest.java :

package com.imooc.anim;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.ValueAnimator;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;public class DropTest extends Activity {    private LinearLayout mHiddenView;    private float mDensity;    private int mHiddenViewMeasuredHeight;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.drop);        mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);        // 获取像素密度        mDensity = getResources().getDisplayMetrics().density;        // 获取布局的高度        mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);    }    public void llClick(View view) {        if (mHiddenView.getVisibility() == View.GONE) {            // 打开动画            animateOpen(mHiddenView);        } else {            // 关闭动画            animateClose(mHiddenView);        }    }    private void animateOpen(final View view) {        view.setVisibility(View.VISIBLE);        ValueAnimator animator = createDropAnimator(                view,                0,                mHiddenViewMeasuredHeight);        animator.start();    }    private void animateClose(final View view) {        int origHeight = view.getHeight();        ValueAnimator animator = createDropAnimator(view, origHeight, 0);        animator.addListener(new AnimatorListenerAdapter() {            public void onAnimationEnd(Animator animation) {                view.setVisibility(View.GONE);            }        });        animator.start();    }    private ValueAnimator createDropAnimator(            final View view, int start, int end) {        ValueAnimator animator = ValueAnimator.ofInt(start, end);        animator.addUpdateListener(                new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                int value = (Integer) valueAnimator.getAnimatedValue();                ViewGroup.LayoutParams layoutParams =                        view.getLayoutParams();                layoutParams.height = value;                view.setLayoutParams(layoutParams);            }        });        return animator;    }}

其中:AnimatorUpdateListener 中监听的是数值变换,从而完成动画的变换;一个完整的动画具有 Start、Repeat、End 、Cancel 四个过程,通过 AnimatorListener 接口可以很方便地监听到这四个事件。AnimatorListener 接口的源码如下:

  public interface AnimatorListener {        void onAnimationStart(Animator var1);        void onAnimationEnd(Animator var1);        void onAnimationCancel(Animator var1);        void onAnimationRepeat(Animator var1);    }

但是,大部分的时候,我们都只关心 onAnimationEnd 事件,所以 Android 也提供了一个 AnimatorListenerAdapter 来让我们选择必要的事件进行监听,如文中的代码:

    animator.addListener(new AnimatorListenerAdapter() {            public void onAnimationEnd(Animator animation) {                view.setVisibility(View.GONE);            }        });



0 0
原创粉丝点击