Android 补间(Tween)动画

2022-02-15  本文已影响0人  gaookey
image.jpg

补间动画就是指开发者只需指定动画开始、动画结束等“关键帧”,而动画变化的“中间帧”由系统计算并补齐

TWeen 动画与 Interpolator

Interpolator 根据特定算法计算出整个动画所需要动态插入帧的密度和位置。简单地说,Interpolator 负责控制动画的变化速度,这就使得基本的动画效果(AlphaScaleTranslateRotate)能以匀速变化、加速、减速、抛物线速度等各种速度变化。
Interpolator 是一个接口,它定义了所有 Interpolator 都需要实现的方法:float getInterpolation(float input),开发者完全可以通过实现 Interpolator 来控制动画的变化速度。

Android 为 Interpolator 提供了如下几个实现类,分别用于实现不同的动画变化速度。

位置、大小、旋转度、透明度改变的补间动画

MainActivity

利用 AnimationUtils 工具类来加载指定的动画资源,加载成功后会返回一个 Animation,该对象即可控制图片或视图播放动画。

public class MainActivity extends AppCompatActivity {
    private ImageView flower;
    private Animation reverse;

    class MyHandler extends Handler {
        private WeakReference<MainActivity> activity;

        public MyHandler(WeakReference<MainActivity> activity) {
            this.activity = activity;
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x123) {
                activity.get().flower.startAnimation(activity.get().reverse);
            }
        }
    }

    Handler handler = new MyHandler(new WeakReference<>(this));

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        flower = findViewById(R.id.flower);
        // 加载第一份动画资源
        Animation anim = AnimationUtils.loadAnimation(this, R.anim.anim);
        // 设置动画结束后保留结束状态
        anim.setFillAfter(true);
        // 加载第二份动画资源
        reverse = AnimationUtils.loadAnimation(this, R.anim.reverse);
        // 设置动画结束后保留结束状态
        reverse.setFillAfter(true);
        Button bn = findViewById(R.id.bn);
        bn.setOnClickListener(view -> {
            flower.startAnimation(anim);
            // 设置3.5秒后启动第二个动画
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    handler.sendEmptyMessage(0x123);
                }
            }, 3500);
        });
    }
}

anim/anim.xml

指定动画匀速变化,同时进行缩放、透明度、旋转三种改变,动画持续时间为 3 秒。

<?xml version="1.0" encoding="UTF-8"?><!-- 指定动画匀速改变 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator">
    <!-- 定义缩放变换 -->
    <scale
        android:duration="3000"
        android:fillAfter="true"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.01"
        android:toYScale="0.01" />
    <!-- 定义透明度的变化 -->
    <alpha
        android:duration="3000"
        android:fromAlpha="1"
        android:toAlpha="0.05" />
    <!-- 定义旋转变换 -->
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="1800" />
</set>

anim/reverse.xml

控制图片以动画的方式恢复回来

<?xml version="1.0" encoding="UTF-8"?><!-- 指定动画匀速改变 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator">
    <!-- 定义缩放变换 -->
    <scale
        android:duration="3000"
        android:fillAfter="true"
        android:fromXScale="0.01"
        android:fromYScale="0.01"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1" />
    <!-- 定义透明度的变化 -->
    <alpha
        android:duration="3000"
        android:fromAlpha="0.05"
        android:toAlpha="1" />
    <!-- 定义旋转变换 -->
    <rotate
        android:duration="3000"
        android:fromDegrees="1800"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="0" />
</set>
image.gif

实例:蝴蝶飞舞

结合逐帧动画和补间动画。蝴蝶飞行时的振翅效果是逐帧动画,蝴蝶飞行时的位置改变时补间动画。

MainActivity

public class MainActivity extends AppCompatActivity {
    // 记录蝴蝶ImageView当前的位置
    private float curX;
    private float curY = 30f;
    // 记录蝴蝶ImageView下一个位置的坐标
    private float nextX;
    private float nextY;
    private ImageView imageView;
    private float screenWidth;

    static class MyHandler extends Handler {
        private WeakReference<MainActivity> activity;

        MyHandler(WeakReference<MainActivity> activity) {
            this.activity = activity;
        }

        @Override
        public void handleMessage(Message msg) {
            if (activity.get() != null && msg.what == 0x123) {
                MainActivity act = activity.get();
                // 横向上一直向右飞
                if (act.nextX > act.screenWidth) {
                    act.nextX = 0f;
                    act.curX = act.nextX;
                } else {
                    act.nextX += 8f;
                }
                // 纵向上可以随机上下
                act.nextY = act.curY + (float) (Math.random() * 10 - 5);
                // 设置显示蝴蝶的ImageView发生位移改变
                TranslateAnimation anim = new TranslateAnimation(act.curX, act.nextX, act.curY, act.nextY);
                act.curX = act.nextX;
                act.curY = act.nextY;
                anim.setDuration(200);
                // 开始位移动画
                act.imageView.startAnimation(anim);  // ①
            }
        }
    }

