Android动画用法大全总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33706840/article/details/81475879

一、FrameAnimation(帧动画)

依赖于完整的UI资源,按顺序依次播放图片集合,存在一定局限性,资源文件占用太大。

  • 在drawable目录下创建资源文件animation_list.xml。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/a_0"
        android:duration="100" />
    <item
        android:drawable="@drawable/a_1"
        android:duration="100" />
    <item
        android:drawable="@drawable/a_2"
        android:duration="100" />
</animation-list>
ImageView img= (ImageView) findViewById(R.id.animation1);
img.setImageResource(R.drawable.frame_anim);
AnimationDrawable animationDrawable = (AnimationDrawable) img.getDrawable();
animationDrawable.start();

二、TweenedAnimation(补间动画)

  • 补间动画又可以分为四种形式,分别是 alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
xml形式实现:
  • 淡入淡出动画
  • 在anim目录下创建alpha_anim.xml。
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:toAlpha="0.0" />
  • 要实现的java代码
Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.alpha_anim);
ImageView img = (ImageView) findViewById(R.id.img);
img.startAnimation(animation);
其它三种实现方式类似,具体属性如下:
Rotate属性详解
XML属性 java代码 解释
android:fromDegrees RotateAnimation(float fromDegrees, , , ) 旋转开始角度,正代表顺时针度数,负代表逆时针度数
android:toDegrees RotateAnimation(, float toDegrees, ,) 旋转结束角度,正代表顺时针度数,负代表逆时针度数
android:pivotX RotateAnimation(, ,float pivotX, ) 缩放起点X坐标(数值、百分数、百分数p)如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点
android:pivotY RotateAnimation(, , , float pivotY) 缩放起点Y坐标,同上规律
Scale属性详解
XML属性 java代码 解释
android:fromXScale ScaleAnimation(float fromX, …) 初始X轴缩放比例,1.0表示无变化
android:toXScale ScaleAnimation(…, float toX, …) 结束X轴缩放比例
android:fromYScale ScaleAnimation(…, float fromY, …) 初始Y轴缩放比例
android:toYScale ScaleAnimation(…, float toY, …) 结束Y轴缩放比例
android:pivotX ScaleAnimation(…, float pivotX, …) 缩放起点X坐标(数值、百分数、百分数p)如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点
android:pivotY ScaleAnimation(…, float pivotY) 缩放起点Y
Animation属性详解
XML属性 java代码 解释
android:detachWallpaper setDetachWallpaper(boolean) 是否在壁纸上运行
android:duration setDuration(long) 动画持续时间,毫秒为单位
android:fillAfter setFillAfter(boolean) 控件动画结束时是否保持动画最后的状态
android:fillBefore setFillBefore(boolean) 控件动画结束时是否还原到开始动画前的状态
android:fillEnabled setFillEnabled(boolean) 与android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 设定插值器(指定的动画效果,譬如回弹等)
android:repeatCount setRepeatCount(int) 重复次数
android:repeatMode setRepeatMode(int) 重复类型有两个值,reverse表示倒序回放,restart表示从头播放
android:startOffset etStartOffset(long) 调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustment setZAdjustment(int) 表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal

插值器

  • AccelerateDecelerateInterpolator 变化率开始和结束缓慢但在中间加速。
  • AccelerateInterpolator 变化率开始缓慢然后加速。
  • AnticipateInterpolator 变化开始向后然后向前。
  • AnticipateOvershootInterpolator 变化开始向后,向前晃动并超过目标值,然后最终返回到最终值。
  • BounceInterpolator 变化在结束时反弹。
  • CycleInterpolator 动画重复指定的周期数。
  • DecelerateInterpolator 变化率快速开始然后减速。
  • LinearInterpolator 变化率恒定。
  • OvershootInterpolator 变化向前晃动并超过一个值,然后返回。
  • TimeInterpolator 一个允许您实现自己的插补器的接口。
  • PathInterpolator 定义路径坐标后按照路径坐标来跑。

