Android学习之属性动画基础

来源:互联网 发布:澳洲排油丸 知乎 编辑:程序博客网 时间:2024/06/05 08:29

Property Animation是一个很强劲的动画框架,几乎可以为所有的事物加上动画效果。

相关API

Duration:动画的持续时间,默认300ms
Time interpolation:时间差值,定义动画的变化率
Repeat count and behavior:重复次数,以及重复模式;可以定义重复多少次,重复时是从头开始还是反向
Animator set:动画集合,可以定义一组动画,一起执行或者顺序执行
Frame refresh delay:帧刷新延迟,多久刷新一次帧,默认为10ms。但最终依赖系统的当前状态,基本不用管
objectAnimator:动画的执行类
ValueAnimator:动画的执行类
AnimatorSet:用于控制一组动画的执行:线性,一起,每个动画的先后执行等
AnimatorInflater:用于加载属性动画的xml文件
TypeEvalutor:类型估值,主要用于设置动画操作属性的值
TimeInterpolator:时间插值

总得来说,属性动画就是,动画的执行类来设置动画操作的对象的属性,持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性

1,ObjectAnimator实现动画

先看一个最简单的
这里写图片描述

 public   void  startAnimation(View view){        ObjectAnimator                .ofFloat(view,"rotationX",0.0f,360.0f)                .setDuration(1000)                .start();    }
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.asus1.testpropertyanimation.MainActivity">    <ImageView        android:layout_width="200dp"        android:layout_height="200dp"        android:id="@+id/iv_image"        android:layout_centerInParent="true"        android:src="@drawable/bg"        android:onClick="startAnimation"        /></RelativeLayout>

对于ObjectAnimator
1,提供了ofInt,ofFloat,ofObject,这几个方法都是设置动画动作的元素,作用的属性,动画开始,结束,以及中间的任意个属性。
当对于属性值,只设置一个的时候,会认为当前对象该属性值的开始 (getPropName反射获取),如果设置两个,则一个为开始,一个为结束
2,ObjectAnimator让我们动画的过程变简单,当我们不需要去实现ValueAnimator.AnimatorUpdateListener,因为该对象的动画属性会自动更新
3,ObjectAnimator会自动更新动画,因此必须有一个setter方法,去访问这个属性

关于这个setter方法,我们来看看

public  void  ViewSetter(View view){        ViewWrap wrap = new ViewWrap(mBall);        ObjectAnimator.ofFloat(wrap,"width",500)                .setDuration(5000)                .start();    }    public   class ViewWrap{        private View mTarget;        public ViewWrap(View view){            mTarget = view;        }        public int getWdith(){            return  mTarget.getLayoutParams().width;        }        public  void  setWidth(float width){            mTarget.getLayoutParams().width = (int) width;            mTarget.requestLayout();        }    }

这里写图片描述

如果我们用渐变动画来:

 ScaleAnimation scaleAnimation  = new                ScaleAnimation(1.0f,3.0f,1.0f,1.0f,                Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);        scaleAnimation.setDuration(5000);        mBall.startAnimation(scaleAnimation);

这里写图片描述

我们可以看到,它的里面的内容也在发生改变,而且还超出了屏幕范围
这样就不好看

注意一个地方,我们如果直接对Button的width属性做动画是没有效果的,虽然Button内部提供了getWidth和setWidth方法,但是这个setWidth并不是改变视图得出大小,它是TextView新增的方法,View是没有这个setWidth方法的,由于Button是继承了TextView,所以Button也就有了setWidth方法
我们看看源码:

 /**     * Makes the TextView exactly this many pixels wide.     * You could do the same thing by specifying this number in the     * LayoutParams.     *     * @see #setMaxWidth(int)     * @see #setMinWidth(int)     * @see #getMinWidth()     * @see #getMaxWidth()     *     * @attr ref android.R.styleable#TextView_width     */    @android.view.RemotableViewMethod    public void setWidth(int pixels) {        mMaxWidth = mMinWidth = pixels;        mMaxWidthMode = mMinWidthMode = PIXELS;        requestLayout();        invalidate();    }

我们看到它是用来设置TextView的最小宽度和最大宽度的,这个和TextView的宽度不是一个东西
具体来说,TextView的宽度对应Xml文件中的layout_width属性,而android:width属性就是对应setWdith方法的

在官方文档中,对于ObjectAnimator有这种解决方式:

  1. 给你的对象加上get和set方法,如果你有权限的话
  2. 用一个类来包装原始对象,间接为其提供get和set方法
  3. 采用ValueAnimator,监听动画过程,自己实现属性的改变

如果我们想要实现多个动画效果用ObejctAnimator
我们可以这样

  ObjectAnimator animator = ObjectAnimator                .ofFloat(view,"foo",0.0f,1.0f)                .setDuration(2000)                ;        animator.start();        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                float value = (float)valueAnimator.getAnimatedValue();                view.setAlpha(value);                view.setScaleX(value);            }        });

这里写图片描述

