Android开发之视图动画

577 阅读6分钟

scale标签实现(缩放)

res下新建anim文件夹,anim文件下新建scaleanim.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:fromXScale="1.0"
    android:fromYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toXScale="1.8"
    android:toYScale="1.8" />

duration:用于设置完成一次动画的持续时间,以毫秒为单位。

fillAfter:如果设置为true,控件动画结束时,将保持动画结束时的状态。不设置默认为false。

fillBefore:如果设置为true,则控件动画结束时,将还原到初始化状态。

fillEnabled:与android:fillBefore效果相同,都是在控件动画结束时,将还原到初始化状态。

fromXScale:动画起始时,控件在X轴方向上相对自身的缩放比例,浮点值。比如 1.0 代表自身无变化,0.5代表缩小1倍,2.0代表放大1倍。

fromYScale:动画起始时,控件在Y轴方向上相对自身的缩放比例,浮点值。

interpolator:用于设定插值器,其实就是指定的动画效果,如弹跳效果等。

pivotX:缩放起始点X轴坐标,可以是数值、百分数、百分数三种样式,如50、50%、50%p。如果是数值,表示在当前视图的左上角, 即原点处加上50px,作为缩放起始点X轴坐标;如果是50%则表示在当前控件的左上角加上自己宽度的50%作为缩放起始点X轴坐标; 如果是50%p,则表示在当前控件的左上角加上父控件宽度的50%作为缩放起始点X轴坐标。

pivotY:缩放起始点Y轴坐标,取值及含义与android:pivotX相同。

repeatCount:用于指定动画的重复次数,当取值 infinite时,表示无限循环。

repeatMode:用于设定重复的类型,有reverse、restart,其中reverse表示倒序回放;restart表示重放, 并且必须与repeatCount一起使用才能看到效果。

toXScale:动画结束时,控件在X轴方向上相对自身的缩放比例,浮点值。

toYScale:动画结束时,控件在Y轴方向上相对自身的缩放比例,浮点值。

activity_main.xml布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:text="开始动画" />

    <RelativeLayout
        android:id="@+id/rl"
        android:layout_width="100dp"
        android:layout_height="100dp" 
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        android:background="@color/red">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="福"
            android:textColor="@color/black"
            android:textSize="50sp"
            android:textStyle="bold" />
    </RelativeLayout>
</LinearLayout>

使用:

binding.btn.setOnClickListener(v -> {
    //所有的动画都继承自Animation类,也就是说,Animation类是所有动画(scale、alpha、translate、rotate)的基类。
    Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.scaleanim);
    binding.rl.startAnimation(animation);
});

效果图:

scale代码实现(缩放)

ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.8f, 1.0f, 1.8f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(2000);
scaleAnimation.setRepeatCount(Animation.INFINITE);
scaleAnimation.setRepeatMode(Animation.REVERSE);
binding.rl.startAnimation(scaleAnimation);

属性参数和标签属性相对应,效果和标签实现效果相同。

alpha标签实现(透明度)

Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.alphaanim);
binding.rl.startAnimation(animation);

anim下的alphaanim.xml文件

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fillBefore="true"
    android:fromAlpha="0"
    android:toAlpha="1.0" />

fromAlpha:动画开始时的透明度,取值范围为0到1.0,0表示全透明,1.0表示完全不透明。toAlpha:动画结束时的透明度,取值范围为0到1.0,0表示全透明,1.0表示完全不透明。

效果图:

alpha代码实现(透明度)

AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(3000);
alphaAnimation.setFillBefore(true);
binding.rl.startAnimation(alphaAnimation);

效果同标签效果相同。

rotate标签实现(旋转)

Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotateanim);
binding.rl.startAnimation(animation);
Animation animation2 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotateanim2);
binding.tv.startAnimation(animation2);

rotateanim.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:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="45" />

fromDegrees:动画开始旋转时的角度位置,正值代表顺时针方向的度数, 负值代表逆时针方向的度数。

toDegrees:动画结束时旋转到的角度位,正值代表顺时针方向的度数,负值代表逆时针方向的度数。

rotateanim2.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:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="-405" />

rotate代码实现(旋转)

