Android学习之属性动画基础

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值