阅读 1058

Android小知识-了解Android中的三种动画

本篇文章已授权微信公众号 顾林海 独家发布

Android中的动画分为三种:视图动画、帧动画以及属性动画,视图动画通过对View进行图像变换(平移、缩放、旋转、透明度)从而产生动画效果,帧动画可以通过顺序播放一系列图像从而产生动画,属性动画是API 11加入的,通过动态地改变对象的属性从而达到动画效果,接下来看看这三种动画的具体实现。

帧动画

帧动画比较简单,就是顺序播放一组预先定义好的图片,通过AnimationDrawable来使用,首先在xml文件中定义一个AnimationDrawable,frame_animation.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item
        android:drawable="@drawable/p1"
        android:duration="500" />
    <item
        android:drawable="@drawable/p2"
        android:duration="500" />
    <item
        android:drawable="@drawable/p3"
        android:duration="500" />
</animation-list>
复制代码

以animation-list为标签,oneshot属性代表是否循环播放,false为循环播放,true代表只播放一次,内部通过一个个item标签设置每一帧播放的图片以及动画时长,将上面的drawable作为ImageView的背景:

<?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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_frame"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:text="帧动画"/>

   <ImageView
       android:id="@+id/iv_frame"
       android:layout_width="120dp"
       android:layout_height="120dp"
       android:layout_centerInParent="true"
       android:background="@drawable/frame_animation"/>

</RelativeLayout>
复制代码

通过View的getBackground()方法获取AnimationDrawable对象,并通过AnimationDrawable的start()方法启动帧动画,代码如下:

public class MainActivity extends AppCompatActivity {

    private Button mFrameBtn;
    private ImageView mFrameIv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mFrameBtn=findViewById(R.id.btn_frame);
        mFrameIv=findViewById(R.id.iv_frame);
        mFrameBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AnimationDrawable animationDrawable= (AnimationDrawable) mFrameIv.getBackground();
                animationDrawable.start();
            }
        });
    }


}
复制代码

运行效果如下:

image

使用帧动画时需要注意每一帧图片的大小不要过大,否则会造成OOM。

视图动画

视图动画支持四种动画效果,分别是平移、缩放、旋转以及透明度。这四种动画既可以通过XML来定义,也可以通过代码来创建。

1、平移

XML定义:

在res/anim/下创建translate.xml:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fillAfter="true"
    android:duration="5000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="150"
    android:toYDelta="150" />
复制代码
  • fillAfter表示动画结束后View是否停留在结束位置,true为停留在结束位置,false为不停留。

  • duration表示动画的持续时间。

  • fromXDelta表示x的起始值。

  • fromYDelta表示y的起始值。

  • toXDelta表示x的结束值。

  • toYDelta表示y的结束值。

  • interpolator表示插值器,插值器会影响动画的速度,常用的插值器如下:

android:interpolator="@android:anim/accelerate_interpolator" 设置动画为加速动画(动画播放中越来越快)  

android:interpolator="@android:anim/decelerate_interpolator" 设置动画为减速动画(动画播放中越来越慢)  

android:interpolator="@android:anim/accelerate_decelerate_interpolator" 设置动画为先加速在减速(开始速度最快 逐渐减慢)  

android:interpolator="@android:anim/anticipate_interpolator" 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)  

android:interpolator="@android:anim/anticipate_overshoot_interpolator" 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)  

android:interpolator="@android:anim/bounce_interpolator" 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)  

android:interpolator="@android:anim/cycle_interpolator" 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)  

android:interpolator="@android:anim/linear_interpolator" 线性均匀改变  

android:interpolator="@android:anim/overshoot_interpolator" 加速执行,结束之后回弹
复制代码

使用方式如下:

        mTranslateBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.translate);
                mImageIv.startAnimation(animation);
            }
        });
复制代码

通过AnimationUtils的静态方法loadAnimation加载translate文件,再通过View的startAnimation方法传入Animation对象并启动动画。

运行效果如下:

image

代码实现如下:

        final TranslateAnimation translateAnimation=new TranslateAnimation(0,150,0,150);
        translateAnimation.setDuration(5000);
        translateAnimation.setFillAfter(true);
        translateAnimation.setInterpolator(new AccelerateInterpolator());
        mTranslateBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mImageIv.startAnimation(translateAnimation);
            }
        });
复制代码