    private Handler handler = new MyHandler(new WeakReference<>(this));

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Point p = new Point();
        // 获取屏幕宽度
        getWindowManager().getDefaultDisplay().getSize(p);
        screenWidth = p.x;
        // 获取显示蝴蝶的ImageView组件
        imageView = findViewById(R.id.butterfly);
        AnimationDrawable butterfly = (AnimationDrawable) imageView.getBackground();
        imageView.setOnClickListener(view -> {
            // 开始播放蝴蝶振翅的逐帧动画
            butterfly.start();  // ②
            // 通过定时器控制每0.2秒运行一次TranslateAnimation动画
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    handler.sendEmptyMessage(0x123);
                }
            }, 0, 200);
        });
    }
}

drawable/butterfly.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/butterfly_f01"
        android:duration="120" />
    <item
        android:drawable="@drawable/butterfly_f02"
        android:duration="120" />
    <item
        android:drawable="@drawable/butterfly_f03"
        android:duration="120" />
    <item
        android:drawable="@drawable/butterfly_f04"
        android:duration="120" />
    <item
        android:drawable="@drawable/butterfly_f05"
        android:duration="120" />
    <item
        android:drawable="@drawable/butterfly_f06"
        android:duration="120" />
</animation-list>
image.gif

自定义补间动画

自定义补间动画需要继承 ,继承 时重写该抽象基类的 applyTransformation(float interpolatedTime, Transformation t) 方法。

Transformation 代表了对图片或视图的变形程度,该对象里封装了一个 Matrix 对象,对它所包装的 Matrix 进行位移、倾斜、旋转等变换时,Transformation 将会控制对应的图片或视图进行相应的变换。

Camera 控制图片或视图进行三维空间的变换。常用方法:

getMatrix(Matrix matrix)Camera 所做的变换应用到指定 matrix 上。
rotateX(float deg) 使目标组件沿X轴旋转。
rotateY(float deg) 使目标组件沿Y轴旋转。
rotateZ(float deg) 使目标组件沿Z轴旋转。
translate(float x, float y, float z) 使目标组件在三维空间里进行位移变换。
applyToCanvas(Canvas canvas)Camera 所做的变换应用到 Canvas 上。

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取ListView组件
        ListView list = findViewById(R.id.list);
        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics metrice = new DisplayMetrics();
        // 获取屏幕的宽和高
        display.getMetrics(metrice);
        // 设置对ListView组件应用动画
        list.setAnimation(new MyAnimation(metrice.xdpi / 2,
                metrice.ydpi / 2, 3500));
    }
}

MyAnimation

public class MyAnimation extends Animation {
    private float centerX;
    private float centerY;
    private int duration;
    private Camera camera = new Camera();

    public MyAnimation(float x, float y, int duration) {
        this.centerX = x;
        this.centerY = y;
        this.duration = duration;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        // 设置动画的持续时间
        setDuration(duration);
        // 设置动画结束后效果保留
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
    }

    /*
     * 该方法的interpolatedTime代表了抽象的动画持续时间,不管动画实际持续时间多长
     * interpolatedTime参数总是从0(动画开始时)变化到1(动画结束时)
     * Transformation参数代表了对目标组件所做的改变
     */
    @Override
    public void applyTransformation(float interpolatedTime, Transformation t) {
        camera.save();
        // 根据interpolatedTime时间来控制X、Y、Z上的偏移
        camera.translate(100.0f - 100.0f * interpolatedTime,
                150.0f * interpolatedTime - 150,
                80.0f - 80.0f * interpolatedTime);
        // 设置根据interpolatedTime时间在Y轴上旋转不同角度
        camera.rotateY(360 * interpolatedTime);
        // 设置根据interpolatedTime时间在X轴上旋转不同角度
        camera.rotateX(360 * interpolatedTime);
        // 获取Transformation参数的Matrix对象
        Matrix matrix = t.getMatrix();
        camera.getMatrix(matrix);
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
        camera.restore();
    }
}

layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:entries="@array/bookArray" />
</LinearLayout>

values/arrays.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <string-array name="bookArray">
        <item>疯狂Java讲义</item>
        <item>轻量级Java EE企业应用实战</item>
        <item>疯狂Swift讲义</item>
        <item>疯狂前端开发讲义</item>
        <item>疯狂Android讲义</item>
    </string-array>
</resources>
image.gif

摘抄至《疯狂Android讲义(第4版)》

上一篇 下一篇

猜你喜欢

热点阅读