Android动画之属性动画(四)

来源:互联网 发布:怎么在知乎回答问题 编辑:程序博客网 时间:2024/05/19 01:06

一 前言

      在《Android属性动画之ValueAnimator》、《Android属性动画之ObjectAnimator和AnimatorSet》两篇文章中学习了ValueAnimator、ObjectAnimator、AnimatorSet等类的使用,而且知道了属性动画通过改变一个对象的属性值来来实现动画效果,属性动画包含了以下几个特性: 
  (1)持续时间(Duration) :主要用来定义动画的持续时间,默认值为300ms,一般可以通过setDuration的函数进行设置
  (2)时间插值器(Time interpolation): 指定时间变化的百分比,就是当前流逝时间除以指定的持续时间,这个可以自定义,继承Interpolator,重写getInterpolation方法,系统给定的插值器有线性插值器、加法插值器、减法插值器、峰值插值器(先进行加法达到一定高度再减)。
  (3)重复次数和行为(Repeat count and behavior) :指定动画的执行次数和动画的重复模式,可以通过setRepeatCount函数设置动画重复的次数,通过setRepeatMode函数设置动画重复的模式。
  (4)动画集(Animator sets) :可以把多个动画放到一个集合中,可以使它们同时执行或者依次执行,或者指定它们直接的顺序和延迟,相关的函数有play、with、before、after。

  (5)Frame refresh delay(帧刷新延迟) :可以指定如何去刷新动画的帧,默认是每10ms刷新一次,这个刷新也取决于系统的繁忙程度。

转载请注明出处:小石头的博客  http://blog.csdn.net/lu1024188315/article/details/74518599

二 属性动画相关的类

Animator

      这个可以说是属性动画的鼻祖,除了ViewPropertyAnimator之外,其余都是由Animator派生出来的,例如上面的ValueAnimator、ObjectAnimator、AnimatorSet最终都是继承的Animator。

(1)它几个重要的成员变量

ArrayList<AnimatorListener> mListeners = null;ArrayList<AnimatorPauseListener> mPauseListeners = null;

说明,mListeners为AnimatorListener类型,AnimatorListener是一个回调接口其源码如下:

public static interface AnimatorListener {            //动画开始回调          void onAnimationStart(Animator animation);         //动画结束时回调         void onAnimationEnd(Animator animation);        //动画取消时回调          void onAnimationCancel(Animator animation);        //动画重复时回调          void onAnimationRepeat(Animator animation);}

mListeners用来保存属性动画所用的AnimatorListener实例。

mPauseListenersAnimatorPauseListener类型,AnimatorPauseListener是一个回调接口其源码如下:
public static interface AnimatorPauseListener {        //动画暂停时回调        void onAnimationPause(Animator animation);         //动画重启时回调        void onAnimationResume(Animator animation);}

(2)常见方法

public void start()动画启动public void cancel()动画取消public void end()动画结束public void pause()动画暂停public void resume()动画重启public void addListener(AnimatorListener listener)添加AnimatorListener监听public void removeListener(AnimatorListener listener)移除AnimatorListener监听public void setupStartValues()设置属性起始值public void setupEndValues()设置属性结束值public void setTarget(@Nullable Object target)设置动画的作用对象

(3)抽象方法

public abstract boolean isRunning()返回动画运行的状态public abstract void setInterpolator(TimeInterpolator value)设置时间插值器public abstract long getDuration()获取动画时间间隔public abstract Animator setDuration(long duration)设置动画时间间隔public abstract long getStartDelay()获取动画启动的延迟时间public abstract void setStartDelay(long startDelay)设置动画启动的延迟时间public void addPauseListener(AnimatorPauseListener listener)添加AnimatorPauseListener监听public void removePauseListener(AnimatorPauseListener listener)移除AnimatorPauseListener监听

AnimatorSet