##三、属性动画

ValueAnimator使用简介

  • ValueAnimator.ofInt(int values)使用

    (1)在res/animator的文件夹里创建相应的xml文件。

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0" // 初始值
    android:valueTo="100" // 结束值
    android:valueType="intType" // 变化值类型 :floatType & intType
    android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
    android:startOffset ="1000" // 动画延迟开始时间(ms)
    android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
    android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
    android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
    android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
/> 

在Java代码中启动动画

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);  
// 设置动画对象
animator.setTarget(view);  
animator.start();  

(2)java代码设置(实际开发中建议使用…)

// 步骤1:设置动画属性的初始值 & 结束值
ValueAnimator anim = ValueAnimator.ofInt(0, 3);
        anim.setDuration(500);
        // 设置动画运行的时长
        anim.setStartDelay(500);
        // 设置动画延迟播放时间
        anim.setRepeatCount(0);
        // 设置动画重复播放次数 = 重放次数+1,动画播放次数 = infinite时,动画无限重复
        anim.setRepeatMode(ValueAnimator.RESTART);
        // 设置重复播放动画模式,ValueAnimator.RESTART(默认):正序重放,ValueAnimator.REVERSE:倒序回放

        // 设置 值的更新监听器
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int currentValue = (Integer) animation.getAnimatedValue();
                // 获得改变后的值
                View.setproperty(currentValue);
		        //刷新视图,即重新绘制,从而实现动画效果
                View.requestLayout();
            }
        });
        anim.start();
    }
  • ValueAnimator.oFloat(float values)使用与ValueAnimator.ofInt(int values)类似,不再做阐述!

  • ValueAnimator.ofObject()使用

  • 估值器(TypeEvaluator) 简介

  • ValueAnimator.ofFloat()实现了将初始值以浮点型的形式过渡到结束值的逻辑,FloatEvaluator的代码实现:

public class FloatEvaluator implements TypeEvaluator {  
	// FloatEvaluator实现了TypeEvaluator接口
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
		// fraction:表示动画完成度(根据它来计算当前动画的值)
		// startValue、endValue:动画的初始值和结束值
        float startFloat = ((Number) startValue).floatValue();  
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);  
        // 初始值 过渡 到结束值 的算法是:
        // 1. 用结束值减去初始值,算出它们之间的差值
        // 2. 用上述差值乘以fraction系数
        // 3. 再加上初始值,就得到当前动画的值
    }  
}  
  • 对于ValueAnimator.ofObject(),我们需自定义估值器(TypeEvaluator)来告知系统如何进行从初始对象过渡到结束对象的逻辑:
  • 实现的动画效果:一个圆从一个点 移动到另外一个点 。

步骤1:定义对象类

扫描二维码关注公众号,回复: 3428385 查看本文章

Point.java

public class Point {
    private float x;
    private float y;
    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }
    public float getX() {
        return x;
    }
    public float getY() {
        return y;
    }
}

步骤2:实现TypeEvaluator接口

PointEvaluator.java

// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
    // 在evaluate()里写入对象动画过渡的逻辑
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        // 根据fraction来计算当前动画的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        // 将计算后的坐标封装到一个新的Point对象中并返回
        Point point = new Point(x, y);
        return point;
    }
}

步骤3:将属性动画作用到自定义View当中

MyView.java

public class MyView extends View {