RotateAnimation rotateAnimation = new RotateAnimation(0.0f, 45.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
binding.rl.startAnimation(rotateAnimation);
RotateAnimation rotateAnimation2 = new RotateAnimation(0.0f, -405.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation2.setDuration(2000);
rotateAnimation2.setFillAfter(true);
binding.tv.startAnimation(rotateAnimation2);

效果同上。

translate标签实现(平移)

Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.translateanim);
binding.rl.startAnimation(animation);

translateanim.xml

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:interpolator="@android:anim/cycle_interpolator"
    android:toXDelta="90%"
    android:toYDelta="90%" />

fromXDelta:起始点X轴坐标,可以是数值、百分数、百分数p,三种种样式。

fromYDelta:起始点Y轴坐标,可以是数值、百分数、百分数p,三种种样式。

android:interpolator="@android:anim/accelerate_interpolator" 加速插值器。

android:interpolator="@android:anim/decelerate_interpolator" 减速插值器。

android:interpolator="@android:anim/linear_interpolator" 匀速插值器。

android:interpolator="@android:anim/bounce_interpolator" 弹跳插值器。

android:interpolator="@android:anim/anticipate_interpolator" 初始偏离插值器。

android:interpolator="@android:anim/overshoot_interpolator" 结束偏移插值器,表示在动画结束时, 沿动画方向继续运动一段距离后再结束动画。

android:interpolator="@android:anim/anticipate_overshoot_interpolator" 是AnticipateInterpolator 与OvershootInterpolator的合体,即在动画开始时向前偏离,在动画结束时向后偏移一段距离。

android:interpolator="@android:anim/cycle_interpolator" 循环插值器,表示动画循环播放特定的次数,速率沿正弦曲线改变。

tension:初始偏离插值器偏移量、结束偏移插值器偏移量,默认值为2。

toXDelta:终点X轴坐标。

toYDelta:终点Y轴坐标。

translate代码实现(平移)

TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 90, Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 90);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
//加速插值器
//translateAnimation.setInterpolator(new AccelerateInterpolator());
//减速插值器
//translateAnimation.setInterpolator(new DecelerateInterpolator());
//线性(匀速)插值器
//translateAnimation.setInterpolator(new LinearInterpolator());
//弹跳插值器
//translateAnimation.setInterpolator(new BounceInterpolator());
//初始偏离插值器,参数float tension对应的XML属性为android:tension,表示张力值,默认值为2,值越大,
//初始的偏移量越大,而且速度越快;当直接使用 new AnticipateInterpolator()构造时,使用的是tension的默认值
//translateAnimation.setInterpolator(new AnticipateInterpolator(4));
//结束偏移插值器,表示在动画结束时,沿动画方向继续运动一段距离后再结束动画。
//translateAnimation.setInterpolator(new OvershootInterpolator(4));
//AnticipateOvershootInterpolator是AnticipateInterpolator与OvershootInterpolator的合体,
//即在动画开始时向前偏离,在动画结束时向后偏移一段距离。
//translateAnimation.setInterpolator(new AnticipateOvershootInterpolator(3,4));
//循环插值器,表示动画循环播放特定的次数,速率沿正弦曲线改变。
translateAnimation.setInterpolator(new CycleInterpolator(1));
binding.rl.startAnimation(translateAnimation);

set组合标签实现组合动画

Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.setanim);
binding.rl.startAnimation(animation);
Animation animation2 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotateanim2);
binding.tv.startAnimation(animation2);

setanim.xml

<?xml version="1.0" encoding="utf-8"?>
<!--注意:在set标签中设置repeateCount属性是无效的,必须对每个动画单独设置才有作用-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true">

    <!--scale标签(缩放)-->
    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:toXScale="1.5"
        android:toYScale="1.5" />

    <!--alpha标签(透明度)-->
    <alpha
        android:fillBefore="true"
        android:fromAlpha="0"
        android:toAlpha="1.0" />

    <!--rotate标签(旋转)-->
    <rotate
        android:fillAfter="true"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="45" />

    <!--translate标签(平移)-->
    <translate
        android:fillAfter="true"
        android:fromXDelta="-400"
        android:toXDelta="0" />
</set>

set代码实现组合动画

ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.5f, 1.0f, 1.5f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setRepeatCount(Animation.INFINITE);
scaleAnimation.setRepeatMode(Animation.REVERSE);
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
RotateAnimation rotateAnimation = new RotateAnimation(0.0f, 45.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, -400,
        Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 0);

AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(translateAnimation);
animationSet.setDuration(2000);
animationSet.setFillAfter(true);
binding.rl.startAnimation(animationSet);

Animation animation2 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotateanim2);
binding.tv.startAnimation(animation2);

setAnimationlistener()函数使用

RotateAnimation rotateAnimation3 = new RotateAnimation(0.0f, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
binding.tv.startAnimation(rotateAnimation3);
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, -400,
        Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 0);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
RotateAnimation rotateAnimation = new RotateAnimation(0.0f, 45.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
RotateAnimation rotateAnimation2 = new RotateAnimation(0.0f, -405.0f,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation2.setDuration(2000);
rotateAnimation2.setFillAfter(true);

translateAnimation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        //当动画开始时,会调用此函数
    }
    @Override
    public void onAnimationEnd(Animation animation) {
        //当动画结束时,会调用此函数通
        binding.rl.startAnimation(rotateAnimation);
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
        //当动画重复时,会调用此函数
    }
});

rotateAnimation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }
    @Override
    public void onAnimationEnd(Animation animation) {
        binding.tv.startAnimation(rotateAnimation2);
    }
    @Override
    public void onAnimationRepeat(Animation animation) {
    }
});