运行效果和上面的一样,其中interpolator、fillAfter以及duration也适用于其他三种动画效果,更多属性请查询官方文档。

2、缩放

XML定义:

在res/anim/下创建scale.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fromXScale="1"
    android:fromYScale="1"
    android:toXScale="0.5"
    android:toYScale="0.5"
    android:pivotX="50%"
    android:pivotY="50%"/>
复制代码
  • fromXScale表示水平方向缩放的起始值。

  • fromYScale表示竖直方向缩放的起始值。

  • toXScale表示水平方向的结束值。

  • toYScale表示竖直方向的结束值。

  • pivotX表示缩放的轴点的x坐标,缩放起点x轴坐标,取值可以是数值(50)、百分数(50%)、百分数p(50%p),当取值为数值时,缩放起点为View左上角坐标加具体数值像素,当取值为百分数时,表示在当前View左上角坐加上View宽度的具体百分比,当取值为百分数p时,表示在View左上角坐标加上父控件宽度的具体百分比。

  • pivotY表示缩放的轴点的y坐标,取值含义与pivotX相同。

使用方式如下:

          mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.scale);
                mImageIv.startAnimation(animation);
            }
        });
复制代码

运行效果:

image

代码实现如下:

        final ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f,
                1, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnimation.setDuration(5000);
        scaleAnimation.setFillAfter(true);
        scaleAnimation.setInterpolator(new AccelerateInterpolator());
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mImageIv.startAnimation(scaleAnimation);
            }
        });
复制代码

3、旋转

XML定义:

在res/anim/下创建rotate.xml:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:repeatCount="-1"
    android:pivotX="50%"
    android:pivotY="50%"/>
复制代码
  • fromDegrees表示旋转的开始角度

  • toDegrees表示旋转的开始角度

  • repeatCount表示播放次数,-1表示循环播放

使用方式:

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation rotateAnimation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.rotate);
                mImageIv.startAnimation(rotateAnimation);
            }
        });
复制代码

运行效果:

image

代码实现如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = findViewById(R.id.btn_translate);
        mImageIv = findViewById(R.id.iv_image);
        final RotateAnimation rotateAnimation = new RotateAnimation(0,360,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setDuration(2000);
        rotateAnimation.setFillAfter(true);
        rotateAnimation.setInterpolator(new AccelerateInterpolator());
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mImageIv.startAnimation(rotateAnimation);
            }
        });
    }
复制代码

4、透明度

XML定义:

在res/anim/下创建alpha.xml:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fromAlpha="1"
    android:toAlpha="0"
    android:pivotX="50%"
    android:pivotY="50%"/>
复制代码
  • fromAlpha表示透明度的起始值

  • toAlpha表示透明度的结束值

使用方式:

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.alpha);
                mImageIv.startAnimation(animation);
            }
        });
复制代码

运行效果:

image

代码实现如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = findViewById(R.id.btn_translate);
        mImageIv = findViewById(R.id.iv_image);
        final AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f,0.0f);
        alphaAnimation.setDuration(2000);
        alphaAnimation.setFillAfter(true);
        alphaAnimation.setInterpolator(new AccelerateInterpolator());
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mImageIv.startAnimation(alphaAnimation);
            }
        });
    }
复制代码

除了上面单个执行动画外,我们可以同时执行多个,在xml中通过set标签,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fillAfter="false"
    android:shareInterpolator="true">

    <alpha
        android:duration="2000"
        android:fromAlpha="1"
        android:toAlpha="0"
        android:pivotX="50%"
        android:pivotY="50%"/>

    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"/>

    <scale
        android:duration="2000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="0.5"
        android:toYScale="0.5"
        android:pivotX="50%"
        android:pivotY="50%"/>

    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="150"
        android:toYDelta="150" />
</set>
复制代码

使用方式:

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.set);
                mImageIv.startAnimation(animation);
            }
        });
复制代码

运行效果:

image

代码实现如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = findViewById(R.id.btn_translate);
        mImageIv = findViewById(R.id.iv_image);
        final AnimationSet animationSet=new AnimationSet(true);
        animationSet.setFillAfter(false);
        animationSet.setDuration(2000);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f,0.0f);
        RotateAnimation rotateAnimation = new RotateAnimation(0,360,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f,
                1, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        TranslateAnimation translateAnimation=new TranslateAnimation(0,150,0,150);
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(rotateAnimation);
        animationSet.addAnimation(scaleAnimation);
        animationSet.addAnimation(translateAnimation);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mImageIv.startAnimation(animationSet);
            }
        });
    }