    public static final float RADIUS = 70f;// 圆的半径 = 70
    private Point currentPoint;// 当前点坐标
    private Paint mPaint;// 绘图画笔
    // 构造方法(初始化画笔)
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }
    // 绘制逻辑:先在初始点画圆,通过监听当前坐标值(currentPoint)的变化,每次变化都调用onDraw()重新绘制圆,从而实现圆的平移动画效果
    @Override
    protected void onDraw(Canvas canvas) {
        // 如果当前点坐标为空(即第一次)
        if (currentPoint == null) {
            currentPoint = new Point(RADIUS, RADIUS);
            // 在该点画一个圆:圆心 = (70,70),半径 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);

            Point startPoint = new Point(RADIUS, RADIUS);
            // 初始点为圆心(70,70)
            Point endPoint = new Point(700, 1000);
			// 结束点为(700,1000)
			
            // 步骤2:创建动画对象 & 设置初始值 和 结束值
            ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
            anim.setDuration(5000);
            // 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    currentPoint = (Point) animation.getAnimatedValue();
                    // 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
					// 每次赋值后就重新绘制,从而实现动画效果
                    invalidate();
                    // 调用invalidate()后,就会刷新View,即才能看到重新绘制的界面,即onDraw()会被重新调用一次
                }
            });
            anim.start();
        } else {
            // 如果坐标值不为0,则画圆
            // 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
            // 在该点画一个圆:圆心 = (30,30),半径 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }
}

步骤4:在布局文件加入自定义View空间,到此就能显示出一个圆从一个点 移动到另外一个点。

ObjectAnimator类使用简介

  • 1. 透明度
ObjectAnimator animator = new ObjectAnimator(view,"alpha",1f,0f,1f);
	//延迟播放时间
	animator.setStartDelay(500);
	//设置重复次数
    animator.setRepeatCount(0);
    //设置重复模式
    animator.setRepeatMode(ValueAnimator.RESTART);
    //设置动画时间
    animator.setDuration(1000);
    animator.start();
动画效果:正常 - 全透明 - 不正常。
  • 2. 旋转
ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);
    animator.setDuration(5000);
    animator.start();
动画效果是:0 - 360。
  • 3. 平移
float curTranslationX = mButton.getTranslationX();
    // 获得当前按钮的位置
    ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
    animator.setDuration(5000);
    animator.start();
 动画效果是:从当前位置平移到 x=1500 再平移到初始位置。
  • 4. 缩放
ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "scaleX", 1f, 3f, 1f);
    animator.setDuration(5000);
    animator.start();
动画效果是:放大到3倍,再缩小到初始大小。

如果需要采用ObjectAnimator 类实现动画效果,那么需要操作的对象就必须有该属性的set()和 get()。

  • 实现的动画效果:一个圆的颜色渐变

步骤1:自定义圆MyVIew

MyView.java

public class MyView extends View {
	// 圆的半径 = 100
    public static final float RADIUS = 100f;
    private Paint mPaint;
    // 设置背景颜色属性
    private String color;
    // 设置背景颜色的get()方法
    public String getColor() {
        return color;
    }
	// 设置背景颜色的set()方法
    public void setColor(String color) {
        this.color = color;
        mPaint.setColor(Color.parseColor(color));
		// 调用invalidate(),通过onDraw()重新绘制刷新视图
        invalidate();
    }
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(500, 500, RADIUS, mPaint);
    }
}

步骤2:在布局文件加入自定义View控件

activity_main.xml

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

    <com.qin.animator.MyView
        android:id="@+id/myview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         />
</RelativeLayout>

步骤3:实现TypeEvaluator接口

ColorEvaluator.java