关于设置属性的那个字符串,可以随便写一个,也就是不管,我们只需要按照时间插值和持续时间计算的那个值,自己手动调用

4,我们还可以使用propertyValuesHolder

 PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",0.0f,1.0f);        PropertyValuesHolder scale = PropertyValuesHolder.ofFloat("scaleX",0.0f,1.0f);        PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotationX",0.0f,360.0f);        ObjectAnimator.ofPropertyValuesHolder(view,alpha,scale,rotate).setDuration(2000)                .start();

效果:
这里写图片描述

ValueAnimator

其实ObjectAnimator是ValueAnimator的子类,所以两个的用法很相似

 public  void  runVertical(final View view){        ValueAnimator valueAnimator =                ValueAnimator.ofFloat(0.0f,mLaout.getHeight()-mBall.getHeight());        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                float verticalValue = (float)valueAnimator.getAnimatedValue();                view.setTranslationY(verticalValue);            }        });        valueAnimator.setDuration(3000);        valueAnimator.setInterpolator(new AccelerateInterpolator());        valueAnimator.start();    }    public void paowuxian(View view){        ValueAnimator valueAnimator = new ValueAnimator();        valueAnimator.setDuration(5000);        valueAnimator.setTarget(mBall2);        valueAnimator.setInterpolator(new AccelerateInterpolator());        valueAnimator.setObjectValues(new PointF(0,0));        valueAnimator.setEvaluator(new TypeEvaluator() {            @Override            public Object evaluate(float v, Object o, Object t1) {                PointF pointF = new PointF();                pointF.set(200*v*3,(float)(0.5*200*v*v*9));                return pointF;            }        });        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                PointF pointF = (PointF)valueAnimator.getAnimatedValue();                mBall2.setX(pointF.x);                mBall2.setY(pointF.y);            }        });        valueAnimator.start();    }

这里写图片描述

我们可以看到,ValueAnimation是没有指定属性的,因此不需要操作的对象的属性一定有getter和setter方法,我们可以根据当前动画的计算值,来做操作
在做抛物线运动的时候,因为需要两个值来确定位置,x,y。所以我们用了TypeEvaluator,实现它的evluate方法,通过这个方法,我们可以返回当前动画点的自己需要的动画值

监听动画的事件

对于动画,一般都是一些辅助效果,比如要删除个元素,希望一个淡出的效果,但是最终还是删掉,并不是透明度没有了,还占着位置,所以我们需要知道动画如何结束