复制代码

动画播放的相关操作可以通过AnimationListener接口来监听,实现方式如下:

        final AnimationSet animationSet=new AnimationSet(true);
        animationSet.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                //动画开始
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //动画结束
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                //重复播放
            }
        });
复制代码

属性动画

属性动画是API 11新加入的,除了能实现上面视图动画的四种动画效果外,还可以持续更加绚丽的效果,同时修复了视图动画带来的不可点击的问题,当View通过视图动画进行一系列的操作后,比如向右偏移100,这时点击这个View时没有任何反应,点击一开始的位置,也就是空白的地方,会触发点击事件,事件不能随动画的变换而变化,新加入的属性动画修复了这些问题。下面主要介绍属性动画中的ValueAnimator、ObjectAnimator以及AnimatorSet等概念。

属性动画中使用ObjectAnimator进行更精细化的控制,控制一个对象和一个属性值,使用多个ObjectAnimator组合到AnimatorSet形成一个动画。属性动画可以通过属性get、set方法来真实地控制一个View的属性值。

ObjectAnimator

ObjectAnimator是属性动画中非常重要的类,创建一个ObjectAnimator只需要通过它的静态工厂类直接返回一个ObjectAnimator对象,参数包括一个对象和对象的属性名字,但这个属性必须有get和set方法,内部会通过反射机制来调用set方法修改对象的属性值。

private void animator(){
    ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mBtnNormal,"translationX",100);
    objectAnimator.setDuration(3000);
    objectAnimator.start();
}
复制代码

以上代码完成mBtnNormal按钮向右平移100。

可以给ObjectAnimator对象设置时长、插值器等属性。

View常用的属性动画的属性值有以下几种:

  • translationX和translationY:沿x轴或y轴进行平移。

  • rotation、rotationX、rotationY:按指定支点进行旋转。

  • PrivotX和PrivotY:设置支点位置。

  • alpha:透明度。

  • x和y:坐标位置。

使用ObjectAnimator时,被操作对象的属性值必须有set和get方法,如果没有,可以定义一个属性类或包装类来间接地给这个属性增加get和set方法。

ObjectAnimator提供了addListener方法:

private void animator(){
    ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mBtnNormal,"translationX",100);
    objectAnimator.setDuration(3000);
    //监听方式一
    objectAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
        }
    });
    //监听方式二
    objectAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {

        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    objectAnimator.start();
}
复制代码

其中Animator.AnimatorListener必须实现它的4个方法,而AnimatorListenerAdapter可以按需实现相关方法。

ValueAnimator

ValueAnimator本身不会实现任何动画效果,可以通过它产生一定规律的数字,通过这些数字控制动画的实现过程,可以使用AnimatorUpdateListener来监听数值的变化。

private void valueAnimator(){
    ValueAnimator animator=ValueAnimator.ofFloat(0,1000);
    animator.setDuration(3000);
    animator.setTarget(mBtnNormal);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value=animation.getAnimatedFraction();
        }
    });

    animator.start();
}
复制代码
AnimatorSet

AnimatorSet是组合动画,提供了一个play()方法,可以传入Animator对象(ObjectAnimator或ValueAnimator)。play()方法会返回一个AnimatorSet.Builder实例,内部提供了以下4个方法:

  1. after(Animator anim):将现有动画插入到传入的动画之后执行。

  2. after(long delay):将现有动画延迟指定毫秒后执行。

  3. before(Animator anim):将现有动画插入到传入的动画之前执行。

  4. with(Animator anim):将现有动画和传入的动画同时执行。

private void animatorSet(){
    ObjectAnimator animator1=ObjectAnimator.ofFloat(mIvImage,"translationX",0,200,0);
    ObjectAnimator animator2=ObjectAnimator.ofFloat(mIvImage,"scaleX",1.0f,2.0f,1.0f);
    ObjectAnimator animator3=ObjectAnimator.ofFloat(mIvImage,"rotationX",0.0f,360.0f);
    AnimatorSet animationSet=new AnimatorSet();
    animationSet.setDuration(3000);
    animationSet.play(animator1).with(animator2).after(animator3);
    animationSet.start();
}
复制代码

效果如下:

image


838794-506ddad529df4cd4.webp.jpg

关注下面的标签,发现更多相似文章
评论