public class ColorEvaluator implements TypeEvaluator {
    // 实现TypeEvaluator接口
    private int mCurrentRed;
    private int mCurrentGreen ;
    private int mCurrentBlue ;
    // 复写evaluate()
    // 在evaluate()里写入对象动画过渡的逻辑:此处是写颜色过渡的逻辑
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // 获取到颜色的初始值和结束值
        String startColor = (String) startValue;
        String endColor = (String) endValue;
        // 通过字符串截取的方式将初始化颜色分为RGB三个部分,并将RGB的值转换成十进制数字
        // 那么每个颜色的取值范围就是0-255
        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);

        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
        // 将初始化颜色的值定义为当前需要操作的颜色值
        mCurrentRed = startRed;
        mCurrentGreen = startGreen;
        mCurrentBlue = startBlue;
        // 计算初始颜色和结束颜色之间的差值
        // 该差值决定着颜色变化的快慢:初始颜色值和结束颜色值很相近,那么颜色变化就会比较缓慢;否则,变化则很快
        // 具体如何根据差值来决定颜色变化快慢的逻辑写在getCurrentColor()
        int redDiff = Math.abs(startRed - endRed);
        int greenDiff = Math.abs(startGreen - endGreen);
        int blueDiff = Math.abs(startBlue - endBlue);
        int colorDiff = redDiff + greenDiff + blueDiff;
        if (mCurrentRed != endRed) {
            mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
                    fraction);
        } else if (mCurrentGreen != endGreen) {
            mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
                    redDiff, fraction);
        } else if (mCurrentBlue != endBlue) {
            mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
                    redDiff + greenDiff, fraction);
        }
        String currentColor = "#" + getHexString(mCurrentRed) + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
        // 最终将RGB颜色拼装起来,并作为最终的结果返回
        return currentColor;
    }
    
    // 具体是根据fraction值来计算当前的颜色。
    private int getCurrentColor(int startColor, int endColor, int colorDiff,
                                int offset, float fraction) {
        int currentColor;
        if (startColor > endColor) {
            currentColor = (int) (startColor - (fraction * colorDiff - offset));
            if (currentColor < endColor) {
                currentColor = endColor;
            }
        } else {
            currentColor = (int) (startColor + (fraction * colorDiff - offset));
            if (currentColor > endColor) {
                currentColor = endColor;
            }
        }
        return currentColor;
    }
    // 将10进制颜色值转换成16进制。
    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }

}

步骤4:调用ObjectAnimator.ofObject()方法

MainActivity.java

public class MainActivity extends AppCompatActivity {

    MyView2 myView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myView = (MyView2) findViewById(R.id.myview );
        ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "color", new ColorEvaluator(),"#0000FF", "#FF0000");
        anim.setDuration(8000);
        anim.start();
    }
}

通过包装原始动画对象,间接给对象加上该属性的 get()和set()

public class MainActivity extends AppCompatActivity {

    Button mButton;
    ViewWrapper wrapper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.Button);
        // 创建动画作用对象
        wrapper = new ViewWrapper(mButton);
        // 创建包装类,并传入动画作用的对象
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(3000).start();
            }
        });
    }
    
    // 提供ViewWrapper类,用于包装View对象
    private static class ViewWrapper {
        private View mTarget;
        // 传入需要包装的对象
        public ViewWrapper(View target) {
            mTarget = target;
        }
        // 为宽度设置get()和 set()
        public int getWidth() {
            return mTarget.getLayoutParams().width;
        }
        
        public void setWidth(int width) {
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }
}

AnimatorSet 类

方法 简介
AnimatorSet.play(Animator anim) 播放当前动画
AnimatorSet.after(long delay) 将现有动画延迟delay毫秒后执行
AnimatorSet.with(Animator anim) 将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) 将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) 将现有动画插入到传入的动画之前执行

在 res/animator的文件目录下创建动画.xml文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially" >
    
    // ordering的属性值:sequentially & together
    // sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )
    // together:表示set中的动画,在同一时间同时进行,为默认值
    
    <set android:ordering="together" >
        <objectAnimator
            android:duration="2000"
            android:propertyName="translationX"
            android:valueFrom="0"
            android:valueTo="300"
            android:valueType="floatType" >
        </objectAnimator>

        <objectAnimator
            android:duration="3000"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" >
        </objectAnimator>
    </set>

    <set android:ordering="sequentially" >
        // 下面的动画按序进行
        <objectAnimator
            android:duration="1500"
            android:propertyName="alpha"
            android:valueFrom="1"
            android:valueTo="0"
            android:valueType="floatType" >
        </objectAnimator>
        
        <objectAnimator
            android:duration="1500"
            android:propertyName="alpha"
            android:valueFrom="0"
            android:valueTo="1"
            android:valueType="floatType" >
        </objectAnimator>
    </set>
