Android进阶–动画
Android进阶路线图
一、Android基础动画
1、平移动画Translation
a、 xml方式实现(在res目录下建立anim目录)
animator_translate.xml
<?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:fromYDelta="0" android:toYDelta="0" android:toXDelta="200" android:duration="500" android:fillAfter="true"> </translate>
代码加载xml文件获取动画
//加载动画 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_translate); //执行动画 testBtn.startAnimation(animation);
b、 代码方式实现
TranslateAnimation translateAnimation = new TranslateAnimation(0,200,0,0); translateAnimation.setDuration(500);//动画执行时间 translateAnimation.setFillAfter(true);//动画执行完成后保持状态 //执行动画 testBtn.startAnimation(translateAnimation);
2、旋转动画Rotation
a、 xml方式实现
animator_rotation.xml
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="90" android:pivotX="50%" android:pivotY="50%" android:duration="500" android:fillAfter="true"> </rotate>
代码加载xml文件获取动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_rotation); testBtn.startAnimation(animation);
b、 代码方式实现
RotateAnimation rotateAnimation = new RotateAnimation(0,90,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); testBtn.startAnimation(rotateAnimation);
3、缩放动画Scale
a、 xml方式实现
animator_scal.xml
<?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:fromXScale="1" android:fromYScale="1" android:toYScale="2" android:toXScale="2" android:duration="500" android:pivotX="50%" android:pivotY="50%" android:fillAfter="true"> </scale>
代码加载xml文件获取动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_scal); testBtn.startAnimation(animation);
b、 代码方式实现
ScaleAnimation scaleAnimation = new ScaleAnimation(1,2,1,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); scaleAnimation.setDuration(500); scaleAnimation.setFillAfter(true); testBtn.startAnimation(scaleAnimation);
4、透明度动画Alpha
a、 xml方式实现
animator_alpha.xml
<?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:fromAlpha="1" android:toAlpha="0.2" android:duration="500" android:fillAfter="true"> </alpha>
代码加载xml文件获取动画
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animator_alpha); testBtn.startAnimation(animation);
b、 代码方式实现
AlphaAnimation alphaAnimation = new AlphaAnimation(1,0.2f); alphaAnimation.setDuration(500); alphaAnimation.setFillAfter(true); testBtn.startAnimation(alphaAnimation);
5、帧动画
a、 xml方式实现(在drawable文件夹下定义xml)
anim_list.xml
布局
<ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/anim_list"/>
获取背景
//获取背景,并将其强转成AnimationDrawable AnimationDrawable animationDrawable = (AnimationDrawable) iv.getBackground(); //判断是否在运行 if(!animationDrawable.isRunning()){ //开启帧动画 animationDrawable.start(); }
b、 代码方式实现
//创建一个AnimationDrawable AnimationDrawable animationDrawable1 = new AnimationDrawable(); int[] ids = {R.drawable.anim_1,R.drawable.anim_2,R.drawable.anim_3,R.drawable.anim_4}; //通过for循环添加每一帧动画 for(int i = 0 ; i < 4 ; i ++){ Drawable frame = getResources().getDrawable(ids[i]); animationDrawable1.addFrame(frame,200); } animationDrawable1.setOneShot(false); //设置背景 iv.setBackground(animationDrawable1); //开启帧动画 animationDrawable1.start();
二、属性动画
1、ObjectAnimator
a、平移动画
//testBtn.animate().translationX(200);
ObjectAnimator animator = ObjectAnimator.ofFloat(testBtn, "translationX", 200);//setTranslationX
animator.setDuration(500);
animator.start();
b、旋转动画
ObjectAnimator animator = ObjectAnimator.ofFloat(testBtn,"rotation",90);
animator.setDuration(500);
animator.start();
c、缩放动画
ObjectAnimator animator = ObjectAnimator.ofFloat(testBtn,"scaleX",1.5f);
animator.setDuration(500);
animator.start();
d、透明度动画
ObjectAnimator animator = ObjectAnimator.ofFloat(testBtn,"alpha",0.2f);
animator.setDuration(500);
animator.start();
5、PropertyValuesHolder
上面的示例中一个ObjectAnimator只能改变一个属性,如果想通过一个ObjectAnimator改变多个属性,则需要使用PropertyValuesHolder
//一个ObjectAnimator通过PropertyValuesHolder可以同时改变多个属性
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX",200);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY",200) ;
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(testBtn, holder1, holder2);
animator.setDuration(500);
animator.start();
e、关键帧Keyframe
//关键帧
//首先定义关键帧
Keyframe keyframe1 = Keyframe.ofFloat(0,0);
Keyframe keyframe2 = Keyframe.ofFloat(0.5f,200);
Keyframe keyframe3 = Keyframe.ofFloat(1,150);
//将关键帧作用于属性上
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",keyframe1,keyframe2,keyframe3);
PropertyValuesHolder holder1 = PropertyValuesHolder.ofKeyframe("translationY",keyframe1,keyframe2,keyframe3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(testBtn,holder,holder1);
animator.setDuration(500);
animator.start();
f、补间器(差值器)
- 系统自带补间器(差值器)
Interpolator | 资源ID | 功能 |
---|---|---|
AccelerateDecelerateInterpolator | @android:anim/accelerate_decelerate_interpolator | 先加速再减速 |
AccelerateInterpolator | @android:anim/accelerate_interpolator | 加速 |
AnticipateInterpolator | @android:anim/anticipate_interpolator | 先后退一小步然后加速前进 |
AnticipateOvershootInterpolator | @android:anim/anticipate_overshoot_interpolator | 先后退一小步再加速前进,超出终点一小步再回到终点 |
BounceInterpolator | @android:anim/bounce_interpolator | 最后阶段弹球效果 |
CycleInterpolator | @android:anim/cycle_interpolator | 周期运行 |
DecelerateInterpolator | @android:anim/decelerate_interpolator | 减速 |
LinearInterpolator | @android:anim/linear_interpolator | 匀速 |
OvershootInterpolator | @android:anim/overshoot_interpolator | 快速到达终点并超出一小步然后回到终点 |
//补间器 差值器
ObjectAnimator animator = ObjectAnimator.ofFloat(testBtn, “translationX”, 100,400);//setTranslationX
animator.setDuration(500);
//animator.setInterpolator(new AccelerateDecelerateInterpolator());//先加速后减速
animator.setInterpolator(new AnticipateOvershootInterpolator());//先后退一小步再加速前进,超出终点一小步再回到终点
animator.start();
自定义补间器(差值器)
//默认是LinearInterpolator animator.setInterpolator(new TimeInterpolator() { @Override public float getInterpolation(float input) { //input 百分比(范围 0 - 1 ) 0.1 0.3 0. 5 只与时间有关 //上面我们定义动画执行时间是500 如果过了200 input=0.4 // 与自己设定的值无关 只与时间有关 Log.i("ObjeceAnimator","interpolation result :"+(1 - input)); return 1 - input;// 1 ---- 0 我们可以根据input自定义返回值 } });
g、Evaluator计算规则
Evaluator是计算规则,系统提供了一些默认的计算规则(FloatEvaluator IntEvaluator)
自定义计算规则Evaluator
//如果上面使用的是ofFloat 默认使用FloatEvaluator 如果是ofInt 默认使用IntEvaluator animator.setEvaluator(new TypeEvaluator<Float>() { @Override public Float evaluate(float fraction, Float startValue, Float endValue) { //在这个方法里,我们可以任意的自定义计算规则 float result = startValue + (endValue - startValue) * fraction ; Log.i("ObjeceAnimator","evaluator fraction:"+fraction+",result:"+result); return result ; } });
h、监听
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
float value = (float) valueAnimator.getAnimatedValue();
Log.i("ObjeceAnimator","update fraction:"+fraction+",value:"+value);
}
});
- Interpolation 中的getInterpolation方法的返回值 负责计算百分比 只和时间有关
- Evaluator 中的evaluate方法根据百分比和开始结束值计算value
- UpdateListener中的fraction和value都是上面计算的结果
2、ValueAnimator
a、ValueAnimator理解
- 1、ValueAnimator只提供变化量,并不真正的作用于view上
- 2、ValueAnimator根据补间器Interpolator(差值器)时间产生百分比
- 3、ValueAnimator根据Evaluator的计算规则和第2步中的百分比,计算value值
- 4、ValueAnimator中的UpdateListener监听获取百分比和value
ValueAnimator是动画基础,可以在此基础上完成想要的任何动画
ValueAnimator animator = ValueAnimator.ofFloat(100,200); animator.setDuration(500); animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float fraction = animation.getAnimatedFraction(); float value = (float) animation.getAnimatedValue(); //在这里设置动画属性 testBtn.setTranslationX(value); Log.i("ObjectAnimator","fraction:"+fraction+",value:"+value); } });
b、ValueAnimator.ofObject使用
- Evaluator作用:提供计算规则
- 强调:无论什么类型都需要计算规则Evaluator
- ofFloat:系统默认提供FloatEvaluator
- ofInt:系统默认提供IntEvaluator
ofObject:系统不知道使用的具体类型,同时又需要计算规则,这时就需要我们自己提供计算规则
ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('a'),new Character('z')); animator.setDuration(10000); animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { char value = (char) animation.getAnimatedValue(); testBtn.setText("测试"+value); } });
ofObject针对自定义类型
自定义类型
public class Point { private int pointX = 0 ; public Point(int pointX) { this.pointX = pointX; } public int getPointX() { return pointX; } public void setPointX(int pointX) { this.pointX = pointX; } }
自定义view代码如下
public class PointView extends View { private Paint paint ; public PointView(Context context) { super(context); init(); } public PointView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public PointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public PointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init(){ paint = new Paint() ; paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); } private int cx = 0 ; private int cy = 100 ; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制圆 canvas.drawCircle(cx+20,cy,20,paint); } //开启动画方法 public void startAnimator(){ ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(0),new Point(300)); animator.setDuration(1000); animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Point point = (Point) animation.getAnimatedValue(); cx = point.getPointX(); //重绘 invalidate(); } }); } //自定义Point类型的计算规则 class PointEvaluator implements TypeEvaluator<Point>{ @Override public Point evaluate(float fraction, Point startValue, Point endValue) { int startX = startValue.getPointX(); int endX = endValue.getPointX(); int reslut = (int) (startX + (endX - startX) * fraction); Point point = new Point(reslut); return point; } } }
开启动画
pointView.startAnimator();
3、AnimatorSet
AnimatorSet animatorSet = new AnimatorSet() ;
ObjectAnimator animator1 = ObjectAnimator.ofFloat(testBtn,"translationX",200);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(testBtn,"translationY",200);
animatorSet.play(animator1);
animatorSet.playSequentially(animator1,animator2);//依次执行动画
animatorSet.playTogether(animator1,animator2);//同时执行动画
animatorSet.setDuration(500);
animatorSet.start();
三、矢量动画
1、SVG
svg导出VectorDrawable:http://inloop.github.io/svg2android/
svg在线编辑器http://www.zhangxinxu.com/sp/svg/
Path指令
- M = moveto(M X,Y) :将画笔移动到指定的坐标位置
- L = lineto(L X,Y) :画直线到指定的坐标位置
- H = horizontal lineto(H X):画水平线到指定的X坐标位置
- V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
- C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝赛曲线
- S = smooth curveto(S X2,Y2,ENDX,ENDY)
- Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线
- T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射
- A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
- Z = closepath():关闭路径
大写绝对定位,参照全局坐标系;小写相对定位
通过一个实例演示SVG动画实现
定义VectorDrawable
search_bar.xml
<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="150dp" android:height="24dp" android:viewportWidth="150" android:viewportHeight="24"> <!-- 搜索按钮--> <path android:name="search" android:pathData="M141,17 A9,9 0 1,1 142,16 L149,23" android:strokeWidth="2" android:strokeColor="#ffffff" android:strokeAlpha="0.8" android:strokeLineCap="round" /> <!-- 底部线条 --> <path android:name="bar" android:pathData="M0,23 L149,23" android:strokeWidth="2" android:strokeColor="#ffffff" android:strokeAlpha="0.8" android:strokeLineCap="square" /> </vector>
上述search_bar.xml定义在drawable文件夹下,可以在Android Studio中直接预览结果
定义animated-vector
animated-vector相当于一个桥梁,将动画与VectorDrawable连接在一起
search_to_bar.xml
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/search_bar"> <target android:animation="@animator/anim_search_none" android:name="search" /> <target android:animation="@animator/anim_bar_fill" android:name="bar" /> </animated-vector>
bar_to_search.xml
<?xml version="1.0" encoding="utf-8"?> <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/search_bar" > <target android:animation="@animator/anim_search_fill" android:name="search"/> <target android:animation="@animator/anim_bar_none" android:name="bar"/> </animated-vector>
定义动画
anim_bar_fill.xml(显示bar)
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="trimPathEnd" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" android:duration="500" />
anim_bar_none.xml(将bar隐藏)
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="trimPathEnd" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:duration="500" />
anim_search_fill.xml(显示搜索按钮)
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="500" android:propertyName="trimPathEnd" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
anim_search_none.xml(隐藏搜索按钮)
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="trimPathEnd" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" android:duration="500" />
使用
public class SvgActivity extends AppCompatActivity { private ImageView search ; private Boolean flag = false ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_svg); search = (ImageView) findViewById(R.id.search); final AnimatedVectorDrawable barToSearch = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.bar_to_search); final AnimatedVectorDrawable searchToBar = (AnimatedVectorDrawable) getResources().getDrawable(R.drawable.search_to_bar); search.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(flag) { search.setImageDrawable(barToSearch); barToSearch.start(); flag = false ; }else { search.setImageDrawable(searchToBar); searchToBar.start(); flag = true ; } } }); } }