     AnimatorSet直接继承Animator,它用来实现属性的组合动画,它可以组合多个ValueAnimator或者ObjectAnimator,它是以Builder模式进行动画添加的,首先调用play函数添加一个主动画,返回Builder,再者可以通过with()、after()、before()函数添加其它的动画。更多参见《Android属性动画之ObjectAnimator和AnimatorSet》。

ValueAnimator

     ValueAnimator继承于AnimatorSet,它监听属性动画的整个过程,它本身没有动画的效果,需要自己添加AnimatorUpdateListener监听来实现动画的效果,AnimatorUpdateListener源码如下:

public static interface AnimatorUpdateListener {        void onAnimationUpdate(ValueAnimator animation);}

说明,AnimatorUpdateListener非常简单只有一个方法onAnimationUpdate需要实现,在这个方法中可以实现自己想要的动画,更多参见Android属性动画之ValueAnimator

ObjectAnimator

       ObjectAnimator继承于ValueAnimator,它作用一个对象,需要指定该对象具体的属性,本身实现动画效果,不必自己去实现,只需指出具体的属性就可以了,更多参见《Android属性动画之ObjectAnimator和AnimatorSet》。

ViewPropertyAnimator

      ViewPropertyAnimator用于实现View动画,需要注意的是它的获取对象实例的方法是在View类中,如下:

View#animate:
public ViewPropertyAnimator animate() {        if (mAnimator == null) {            mAnimator = new ViewPropertyAnimator(this);        }        return mAnimator;}

使用示例:

ViewPropertyAnimator viewPropertyAnimator = img.animate();viewPropertyAnimator.translationX(200).scaleX(2).setDuration(2000).start();

它更多方法参见《Android属性动画之ObjectAnimator和AnimatorSet》。

三 使用XML

      在属性动画中使用XML文件,就是自定义一些XML文件,在这些XML文件中使用objectAnimator标签来实现动画效果,接下来直接上示例来看看属性动画的XML文件是如何使用的,示例说明:使用AdapterViewFlipper控件来实现,当点击next按钮时,图片从左进右出,点击previous按钮时,图片右进左出:

1 建立XML文件

首先在资源文件res下建立目录animator,注意这里建立的不是animation动画使用的anim目录,否则的话很容易找不到objectAnimator标签或其它错误,图片从左进右出和图片右进左出,这里需要四个XML文件,它们依次是:

anim_left_enter.xml:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"     android:interpolator="@android:anim/accelerate_decelerate_interpolator"    android:propertyName="x"    android:valueType="floatType"    android:valueFrom="-1500"    android:valueTo="0"    android:duration="600"></objectAnimator>
anim_left_exit.xml:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"     android:interpolator="@android:anim/accelerate_decelerate_interpolator">    <objectAnimator        android:propertyName="x"        android:valueType="floatType"        android:valueFrom="0"        android:valueTo="1500"        android:duration="600"/></objectAnimator>
anim_right_enter.xml:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"     android:interpolator="@android:anim/accelerate_decelerate_interpolator"    android:propertyName="x"    android:valueType="floatType"    android:valueFrom="1500"    android:valueTo="0"    android:duration="600"></objectAnimator>
anim_right_exit.xml:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"     android:interpolator="@android:anim/accelerate_decelerate_interpolator">    <objectAnimator        android:propertyName="x"        android:valueType="floatType"        android:valueFrom="0"        android:valueTo="-1500"        android:duration="600"/></objectAnimator>

      说明,上面出现了两种情况,一直接使用objectAnimator标签,二在使用objectAnimator标签(父标签)后,又嵌套一个objectAnimator标签(子标签),在这里没有特殊的含义,仅仅想证明两个情况都可以使用,还有的资料说父标签使用set、子标签使用objectAnimator标签,这情况,我尝试过,运行时会出现异常,不知道别人是怎么成功,(我使用的虚拟机是Android 5.0系统)。