</set>

在Java代码中启动动画

AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
// 创建组合动画对象  &  加载XML动画
animator.setTarget(mButton);
// 设置动画作用对象
animator.start();
// 启动动画

动画另外一种用法(ViewPropertyAnimator)

mButton.animate().alpha(0f);
// 单个动画设置:将按钮变成透明状态 
mButton.animate().alpha(0f).setDuration(5000).setInterpolator(new BounceInterpolator());
// 单个动画效果设置,参数设置 
mButton.animate().alpha(0f).x(500).y(500);
// 组合动画:将按钮变成透明状态再移动到(500,500)
// 动画自动启动,无需调用start()方法.

四、FlingAnimation 与SpringAnimation

  • 简介

  • 基于Fling的动画使用与物体速度成比例的摩擦力。使用它来设置对象属性的动画,并希望逐渐结束动画。

  • 触摸力决定了动画的加速和减速。

  • 在每一帧中动画值和速度都会更新。

  • 当受力达到平衡时动画停止。

  • 添加支持库

dependencies {
     implementation 'com.android.support:support-dynamic-animation:27.1.1'
}
  • FlingAnimation 使用
ImageView img = (ImageView)findViewById(R.id.img);
//DynamicAnimation.X表示动画的类型
FlingAnimation fling = new FlingAnimation(img, DynamicAnimation.X);
//设置大于0的初速度,否则动画不会动
fling.setStartVelocity(500f);
//设置摩擦系数,默认0,摩擦系数越大越容易停止
fling.setFriction(0.5f);
//开启动画
fling.start();
  • SpringAnimation 使用
//DynamicAnimation.X表示动画的类型
SpringAnimation springAnimation = new SpringAnimation(img, DynamicAnimation.X);
//设置大于0的初速度,否则动画不会动
springAnimation.setStartVelocity(2000);

SpringForce springForce = new SpringForce();
//设置弹性阻尼,值越大,反弹次数越小
springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
//设置生硬度,恢复成未拉伸状态所需的时间
springForce.setStiffness(SpringForce.STIFFNESS_LOW);
//设置最后静止时的位置
springForce.setFinalPosition(img.getX());
springAnimation.setSpring(springForce);
springAnimation.start();
  • setDampingRatio()方法中参数共有4种可选项:
public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2F;
public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5F;
public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75F;
public static final float DAMPING_RATIO_NO_BOUNCY = 1.0F;
  • setStiffness()方法中共有4种可选项:
public static final float STIFFNESS_HIGH = 10000.0F;
public static final float STIFFNESS_MEDIUM = 1500.0F;
public static final float STIFFNESS_LOW = 200.0F;
public static final float STIFFNESS_VERY_LOW = 50.0F;
  • 创建自定义动画属性

    SpringAnimation和FlingAnimation的构造函数只能接收一个可动画属性参数,如ALPHA、ROTATION、SCALE等,如果要同时为多个属性生成动画,可以创建一个新的属性,改属性封装了我们想改变的其他动画属性值。

FloatPropertyCompat<View> scale = new FloatPropertyCompat<View>("scale") {
    @Override
    public float getValue(View view) {
        // return the value of any one property
        return view.getScaleX();
    }
    @Override
    public void setValue(View view, float value) {
        // Apply the same value to two properties
        view.setScaleX(value);
        view.setScaleY(value);
    }
};