binding.rl.startAnimation(translateAnimation);

实现加载框动画

实现方案:使用旋转效果加上匀速插值器或加减速插值器实现。

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:text="开始动画" />

    <ImageView
        android:id="@+id/loading"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="40dp"
        android:src="@drawable/loading" />
</LinearLayout>

drawable-xxhdpi的loading.png图片:

代码实现:

RotateAnimation rotateAnim = new RotateAnimation(0, 360,
        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnim.setRepeatCount(Animation.INFINITE);
rotateAnim.setDuration(2000);
rotateAnim.setInterpolator(new LinearInterpolator());
binding.loading.startAnimation(rotateAnim);

实现效果:

实现扫描动画

实现方案:叠加错时放大加透明度实现。

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:text="开始动画" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <ImageView
        android:id="@+id/circle1"
        android:layout_width="140dp"
        android:layout_height="140dp"
        android:layout_gravity="center"
        android:src="@drawable/scan_crile" />

    <ImageView
            android:id="@+id/circle2"
            android:layout_width="140dp"
            android:layout_height="140dp"
            android:layout_gravity="center"
            android:src="@drawable/scan_crile" />

        <ImageView
            android:id="@+id/circle3"
            android:layout_width="140dp"
            android:layout_height="140dp"
            android:layout_gravity="center" 
           android:src="@drawable/scan_crile" /> 

       <ImageView
            android:id="@+id/circle4"
            android:layout_width="140dp"
            android:layout_height="140dp"
            android:layout_gravity="center"
            android:src="@drawable/scan_crile" />
    </FrameLayout>
</LinearLayout>

drawable下引用的scan_crile.xml资源文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#ff6c2f" />
</shape>

代码实现:

Animation animation1 = AnimationUtils.loadAnimation(this, R.anim.scale_alpha_anim);
Animation animation2 = AnimationUtils.loadAnimation(this, R.anim.scale_alpha_anim);
Animation animation3 = AnimationUtils.loadAnimation(this, R.anim.scale_alpha_anim);
Animation animation4 = AnimationUtils.loadAnimation(this, R.anim.scale_alpha_anim);

binding.circle1.startAnimation(animation1);

animation2.setStartOffset(600);
binding.circle2.startAnimation(animation2);

animation3.setStartOffset(1200);
binding.circle3.startAnimation(animation3);

animation1.setStartOffset(1800);
binding.circle4.startAnimation(animation4);

anim下的组合动画scale_alpha_anim.xml引用文件:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000">
    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="infinite"
        android:toXScale="3"
        android:toYScale="3" />

    <alpha
        android:fromAlpha="0.4"
        android:repeatCount="infinite"
        android:toAlpha="0" />
</set>

实现效果:

实现逐帧动画

xml实现

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"       
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:text="开始动画" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp" 
        android:layout_marginEnd="10dp"
        android:text="停止动画" />

    <ImageView
        android:id="@+id/frame_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:background="@drawable/playing_ani" />
</LinearLayout>

drawable文件下playing_ani.xml

<?xml version="1.0" encoding="utf-8"?>
<!--oneshot:设置是否只播放一次-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <!--drawable:设置图片-->
    <!--duration:播放时间设置-->
    <item
        android:drawable="@drawable/icon_gif_playing1"
        android:duration="250" />

    <item
        android:drawable="@drawable/icon_gif_playing2"
        android:duration="250" />

    <item
        android:drawable="@drawable/icon_gif_playing3"
        android:duration="250" />

    <item
        android:drawable="@drawable/icon_gif_playing4"
        android:duration="250" />
</animation-list>

drawable-xxhdpi文件夹下的图片资源:

icon_gif_playing1.png

 

icon_gif_playing2.png

  

icon_gif_playing3.png

icon_gif_playing4.png

代码实现:

private AnimationDrawable animationDrawable;

...

binding.btn.setOnClickListener(v -> {
    animationDrawable = (AnimationDrawable) binding.frameImage.getBackground();
    //开始播放逐帧动画
    animationDrawable.start();
});

binding.btnStop.setOnClickListener(v -> {
    if (animationDrawable != null && animationDrawable.isRunning()) {
        //停止播放逐帧动画
        animationDrawable.stop();
    }
});

实现效果:

代码实现

animationDrawable = new AnimationDrawable();
//按顺序遍历图片名称
for (int i = 1; i <= 4; i++) {
    //通过文件名获取资源id
    //第一个参数name:资源名称
    //第二个参数defType:资源类型
    //第三个参数defPackage:应用包名
    int id = getResources().getIdentifier("icon_gif_playing" + i, "drawable", getPackageName());
    Drawable drawable = getResources().getDrawable(id);
    animationDrawable.addFrame(drawable, 250);
}
//设置非播放一次
animationDrawable.setOneShot(false);
binding.frameImage.setBackground(animationDrawable);
animationDrawable.start();