public void fadeOut(View view)      {          ObjectAnimator anim = ObjectAnimator.ofFloat(mBlueBall, "alpha", 0.5f);          anim.addListener(new AnimatorListener()          {              @Override              public void onAnimationStart(Animator animation)              {                  Log.e(TAG, "onAnimationStart");              }              @Override              public void onAnimationRepeat(Animator animation)              {                  // TODO Auto-generated method stub                  Log.e(TAG, "onAnimationRepeat");              }              @Override              public void onAnimationEnd(Animator animation)              {                  Log.e(TAG, "onAnimationEnd");                  ViewGroup parent = (ViewGroup) mBlueBall.getParent();                  if (parent != null)                      parent.removeView(mBlueBall);              }              @Override              public void onAnimationCancel(Animator animation)              {                  // TODO Auto-generated method stub                  Log.e(TAG, "onAnimationCancel");              }          });          anim.start();      }  

这样就可以监听动画的开始、结束、被取消、重复等事件~但是有时候会觉得,我只要知道结束就行了,这么长的代码我不能接收,那你可以使用

anim.addListener(new AnimatorListenerAdapter()  {      @Override      public void onAnimationEnd(Animator animation)      {          Log.e(TAG, "onAnimationEnd");          ViewGroup parent = (ViewGroup) mBlueBall.getParent();          if (parent != null)              parent.removeView(mBlueBall);      }  });  

AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法~

animator还有cancel()和end()方法:cancel动画立即停止,停在当前的位置,end动画直接到最终状态

AnimatorSet使用

首先是几个动画组合在一起

 public  void  playTogether(View view){        AnimatorSet set = new AnimatorSet();        ObjectAnimator alpha = ObjectAnimator.ofFloat(view,"alpha",1.0f,0.0f);        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX",1.0f,0.0f);        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY",1.0f,0.0f);        ObjectAnimator translation =                ObjectAnimator.ofFloat(view,"translationX",0,mLaout.getWidth()/3*2);        set.playTogether(alpha,scaleX,scaleY,translation);        set.setDuration(5000);        set.start();    }

这里写图片描述

然后我们来试试先后动画

 public  void  playAfter(View view){        AnimatorSet set = new AnimatorSet();        ObjectAnimator alpha = ObjectAnimator.ofFloat(view,"alpha",1.0f,0.3f);        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX",1.0f,0.3f);        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY",1.0f,0.3f);        ObjectAnimator translation =                ObjectAnimator.ofFloat(view,"translationX",0,mLaout.getWidth()/3);        ObjectAnimator alphaAfter = ObjectAnimator.ofFloat(view,"alpha",0.3f,1.0f);        ObjectAnimator scaleXAfter = ObjectAnimator.ofFloat(view,"scaleX",0.3f,1.5f);        ObjectAnimator scaleYAfter = ObjectAnimator.ofFloat(view,"scaleY",0.3f,1.5f);        set.setDuration(5000);        set.play(alpha).with(scaleX).with(scaleY).with(translation);        set.play(alphaAfter).with(scaleXAfter).with(scaleYAfter).after(translation);        set.start();    }

这里写图片描述


使用xml文件创建属性动画

<?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"      android:ordering="together" >      <objectAnimator          android:duration="1000"          android:propertyName="scaleX"          android:valueFrom="1"          android:valueTo="0.5" >      </objectAnimator>      <objectAnimator          android:duration="1000"          android:propertyName="scaleY"          android:valueFrom="1"          android:valueTo="0.5" >      </objectAnimator>  </set>  

然后在方法中加载这个动画

public void scaleX(View view){        Animator animator = AnimatorInflater.loadAnimator(this,R.animator.scalex);        animator.setTarget(view);        view.setPivotX(view.getWidth()/2);        view.setPivotY(view.getHeight()/2);        animator.start();    }

使用set标签,有一个orderring属性设置为together(还有一个值,sequentially 表示一个接一个)
这里写图片描述


布局动画

主要使用LayoutTransition为布局的容器设置动画,当容器中的视图层次发生变化时存在过渡的动画效果

过渡的类型:
LayoutTransition.APPEARING :当一个view在ViewGroup中出现时,对此View设置的动画

LayoutTransition.CHANGR_APPEARING:当一个view在ViewGroup中出现时,对此View对其他View位置造成影响,对其他View设置的动画

LayoutTransition.DISAPPEARING:当一个view在ViewGroup中消失的时候。对此view设置的动画

LayoutTransition.CHANGE.DISAPPEARING:当一个View在ViewGroup中消失时,对此View对其他View位置造成影响,对其他View设置的动画

LayoutTransition.CHANGE:不是由于VIew出现或者消失造成对其他View位置造成影响,然后对其他View设置的动画

@Override    public void onClick(View view) {        final Button button =  new Button(this);        button.setText(String.valueOf(mValue++));        mGridView.addView(button,Math.min(1,mGridView.getChildCount()));        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                mGridView.removeView(button);            }        });    }    @Override    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {        mLayoutTransition = new LayoutTransition();        mLayoutTransition.                setAnimator(LayoutTransition.APPEARING,                        (mAppear.isChecked()?                                mLayoutTransition.getAnimator(LayoutTransition.APPEARING):                        null));        mLayoutTransition.                setAnimator(LayoutTransition.CHANGE_APPEARING,                        (mAppear.isChecked()?                                mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING):                                null));        mLayoutTransition.                setAnimator(LayoutTransition.DISAPPEARING,                        (mAppear.isChecked()?                                mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING):                                null));        mLayoutTransition.                setAnimator(LayoutTransition.CHANGE_DISAPPEARING,                        (mAppear.isChecked()?                                mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING):                                null));        mGridView.setLayoutTransition(mLayoutTransition);    }

这里写图片描述

这个表现的不是很清楚,我们可以自己定义这个动画

 mLayoutTransition.                setAnimator(LayoutTransition.APPEARING,                        (mAppear.isChecked()?                                ObjectAnimator.ofFloat(button,"scaleX",0.0f,1.0f)                                :                                null));

这里写图片描述

原本的淡入淡出,变成了宽度从中间开始放大


View的anim方法

我们继续模拟下落的过程
先看代码:

 public void ViewAnim(final View view){       mBall.animate()               .y(mLaout.getHeight())               .alpha(0.3f)               .rotationBy(0)               .rotationBy(360)               .setDuration(3000)               .withEndAction(new Runnable() {                   @Override                   public void run() {                       runOnUiThread(new Runnable() {                           @Override                           public void run() {                               mBall.setY(0);                               mBall.setAlpha(1.0f);                           }                       });                   }               })               .start();   }

看效果:

这里写图片描述

啊啊啊啊GIF图好难录,大概就是这个效果,它是从顶开始的
感觉animate使得动画变得更加简单

补充:
AccelerateDecelerateInterolator  先加速后减速,开始结束时慢,中间加速

AccelerateInterpolator       加速,开始时慢中间加速

DecelerateInterpolator       减速,开始时快然后减速

AnticipateInterpolator       反向 ,先向相反方向改变一段再加速播放

AnticipateOvershootInterpolator  反向加超越,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值

BounceInterpolator        跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100

CycleIinterpolator         循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 mCycles Math.PI * input)

LinearInterpolator         线性,线性均匀改变

OvershottInterpolator       超越,最后超出目的值然后缓慢改变到目的值

TimeInterpolator        一个接口,允许你自定义interpolator,以上几个都是实现了这个接口

代码练习的链接
https://github.com/vivianluomin/PracticeEveryDay/tree/master/TestPropertyAnimation

原创粉丝点击