SpringAnimation stretchAnimation = new SpringAnimation(img, scale);
//传递一个有意义的值,以确保动画不会消耗太多的CPU性能
stretchAnimation.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
......
  • 动画监听

    DynamicAnimation提供了两个动画监听器OnAnimationUpdateListener和OnAnimationEndListener,前者监听动画值改变,后者监听动画结束状态。添加动画变化监听需要调用addUpdateListener()方法,重写onAnimationEnd()方法执行具体操作;如果移除动画监听,则需要调用removwUpdateListener()和removeEndListener()。

SpringAnimation springAnimation = new SpringAnimation(img, DynamicAnimation.X);
SpringForce springForce = new SpringForce();

springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
springForce.setStiffness(SpringForce.STIFFNESS_LOW);
springForce.setFinalPosition(img.getX());

springAnimation.setSpring(springForce);
springAnimation.setStartVelocity(2000);
springAnimation.start();

img.setImageResource(R.drawable.pic1);
springAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
    @Override
    public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity) {
        img.setImageResource(R.drawable.pic2);
   }
});

FlingAnimation 与SpringAnimation示例


五、animateLayoutChanges属性使用

步骤1、编写activity_main.xml布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:orientation="vertical">

    </LinearLayout>
</ScrollView>

步骤2、编写layout.xml布局

layout.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="horizontal">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn"
        android:text="删除"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

步骤3、在res/menu中编写menu.xml布局

menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/add"
        android:title="添加"
        app:showAsAction="always" />
</menu>

步骤4、MainActivity布局

MainActivity.java

package com.qin.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private LinearLayout mLl;
    private int count = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLl = findViewById(R.id.container);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case R.id.add:
                addItem();
        }

        return super.onOptionsItemSelected(item);
    }

    private void addItem() {
        final View views = getLayoutInflater().inflate(R.layout.layout, mLl, false);
        Button btn = views.findViewById(R.id.btn);
        TextView tv = views.findViewById(R.id.tv);
        mLl.addView(views);
        tv.setText((count++)+"");
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mLl.getChildCount()!=1){
                    mLl.removeView(views);
                    count--;
                }else{
                    Log.i("tag","没有更多子view!");
                }
            }
        });
    }
}


六、layoutAnimation用法

动画只在ListView或GradView子View第一次进入时有效,如果后面动态给listView添加子View时,此动画效果无效。

  • 只需在ListView或GradView布局文件中使用即可,如:
<ListView
      android:id="@+id/listView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      <!-- 设置在这里 -->
      android:layoutAnimation="@anim/layout_animation"
      />
  • layout_animation.xml自定义动画
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/left_into"
    android:animationOrder="normal"
    android:delay="0.5"
/>
  • 通过代码方式设置:
//通过加载XML动画设置文件来创建一个Animation对象,子View进入的动画
Animation animation=AnimationUtils.loadAnimation(this, R.anim.left_into);
//得到一个LayoutAnimationController对象;
LayoutAnimationController lac=new LayoutAnimationController(animation);
//设置控件显示的顺序;
lac.setOrder(LayoutAnimationController.ORDER_REVERSE);
//设置控件显示间隔时间;
lac.setDelay(0.5f);
//为ListView设置LayoutAnimationController属性;
listView.setLayoutAnimation(lac);

七、LayoutTransition 使用

  • LayoutTransition类用于当前布局容器中需要View添加,删除,隐藏,显示时设置布局容器子View的过渡动画。可以通过setLayoutTransition()方法为布局容器ViewGroup设置LayoutTransition对象,代码如下:
//初始化容器动画
LayoutTransition mTransitioner = new LayoutTransition();
container.setLayoutTransition(mTransitioner);
  • LayoutTransition 常用函数:
