Drawable Animation
帧动画,多张图片循环播放,实现动画效果。
以Xml定义一组帧动画
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item
android:drawable="@drawable/a1"
android:duration="200" />
<item
android:drawable="@drawable/a2"
android:duration="200" />
<item
android:drawable="@drawable/a3"
android:duration="200" />
<item
android:drawable="@drawable/a4"
android:duration="200" />
<item
android:drawable="@drawable/a5"
android:duration="200" />
</animation-list>
oneshot表示动画是否只执行一次。
每一帧的动画可以设置图层,让同一帧能有几个图。
<item android:duration="100">
<layer-list>
<item android:drawable="@drawable/login_loading_1" />
<item android:drawable="@drawable/login_loading_2" />
</layer-list>
</item>
帧动画图片多,可能会有内存溢出的问题、
View Animation
视图动画,又叫补间动画,有旋转,透明度,大小,位移四种动画效果,特点是实现简单,但是动画并不改变实际view的属性
用代码实现动画
TranslateAnimation 平移动画
TranslateAnimation ta = new TranslateAnimation(0,400,0,0);//左平移400个单位
ta.setDuration(2000);
view.startAnimation(ta);
AlphaAnimation 透明度动画
AlphaAnimation aa = new AlphaAnimation(1.0f,0.5f);//从不透明到半透明
aa.setDuration(2000);
view.startAnimation(aa)
RotateAnimation 旋转动画
RotateAnimation ra = new RotateAnimation(0,180);
ra.setDuration(2000);
view.startAnimation(ra);
ScaleAnimation 缩放动画
ScaleAnimation sa = new ScaleAnimation(0,5,0,5);
sa.setDuration(2000);
view.startAnimation(ra);
其中除AlphaAnimation之外其余三种动画的参考坐标系有三种。
- 默认是view的左上角。
- 以及view自身:Animation.RELATIVE_TO_SELF
- 还有view的父控件:Animation.RELATIVE_TO_PARENT
这里说的参考系,可以理解成一个关键点的确定
这是一个300*300px的ViewGroup,背景图是个圆,小红点位于ViewGroup的左上角
以TranslateAnimation来看这三种参考坐标系
TranslateAnimation ta1 = new TranslateAnimation(0,150,0,0);
//TranslateAnimation ta2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,10f,Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0);
//TranslateAnimation ta3 = new TranslateAnimation(Animation.RELATIVE_TO_PARENT,0,Animation.RELATIVE_TO_PARENT,1f,Animation.RELATIVE_TO_PARENT,0,Animation.RELATIVE_TO_PARENT,0);
ta1.setDuration(2000);
image.startAnimation(ta1);
三种设置分别对应三个gif图,第一个是以左上角为参考点,右平移150个单位(px)
第二个是以自身为参考,右平移自身10倍的距离。
第三个是以父控件为参考,右平移父控件一倍宽的距离。
ScaleAnimation有个比较有意思的地方。如果参考点不在原先的view上,缩放之后的view会带移动的效果。
ScaleAnimation sa1 = new ScaleAnimation(1,5,1,5,-10,0);
ScaleAnimation sa = new ScaleAnimation(1,5,1,5,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
比如说sa1这个ScaleAnimation,参考点在他的左侧-10px,0px的地方
他放大五倍的效果是这样的
由于参考点在圆的左边,所以圆是向右边移动的。实际上这个放大效果是把圆和他的参考点看成了一个整体,参考点固定不动,放大五倍的过程中,参考点和圆的距离10px也放大了五倍。
让几个动画一起作用可以使用AnimationSet
AnimationSet as = new AnimationSet(true);//boolean 是否共享插值器
ScaleAnimation sa1 = new ScaleAnimation(1,5,1,5,-10,0);
ScaleAnimation sa = new ScaleAnimation(1,5,1,5,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
RotateAnimation ra = new RotateAnimation(0, 360f, Animation.RELATIVE_TO_PARENT, 0.5f, Animation.RELATIVE_TO_PARENT, 0.5f);
as.setInterpolator(new AccelerateDecelerateInterpolator());
as.addAnimation(sa1);
as.addAnimation(ra);
as.setDuration(2000);
image.startAnimation(as);
相比在代码里设置动画,一般还是用Xml来配置
在res目录下新建文件夹anim用来存放动画文件
如下代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="1000"
android:fillAfter="true"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="5"
android:toYScale="5" />
<set android:startOffset="1000">
<translate
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="200"
android:toYDelta="0"
/>
<alpha
android:duration="2000"
android:fromAlpha="1"
android:toAlpha="0"
/>
</set>
</set>
定义了一个一秒内放大五倍,接着一边平移一边消失的动画
值得注意的是pivotX和pivotY参考点的确定,以view的左上角为0,0点,用绝对数值确定,比如pivotX="-10",pivotY="0",表示左上角往左10px的点,pivotX="50%"表示相对于自身的百分之50,pivotX="50%p"表示相对于父控件的百分之50.对应代码设置的三种坐标确定
加载一个xml的Animation
Animation animation = AnimationUtils.loadAnimation(MainActivity.this,R.anim.animation);
img.startAnimation(animation);
Property Animation
属性动画,通过改变View的属性,来达到动画的效果。
因为改变的是属性值,所以动画会改变View的位置,大小等。
一般要求改变的属性值有 get/set 方法
属性动画有两个动画执行类分别是ObjectAnimator和ValueAnimator
ObjectAnimator是ValueAnimator的子类,具有比ValueAnimator更强的封装性,使用代码更简洁,相反的,封装性不强的ValueAnimator则显得更加灵活,容易实现复杂的效果
ObjectAnimator
修改单个属性的动画效果
ObjectAnimator.ofFloat(img,"rotation",0.0F,180F).setDuration(500).start();
直接调用ObjectAnimator的ofFloat,ofInt,ofObject方法,第一个参数是作用的view,第二个是操作的属性,第三个是一个不定参,只填一个表示结束的属性值,填两个分别表示开头和结尾的属性值。
多个属性值改变的动画效果,(同时改变)可以用PropertyValuesHolder
PropertyValuesHolder pvha = PropertyValuesHolder.ofFloat("alpha", 1f, 0, 1f);
PropertyValuesHolder pvhsx = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvhsy = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvha, pvhsx, pvhsy).setDuration(2000).start();
ValueAnimator
实际上,ValueAnimator可以看成是提供一个区间的计算过程。
而实际的动画效果,需要我们在回调中自己去设置。
例如:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(200.0F);
valueAnimator.setTarget(img);
valueAnimator.setDuration(2000).start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
img.setTranslationY((Float) valueAnimator.getAnimatedValue());
}
});
因为ofFloat只传了一个参,所以200是结束值,区间是0-200;
2000ms时间内在该区间内不断的递增,AnimatorUpdateListener里通过valueAnimator.getAnimatedValue()就能拿到某时刻的值,再根据需要进行设置,这里是平移200个px
TypeEvaluator
上面说道ValueAnimator可看成是提供一个区间的计算过程。实际上,这个计算的操作,是由TypeEvaluator来进行的。
TypeEvaluator是一个接口,我们可以实现这个接口来实现自己的需求,也可以用Android提供的几个Evaluator
比如:
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointFEvaluator(),new PointF(0,0),new PointF(100,100));
valueAnimator.setDuration(2000).start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF pf = (PointF) valueAnimator.getAnimatedValue();
Log.e("walker","x值 : " + pf.x);
Log.e("walker","y值 : " + pf.y);
}
});
PointFEvaluator 是 自带的一个Evaluator,上面代码是(0,0)点到
(100,100)的计算过程,过程中不断回调AnimatorUpdateListener
因为我们没有为任何view设置动画,所以不会有什么动画效果,但是Log里可以看到2s间点的移动状态。毫无疑问,这是一条直线的运动轨迹
自定义TypeEvaluator模拟抛物线效果
ValueAnimator valueAnimator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float v, PointF o, PointF t1) {
PointF pf = new PointF();
pf.x = 200 * v * 2;
pf.y = 0.5f * 200 * (v * 2) * (v * 2);
return pf;
}
},new PointF(0,0));
valueAnimator.setDuration(2000).start();
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF point = (PointF) valueAnimator.getAnimatedValue();
ball.setX(point.x);
ball.setY(point.y);
}
})
关于动画状态的监听
一个有四个状态,分别为开始,结束,取消,重复
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
})
也可以用AnimatorListenerAdapter选择自己需要的监听
比方说只需要动画结束的监听
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
}
})
AnimatorSet设置多个动画的执行
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(img,"rotation",0.0F,360F);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(400.0F);
valueAnimator.setTarget(img);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
img.setTranslationY((Float) valueAnimator.getAnimatedValue());
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
animatorSet.playTogether(objectAnimator,valueAnimator);//一起执行
// animatorSet.playSequentially(objectAnimator,valueAnimator);//顺序执行
// animatorSet.play(objectAnimator).with(valueAnimator);//链式排列
animatorSet.start();