美文网首页
安卓动画

安卓动画

作者: migill | 来源:发表于2019-10-17 18:57 被阅读0次

    Android 中的动画可以分为以下几类:

    • 逐帧动画
    • 补间动画
    • 属性动画

    1、逐帧动画

    逐帧动画的原理就是让一系列的静态图片依次播放,利用人眼“视觉暂留”的原理,实现动画。

    步骤:
    1、res/drawable 下新建 xml 文件,这里定义动画的每一帧,素材图片放到 drawable 下。
    frame_animation.xml

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="true">
        <item android:drawable="@drawable/frame01" android:duration="100"/>
        <item android:drawable="@drawable/frame02" android:duration="100"/>
        <item android:drawable="@drawable/frame03" android:duration="100"/>
        <item android:drawable="@drawable/frame04" android:duration="100"/>
        <item android:drawable="@drawable/frame05" android:duration="100"/>
        <item android:drawable="@drawable/frame06" android:duration="100"/>
        <item android:drawable="@drawable/frame07" android:duration="100"/>
    </animation-list>
    

    2、布局中将 AnimationDrawable 对象直接作为背景

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <ImageView
            android:layout_marginTop="50dp"
            android:layout_centerHorizontal="true"
            android:id="@+id/frame_image"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@drawable/frame_animation"
            />
    </RelativeLayout>
    

    3、Activity 中控制播放

    public class FrameAnimation extends AppCompatActivity {
        ImageView frame_image;
        AnimationDrawable animationDrawable;
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_frame_animation);
            frame_image = findViewById(R.id.frame_image);
            // 获取 AnimationDrawable 对象
             animationDrawable = (AnimationDrawable) frame_image.getBackground();
            // 开始播放
            animationDrawable.start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
             //停止播放
            animationDrawable.stop();
        }
    }
    

    利用 Java 代码实现逐帧动画

    public class FrameAnimation extends Activity {
        ImageView frame_image;
        AnimationDrawable animationDrawable;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_frame_animation);
            frame_image = findViewById(R.id.frame_image);
            animationDrawable = new AnimationDrawable();
    
            for (int i = 1; i < 10; i++) {
                int id = getResources().getIdentifier("frame0" + i, "drawable", getPackageName());
                Drawable drawable = getResources().getDrawable(id);
                animationDrawable.addFrame(drawable, 100);
            }
            animationDrawable.setOneShot(true);
            frame_image.setImageDrawable(animationDrawable);
            animationDrawable.stop();
            // 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
            animationDrawable.start();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            animationDrawable.setOneShot(true);
            frame_image.setImageDrawable(animationDrawable);
            animationDrawable.stop();
        }
    }
    

    2、补间动画

    补间动画就是指开发者指定动画的开始、动画的结束的"关键帧",而动画变化的"中间帧"由系统计算,并补齐。文件一般存放在res/anim文件夹下,访问时采用R.anim.XXX.xml的方式。
    补间动画有四种:

    • 淡入淡出: alpha
    • 位移:translate
    • 缩放:scale
    • 旋转: rotate

    scale标签——调节尺寸
    android:fromXScale:起始的X方向上相对自身的缩放比例,浮点值,比如1.0代表自身无变化,0.5代表起始时缩小一倍,2.0代表放大一倍;
    android:toXScale:结尾的X方向上相对自身的缩放比例,浮点值;
    android:fromYScale:起始的Y方向上相对自身的缩放比例,浮点值,
    android:toYScale:结尾的Y方向上相对自身的缩放比例,浮点值;
    android:pivotX :缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,当为数值时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;如果是50%,表示在当前控件的左上角加上自己宽度的50%做为起始点;如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标。
    android:pivotY:缩放起点Y轴坐标,取值及意义跟android:pivotX一样。

    从Animation类继承的属性
    android:duration:动画持续时间,以毫秒为单位
    android:fillAfter:如果设置为true,控件动画结束时,将保持动画最后时的状态
    android:fillBefore:如果设置为true,控件动画结束时,还原到开始动画前的状态
    android:fillEnabled:与android:fillBefore 效果相同,都是在动画结束时,将控件还原到初始化状态
    android:repeatCount:重复次数
    android:repeatMode:重复类型,有reverse和restart两个值,reverse表示倒序回放,restart表示重新放一遍,必须与repeatCount一起使用才能看到效果。因为这里的意义是重复的类型,即回放时的动作。
    android:interpolator:设定插值器,其实就是指定的动作效果,比如弹跳效果等,不在这小节中讲解,后面会单独列出一单讲解。
    android:duration:动画的持续时长,以毫秒为单位

    alpha标签——调节透明度
    android:fromAlpha 动画开始的透明度,从0.0 --1.0 ,0.0表示全透明,1.0表示完全不透明
    android:toAlpha 动画结束时的透明度,也是从0.0 --1.0 ,0.0表示全透明,1.0表示完全不透明

    rotate标签——旋转
    android:fromDegrees:开始旋转的角度位置,正值代表顺时针方向度数,负值代码逆时针方向度数
    android:toDegrees:结束时旋转到的角度位置,正值代表顺时针方向度数,负值代码逆时针方向度数
    android:pivotX:缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,具体意义已在scale标签中讲述,这里就不再重讲
    android:pivotY:缩放起点Y轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p

    translate标签 —— 平移
    android:fromXDelta:起始点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,具体意义已在scale标签中讲述,这里就不再重讲
    android:fromYDelta:起始点Y轴从标,可以是数值、百分数、百分数p 三种样式;
    android:toXDelta:结束点X轴坐标
    android:toYDelta:结束点Y轴坐标

    示例:
    1、定义动画资源:
    res\anim\tween_anim.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        >
        <scale
            android:duration="3000"
            android:fromXScale="0.0"
            android:fromYScale="0.0"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toXScale="1.0"
            android:toYScale="1.0"/>
        <alpha
            android:duration="3000"
            android:fromAlpha="1.0"
            android:toAlpha="0.5" />
        <rotate
            android:fromDegrees="0"
            android:toDegrees="720"
            android:pivotX = "50%"
            android:pivotY="50%"
            android:duration = "3000"
            />
        <translate
            android:fromXDelta="0"
            android:toXDelta="100"
            android:fromYDelta="0"
            android:toYDelta="100" />
    </set>
    

    2、Animation 控制图片播放动画

    public class tweenAnimation extends Activity {
        // tween_image;
        Button tween_start;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_tween_animation);
    
            final ImageView  tween_image = findViewById(R.id.tween_image);
            tween_start = findViewById(R.id.tween_start);
            
            // 加载动画资源
            final Animation anim = AnimationUtils.loadAnimation(this,R.anim.tween_anim);
            //设置动画结束后保留结束状态
            anim.setFillAfter(true);
            
            tween_start.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    tween_image.startAnimation(anim);
                }
            });
            
        }
    }
    

    这几个动画可以组合在一起使用,同时完成缩放、透明的、旋转或者位移等的变化。

    3、属性动画

    工作原理 指定时间内,修改属性(对象中对应的字段)的值,以此实现该对象在属性上的动画效果。

    属性动画的使用:

    ObjectAnimator animator = ObjectAnimator.ofFloat(button, "ScaleX", 1f, 2f, 3f, 1f);
    animator.setInterpolator(new LinearInterpolator());
    animator.setDuration(3000);
    animator.start();
    

    动画的本质:
    动画实际上是改变View在某一时间点的样式属性,比如在0.1s的时候View的坐标是100px,在0.2s的时候改变成了200px,用户感觉View在向右移动
    实际上是通过一个线程每隔一段时间通过调用View.setX()改变属性值,也能产生动画效果

    为什么要将动画分解成不同的关键帧:
    动画是需要时间开销才能够完成的,如果不给出关键帧动画,动画的过程将无法控制。在不同的时间点,控件的状态也不一样。

    什么时候开始绘制呢?
    在Choreographer类里面存在FrameDisplayEventReceiver对象,用于接收Vsync信号,接收到信号之后就会回调ViewRootImpl里面TraversalRunnable的run方法。


    自定义动画框架的实现:


    实现属性动画实际是通过反射获取View中的方法,在不同的时间改变对象的属性值

       //每隔16ms执行一次
        @Override
        public boolean doAnimationFrame(long currentTime) {
            //后续的效果渲染
            //动画的总帧数
            float total = mDuration / 16;
            //拿到执行百分比 (index)/total
            float fraction = (index++) / total;
            //通过插值器去改变对应的执行百分比
            if (interpolator != null) {
                fraction = interpolator.getInterpolator(fraction);
            }
            //循环 repeat
            if (index >= total) {
                index = 0;
            }
            //交给mFloatPropertyValuesHolder,改变对应的属性值
            mFloatPropertyValuesHolder.setAnimatedValue(mTarget.get(), fraction);
            return false;
        }
    
    public class MyFloatPropertyValuesHolder {
        //属性名
        String mPropertyName;
        //属性类型 float
        Class mValueType;
        //反射
        Method mSetter = null;
        //关键帧管理类
        MyKeyframeSet mKeyframeSet;
    
        public MyFloatPropertyValuesHolder(String propertyName, float... values) {
            this.mPropertyName = propertyName;
            mValueType = float.class;
            //交给关键帧管理初始化
            mKeyframeSet = MyKeyframeSet.ofFloat(values);
        }
    
        //通过反射获取控件对应的方法
        public void setupSetter() {
            char firstLetter = Character.toUpperCase(mPropertyName.charAt(0));
            String theRest = mPropertyName.substring(1);
            //setScaleX
            String methodName = "set" + firstLetter + theRest;
            try {
                mSetter = View.class.getMethod(methodName, float.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    
        //给控件设置相应的属性值
        public void setAnimatedValue(View view, float fraction) {
            Object value = mKeyframeSet.getValue(fraction);
            try {
                mSetter.invoke(view, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:安卓动画

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