      还有一点,感觉需要注意下,如果你先前使用父标签是set,那么即使换过来(父标签换为objectAnimator),保存远行,可能还会提示远行异常信息如 android.animation.AnimatorSet cannot be cast to android.animation.ObjectAnimator等,那么就请clear、rebuild下你的项目吧,再次运行,看看有没有异常,还是会有的话,那就保存内容重启AS吧(我也很绝望!我就是被这么坑过来的,以此作此总结,以防更多的小伙伴被坑),好闲话少说继续看。

2 布置界面

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:gravity="center_horizontal"    android:layout_width="match_parent"    android:layout_height="match_parent">    <!--属性  android:loopViews="true" 设置动画可循环播放-->    <AdapterViewFlipper        android:id="@+id/av_flipper"        android:layout_width="match_parent"        android:loopViews="true"        android:layout_weight="1"        android:layout_height="wrap_content">    </AdapterViewFlipper>    <LinearLayout        android:layout_width="match_parent"        android:gravity="center"        android:layout_height="wrap_content"        android:layout_marginBottom="10dp"        android:orientation="horizontal">        <Button            android:text="下一个"            android:onClick="next"            android:layout_width="wrap_content"            android:layout_height="wrap_content"/>        <Button            android:text="上一个"            android:onClick="previous"            android:layout_width="wrap_content"            android:layout_height="wrap_content"/>        <Button            android:text="自动播放"            android:onClick="auto"            android:layout_width="wrap_content"            android:layout_height="wrap_content"/>    </LinearLayout></LinearLayout>

说明,这里很简单,一个轮番显示图片的AdapterViewFlipper控件,即三个按钮,分别是显示下一张、上一张图片,及其自动轮番播放按钮。

3 自定义适配器

public class FliperAdapter extends BaseAdapter {    private int[] imgIds;    private Context context;    public FliperAdapter(Context context, int[] imgIds) {        this.imgIds = imgIds;        this.context  = context;    }    @Override    public int getCount() {        return imgIds == null ? 0 : imgIds.length;    }    @Override    public Object getItem(int position) {        return imgIds[position];    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ViewHolde holde = null;        if (convertView == null){            convertView = LayoutInflater.from(context).inflate(R.layout.item_fliper,parent,false);            holde = new ViewHolde();            holde.imageView = (ImageView) convertView.findViewById(R.id.item_img);            convertView.setTag(holde);        }else {            holde = (ViewHolde) convertView.getTag();        }        holde.imageView.setImageResource(imgIds[position]);        return convertView;    }    static class ViewHolde{        public ImageView imageView;    }}

4 Activity中使用XML动画

public class AnimatorXMLTestActivity extends Activity  {    private AdapterViewFlipper avFlipper;    //图片资源    public int[] imgIds = {R.mipmap.pic_1,R.mipmap.pic_2,R.mipmap.pic_3,R.mipmap.pic_4,R.mipmap.pic_5};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_fliper);        FliperAdapter adapter  = new FliperAdapter(this,imgIds);        avFlipper = (AdapterViewFlipper) findViewById(R.id.av_flipper);        //设置动画自动播放的时间间隔        avFlipper.setFlipInterval(1000);        //设置显示第一个View时是否使用动画        avFlipper.setAnimateFirstView(true);       //设置适配器        avFlipper.setAdapter(adapter);//        ObjectAnimator  objectAnimator = ObjectAnimator.ofFloat(avFlipper,"x",0f,1f);//        ObjectAnimator  objectAnimator2 = ObjectAnimator.ofFloat(avFlipper,"x",1f,0f);    }    /**     * 下一个     * @param view     */    public void next(View view){        //左进左出        avFlipper.setInAnimation(this , R.animator.anim_left_enter);//设置图片进入动画        avFlipper.setOutAnimation(this , R.animator.anim_left_exit);//设置图片出来动画        avFlipper.showNext();//显示下一个view        avFlipper.stopFlipping();//停止自动播放    }    /**     * 上一个     * @param view     */    public void previous(View view){        //右进右出        avFlipper.setInAnimation(this , R.animator.anim_right_enter);        avFlipper.setOutAnimation(this , R.animator.anim_right_exit);        avFlipper.showPrevious();//显示下一个view        avFlipper.stopFlipping();//停止自动播放    }    /**     * 自动播放     * @param view     */    public void auto(View view){        avFlipper.startFlipping();//开始自动播放    }}

说明,在使用自定义XML动画时,按照如下格式:R.animator.XXX(xxx代表的是你自定义的XML文件),上面使用的情况如下:

        avFlipper.setInAnimation(this , R.animator.anim_right_enter);        avFlipper.setOutAnimation(this , R.animator.anim_right_exit);

是因为AdapterViewFlipper有setInAnimation、setOutAnimation的方法,它们源码如下:

public void setInAnimation(Context context, int resourceID) {       setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));}public void setOutAnimation(Context context, int resourceID) {       setOutAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));}

说明,通过AnimatorInflatter的静态方法loadAnimator加载动画,返回ObjectAnimator对象实例,而

ObjectAnimator上面已经说过了,它作用一个对象,要指定具体的属性,该实例的具体属性就是“x”即在x轴移动。

效果如下:


四 使用Animator类

1 ValueObject

 img = (ImageView) findViewById(R.id.img);        ValueAnimator animator = ValueAnimator.ofFloat(0, 500);        // 这里指定变化持续时间        animator.setDuration(1000);        animator.setInterpolator(new LinearInterpolator());        //开始动画        animator.start();        //开始动画后,我们可以动态的获取变化值        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){            @Override            public void onAnimationUpdate(ValueAnimator animation){                //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动                img.setTranslationY((Float) animation.getAnimatedValue());            }        });

2 ObjectAnimator

        img = (ImageView) findViewById(R.id.img);        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(img, "translationX", 0.0f, 350.0f, 0f);        objectAnimator.setDuration(2500).start();        objectAnimator.setDuration(2000);//动画时间        objectAnimator.setInterpolator(new BounceInterpolator());//动画插值        objectAnimator.setRepeatCount(-1);//设置动画重复次数        objectAnimator.setRepeatMode(ValueAnimator.RESTART);//动画重复模式        objectAnimator.setStartDelay(1000);//动画延时执行        objectAnimator.start();//启动动画        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){            @Override            public void onAnimationUpdate(ValueAnimator animation){                //根据变化值来设置imageView对象的Y轴坐标,这样就实现了imageView的垂直移动//                img.setTranslationY((Float) animation.getAnimatedValue());            }        });

3 AnimatorSet

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_animator_set);        container = (LinearLayout) findViewById(R.id.container);        img = (ImageView) findViewById(R.id.img);        findViewById(R.id.button).setOnClickListener(this);        ObjectAnimator animator = ObjectAnimator.ofInt(container, "backgroundColor", 0xFFFF0000, 0xFFFF00FF);        ObjectAnimator animator1 = ObjectAnimator.ofFloat(img, "translationX", 0.0f, 200.0f, 0f);        ObjectAnimator animator2 = ObjectAnimator.ofFloat(img, "scaleX", 1.0f, 2.0f);        ObjectAnimator animator3 = ObjectAnimator.ofFloat(img, "rotationX", 0.0f, 90.0f, 0.0F);        ObjectAnimator animator4 = ObjectAnimator.ofFloat(img, "alpha", 1.0f, 0.2f, 1.0F);        //组合动画方式1        set = new AnimatorSet();        set.play(animator)                .with(animator1)                .after(animator2)                .after(animator3)                .before(animator4);        set.setDuration(5000);    }    @Override    public void onClick(View view) {        switch (view.getId()) {            case R.id.button:                set.start();                break;        }    }

说明,更多详细参见Android属性动画之ValueAnimator》、《Android属性动画之ObjectAnimator和AnimatorSet》。

动画的示例代码都在anima目录下:

https://git.oschina.net/lzbgit/androidwindows/tree/master/app/src/main/java/com/eric/windows/anima


原创粉丝点击