函数名 简介
setAnimator(int transitionType, Animator animator) 设置不同状态下的动画过渡,transitionType取值为, APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 、CHANGING
setDuration(long duration) 设置所有动画完成所需要的时长
setDuration(int transitionType, long duration) 设置特定type类型动画时长,transitionType取值为, APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 、CHANGING
setStagger(int transitionType, long duration) 设置特定type类型动画的每个子item动画的时间间隔 ,transitionType取值为: APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING、CHANGING
setInterpolator(int transitionType, TimeInterpolator interpolator) 设置特定type类型动画的插值器, transitionType取值为: APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING、CHANGING
setStartDelay(int transitionType, long delay) 设置特定type类型动画的动画延时 transitionType取值为, APPEARING、DISAPPEARING、CHANGE_APPEARING、CHANGE_DISAPPEARING 、CHANGING
addTransitionListener(TransitionListener listener) 设置监听器TransitionListener
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private int i = 0;
    private LinearLayout container;
    private LayoutTransition mTransitioner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        container = (LinearLayout) findViewById(R.id.container);
        //构建LayoutTransition
        mTransitioner = new LayoutTransition();
        //设置给ViewGroup容器
        container.setLayoutTransition(mTransitioner);
        setTransition();
    }
    
    private void setTransition() {
        //添加View时过渡动画效果
        ObjectAnimator addAnimator = ObjectAnimator.ofFloat(null, "rotationY", 0, 90,0).setDuration(mTransitioner.getDuration(LayoutTransition.APPEARING));
        mTransitioner.setAnimator(LayoutTransition.APPEARING, addAnimator);
        // 移除View时过渡动画效果
        ObjectAnimator removeAnimator = ObjectAnimator.ofFloat(null, "rotationX", 0, -90, 0).
                setDuration(mTransitioner.getDuration(LayoutTransition.DISAPPEARING));
        mTransitioner.setAnimator(LayoutTransition.DISAPPEARING, removeAnimator);
       //view 动画改变时,布局中的每个子view动画的时间间隔
        mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
        mTransitioner.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);

        /**
         *LayoutTransition.CHANGE_APPEARING和LayoutTransition.CHANGE_DISAPPEARING的过渡动画效果
         * 必须使用PropertyValuesHolder所构造的动画才会有效果,不然无效!使用ObjectAnimator是行不通的,
         * 时,”left”、”top”、”bottom”、”right”属性的变动是必须设置的,至少设置两个,不然动画无效,问题是我们即使这些属性不想变动!!!也得设置!!!
         * 这时只要传递的可变参数都一样就行如下面的(0,0)也可以是(100,100)即可。
         */
        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 0);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 0);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 0);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 0);
        
         //view被添加时,其他子View的过渡动画效果
        PropertyValuesHolder animator = PropertyValuesHolder.ofFloat("scaleX", 1, 1.5f, 1);
        final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,  pvhBottom, animator).setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_APPEARING));
        //设置过渡动画
        mTransitioner.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
        //view移除时,其他子View的过渡动画
        PropertyValuesHolder pvhRotation =
                PropertyValuesHolder.ofFloat("scaleX", 1, 1.5f, 1);
        final ObjectAnimator changeOut = ObjectAnimator.ofPropertyValuesHolder(
                this, pvhLeft, pvhBottom, pvhRotation).
                setDuration(mTransitioner.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        mTransitioner.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
    }
    
    public void addView(View view) {
        i++;
        Button button = new Button(this);
        button.setText(""+ i);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        container.addView(button, Math.min(1, container.getChildCount()), params);
    }

    public void removeView(View view) {
        if (i > 0)
            container.removeViewAt(0);
    }
}
  • 还有点需要注意的是在使用的ofInt,ofFloat中的可变参数值时,第一个值和最后一个值必须相同,不然此属性将不会有动画效果,比如下面首位相同是有效的
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",100,0,100);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top",0,100,0);

后续将会对Android Transition(Android过渡动画)做一个使用总结

参考:

https://developer.android.google.cn/training/animation/overview 官方解析
https://blog.csdn.net/carson_ho/article/details/72909894 属性动画
https://blog.csdn.net/javazejian/article/details/52334098 动画详解

猜你喜欢

转载自blog.csdn.net/qq_33706840/article/details/81475879