美文网首页
Android 补间(Tween)动画

Android 补间(Tween)动画

作者: gaookey | 来源:发表于2022-02-15 15:02 被阅读0次
    image.jpg

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

    TWeen 动画与 Interpolator
    • AlphaAnimation 透明度改变的动画。创建该动画时要指定动画开始时的透明度、结束时的透明度和动画持续时间。其中透明度可从0变化到1。
    • ScaleAnimation 大小缩放的动画。创建该动画时要指定动画开始时的缩放比(以X、Y轴的缩放参数来表示)、结束时的缩放比(以X、Y轴的缩放参数来表示),并指定动画持续时间。由于缩放时以不同点为中心的缩放效果并不相同,因此指定缩放动画时还需要通过 pivotXpivotY 来指定"缩放中心"的坐标。
    • TranslateAnimation 位移变化的动画。创建该动画时只要指定动画开始时的位置(以X、Y坐标来表示)、结束时的位置(以X、Y坐标来表示),并指定动画持续时间即可。
    • RotateAnimation 旋转动画。创建该动画时只要指定动画开始时的旋转角度、结束时的旋转角度,并指定动画持续时间即可。由于旋转时以不同点为中心的旋转效果并不相同,因此指定旋转动画时还要通过 pivotXpivotY来指定"旋转轴心"的坐标。

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

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

    • LinearInterpolator:动画以均匀的速度改变。
    • AccelerateInterpolator:在动画开始的地方改变速度较慢,然后开始加速。
    • AccelerateDecelerateInterpolator: 在动画开始、结束的地方改变速度较慢,在中间的时候加速。
    • CycleInterpolator:动画循环播放特定的次数,变化速度按正弦曲线改变。
    • DecelerateInterpolator:在动画开始的地方改变速度较快,然后开始减速。

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

    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) 方法。

    • interpolatedTime 代表了动画的时间进行比。不管动画实际的持续时间如何,当动画播放时,该参数总是自动从 0 变化到 1.
    • 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版)》

    相关文章

      网友评论

          本文标题:Android 补间(Tween)动画

          本文链接:https://www.haomeiwen.com/subject/mjhdlrtx.html