今天重新学习了动画方面的知识,进行总结一下
补间动画
下面是代码,补间动画就是简单的一些操作,直接上代码
public void start(View view) {
ImageView imageView = (ImageView) findViewById(R.id.imageView);
//第一种,补间动画
//平移动画
TranslateAnimation animation = new TranslateAnimation(0, 200F, 0, 0);
animation.setDuration(1000);
animation.setFillAfter(true); //指示动画指定完毕之后控件的状态是否停留在动画停止的时候
animation.setRepeatMode(Animation.RESTART); //设置重复模式,有很多值
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
//当动画重复执行
}
});
imageView.startAnimation(animation);
//旋转
RotateAnimation rotateAnimation = new RotateAnimation(0F, 180F);
rotateAnimation.setDuration(1000);
imageView.startAnimation(rotateAnimation);
//透明
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0F, 0.5F);
alphaAnimation.setDuration(1000);
imageView.startAnimation(alphaAnimation);
//缩放
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 2.0F, 0, 2.0F);
scaleAnimation.setDuration(1000);
scaleAnimation.setFillAfter(true);
imageView.startAnimation(scaleAnimation);
}
动画合集
单一的动画肯定满足不了我们实际开发,实际开发中,我们需要使用组合的方式,形成动画合集
AnimationSet set = new AnimationSet(true);
set.addAnimation(animation);
set.addAnimation(rotateAnimation);
set.addAnimation(alphaAnimation);
set.addAnimation(scaleAnimation);
set.setDuration(1000);
set.setFillAfter(true);
imageView.startAnimation(set);
xml形式的动画
首先在res文件夹下创建anim文件夹(必须命名为anim,不然会报错),创建动画的xml文件
这是单个的文件
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.1"
android:duration="2000"/>
这是set合集的写法
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:fillAfter="true" //是否停留
android:shareInterpolator="true" > //每个子动画是否共享同一个插值器
<scale
android:duration="2000"
android:fromXScale="0.2" //水平方向上的缩放起始值
android:fromYScale="0.2" //竖直方向上的缩放的起始值
android:pivotX="50%"
//pivotX/pivotY:缩放的中轴点X/Y坐标,即距离自身左边缘的位置,比如50%就是以图像的 中心为中轴点
android:pivotY="50%"
android:toXScale="1.5" // 竖直方向上缩放的结束值
android:toYScale="1.5" /> //水平方向上缩放的结束值
<rotate
android:duration="1000"
android:fromDegrees="0" //旋转开始的角度
android:repeatCount="1" //repeatCount:旋转的次数,默认值为0,代表一次,假如是其他值,比如3,则旋转4次 另外,值为-1或者infinite时,表示动画永不停止
android:repeatMode="reverse" //repeatMode:设置重复模式,默认restart,但只有当repeatCount大于0或者infinite或-1时 才有效。还可以设置成reverse,表示偶数次显示动画时会做方向相反的运动
android:toDegrees="360" /> //旋转结束的角度
<translate
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="320"
android:toYDelta="0" />
<alpha
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0.1" />
</set>
调用的方法如下
Animation loadAnimation = AnimationUtils.loadAnimation(this, R.anim.ad_detail_enter);
imageView.startAnimation(loadAnimation);
使用xml动画的好处是可以重复使用,不用每次都去写
属性动画
其达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变
既然已将有了补间动画,为什么谷歌还要开发属性动画呢?
因为补间动画本质上只是调用了ondrow()方法,一点点的去绘制view,造成一种view移动的假象,无法处理交互性需求,属性动画是将任意对象的属性,通过set和get方法,去改变,实现的效果,只要对象拥有set属性,就可以使用属性动画改变,属性动画一般使用三个对象:ObjectAnimator,ValueAnimator,ViewPropertyAnimator
ObjectAnimator的用法
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mImageView,"alpha",1.0F,0.5F);
objectAnimator.setDuration(1000);
objectAnimator.start();
ObjectAnimator是ValueAnimator的子类,所以ValueAnimator有的方法ObjectAnimator都会有,上面这句代码
ObjectAnimator.ofFloat(mImageView,"alpha",1.0F,0.5F);
中的alpha,View类中并没有这个属性,ObjectAnimator是如何进行操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是
public void setAlpha(float value);
public float getAlpha();
只要对象拥有set属性,就可以使用属性动画改变
这里常用的值有:alpha、rotation、translationX和scaleY,甚至backgroundColor都可以改变
ObjectAnimator objectAnimator =ObjectAnimator.ofInt(mImageView,“backgroundColor”,0xFFFF0808,0xFF0808FF);
下面有一个小例子,实现一个弹出菜单:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private int[] res = {R.id.imageView1, R.id.imageView2, R.id.imageView3,R.id.imageView4, R.id.imageView5, R.id.imageView6};
private ArrayList<ImageView> images = new ArrayList();
private boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < res.length; i++) {
ImageView imageView = (ImageView) findViewById(res[i]);
imageView.setOnClickListener(this);
images.add(imageView);
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.imageView1:
if (flag) {
startAnim();
} else {
closeAnim();
}
break;
default:
break;
}
}
private void closeAnim() {
for (int i = 1; i < res.length; i++) {
ObjectAnimator animator = ObjectAnimator.ofFloat(images.get(i),"translationY", i * 150, 0);
animator.setStartDelay(i * 300); //设置延时启动动画
animator.setDuration(500);
animator.setInterpolator(new BounceInterpolator());
animator.start();
flag = true;
}
}
//开始动画
private void startAnim() {
for (int i = 1; i < res.length; i++) {
ObjectAnimator animator = ObjectAnimator.ofFloat(images.get(i),"translationY", 0, i * 150);
animator.setStartDelay(i * 300);
animator.setInterpolator(new BounceInterpolator());
animator.setDuration(500);
animator.start();
flag = false;
}
}
}
借助这个例子,讲一下插值器的概念Interpolator,用于控制值变化的规律
如上面的例子所示,从0-150,持续的时间是500毫秒,每一个时间因子会得到一个值,根据这个值去改变对象的属性,形成动画效果,有时候,我们为了实现不同的动画效果,需要让每个时间因子获取的值不一样,这样就引入了插值器的概念,默认情况下是线性插值器(平均值),谷歌为我们提供了9种插值器,基本够我们平时开发使用
- AccelerateInterpolator //加速
- AccelerateDecelerateInterpolator //先加速后减速
- AnticipateInterpolator //开始的时候向后然后向前甩
- LinearInterpolator //线性
- DecelerateInterpolator //减速
- BounceInterpolator //动画结束的时候弹起(自由落体)
- OvershootInterpolator //向前甩一定值后再回到原来位置
- CycleInterpolator //动画循环播放特定的次数,速率改变沿着正弦曲线
ValueAnimator用法
ValueAnimator主要是进行值得运算,将获取的值通过回调监听,重新赋值,以达到改变属性的目的
下面是一段代码示例:
final Button button = (Button) findViewById(R.id.button);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = (int) valueAnimator.getAnimatedValue(); //这里强转int是因为上面是ofInt();
button.setText("" + value); //实现了button快速显示数字
}
});
valueAnimator.setDuration(3000);
valueAnimator.start();
还有一个改变对象的代码片段:
ValueAnimator valueAnimator1 = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
/**
*
* @param v 每一个时间因子获取到的值
* @param o 开始的值
* @param t1 结束的值
* @return 一个新的对象
*/
@Override
public PointF evaluate(float v, PointF o, PointF t1) {
return null;
}
});
这个类是很强大的,一般处理一些高级的用法
ViewPropertyAnimator用法
在绝大多数情况下,我相信大家主要都还是对View进行动画操作的。Android开发团队也是意识到了这一点,没有为View的动画操作提供一种更加便捷的用法确实是有点太不人性化了,于是在Android 3.1系统当中补充了ViewPropertyAnimator这个机制
mImageView.animate().x(500).y(500).setDuration(5000).setInterpolator(new BounceInterpolator());
mImageView.animate() 这个会获取到一个ViewPropertyAnimator的实例,也就是说拿到这个对象之后我们就可以调用它的各种方法来实现动画效果了
- 整个ViewPropertyAnimator的功能都是建立在View类新增的animate()方法之上的,这个方法会创建并返回一个ViewPropertyAnimator的实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的。
- 大家注意到,在使用ViewPropertyAnimator时,我们自始至终没有调用过start()方法,这是因为新的接口中使用了隐式启动动画的功能,只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。当然如果不想使用这一默认机制的话,我们也可以显式地调用start()方法来启动动画。
- ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完一个方法之后可以直接连缀调用它的另一个方法,这样把所有的功能都串接起来,我们甚至可以仅通过一行代码就完成任意复杂度的动画功能。
属性动画的合集写法
PropertyValuesHolder 写法
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
0, 1f);
ObjectAnimator.ofPropertyValuesHolder(mImageView, pvhX, pvhY,pvhZ).setDuration(1000).start();
AnimatorSet写法
这种写法能够比较方便的控制动画的执行顺序
ObjectAnimator moveIn = ObjectAnimator.ofFloat(mImageView, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(mImageView, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(mImageView, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
属性动画的xml写法
使用XML来编写动画,画的时间可能比Java代码长一点,但是重用起来就轻松很多!
对应的XML标签分别为:
- animator
- objectAnimator
- set
相关的属性解释如下:
- android:ordering:指定动画的播放顺序:sequentially(顺序执行),together(同时执行)
- android:duration:动画的持续时间
- android:propertyName=”x”:这里的x,还记得上面的”alpha”吗?加载动画的那个对象里需要 定义getx和setx的方法,objectAnimator就是通过这里来修改对象里的值的!
- android:valueFrom=”1” :动画起始的初始值
- android:valueTo=”0” :动画结束的最终值
- android:valueType=”floatType”:变化值的数据类型
单个的xml文件写法
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"/>
set的形式写法
<set android:ordering="sequentially" > //两个值 一个是together:动画集合的子动画同时播放;一个是sequentially:子动画按照先后顺序依次播放
<set>
<objectAnimator
android:duration="500"
android:propertyName="x"
android:valueTo="400"
android:valueType="intType" />
<objectAnimator
android:duration="500"
android:startOffset="int" //动画的延迟时间,当动画开始后,需要延迟多少毫秒才真正开始播放动画
android:repeatCount="int" //表示动画的重复次数 -1代表无限循环 默认为0
android:repeatMode={"restart" | "reverse"} // 动画的重复模式 第一个表示连续重复,第二个表示逆向重复:第一次播放正着播放,第二次逆向,第三次正向,第四次逆向....
android:propertyName="y"
android:valueTo="300"
android:valueType="intType" />
</set>
<objectAnimator
android:duration="500"
android:propertyName="alpha"
android:valueTo="1f" />
</set>
加载代码
AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(mContext,R.animator.property_animator);
animator.setTarget(view);
animator.start();
调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画就可以了,就是这么简单。
属性动画的监听
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
可以看到,我们需要实现接口中的四个方法,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。
但是也许很多时候我们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口全部实现一遍就显得非常繁琐。没关系,为此Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:
//或者使用adapter,选择自己要复写的方法
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
这里我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了
总结
- ValueAnimator --数值发生器,可以实现很多很灵活的动画效果;
- ObjectAnimator --继承于ValueAnimator,可以很好滴使用属性对话框架;
- AnimatorUpdateListener – 用于动画监听器
- AnimatorListenerAdapter-- 用于动画监听器
- PropertyValuesHolder --用于控制动画集合的显示效果
- Animatorset --用于控制动画集合的显示效果(更好的顺序控制)
- TypeEvaluators ---值计算器,用于控制值变化的规律(配合ValueAnimator使用)
- Interprolators ---插值计算器,用于控制值变化的规律