该文章笔记代码是从慕课网自定义view课程中学习到的,有兴趣的同学可以学习一下
我们在学习动画的时候以前总是用喜欢用Animation的一些子类进行动画的创建或者AnimationSet的集合进行动画的合并,到最后发现,API的耦合性使我并不是特别喜欢用那些对象。
今天看了大神的讲解视频,发现了几个比较好用的动画属性集,下面我将从基础开始逐一介绍。
这是我们学习基础动画时最常用的几个类
我在这里将根据一个小的平移动画(TranslateAnimation)的demo给大家展示我们需要学习的几个类的实现方法。
首先写一个简单的Demo图
在这里我写了一个imageview和一个button,分别实现了其onClick属性
TranslateAnimation
public void move(View view) {
TranslateAnimation ta = new TranslateAnimation(0, 200,0,0);
ta.setDuration(1000);//持续时间
ta.setFillAfter(true);//平移完后true代表保存平移后的状态
imageView.startAnimation(ta);//记得需要findViewById
}
public void click(View view) {
Log.i(TAG, "click: 点击了imageview");
}
四个构造参数分别为
- x轴初始坐标
- x轴的移动坐标
- y轴初始坐标
- y轴的移动坐标
因为我们只是简单的平移,所以只写在x轴上平移即可。
在click方法中,实现了一个点击安卓机器人图标打印日志的方法,这个需要留意一下,为了铺垫我们为什么要使用后来的ObjectAnimator。
我们来运行一下程序
我们发现,运行出来的结果与我们想的一致,这时候我们点击imageview现在所在的位置,发现出现的问题,Log并没有打印。这是为什么呢
原因是这个方法实现动画的原理在于动画只是在onDraw方法中重新绘制,动画的移动只是视图的单纯移动,所以在平移完后,我们点击图标,相当于点击了个一个空白的布局,如果我们再点击一开始图标初始的位置,我们发现,Log打印了...
你看,这就是个问题,我们该如何解决呢
谷歌官方给我们提供了几个优化好的类
ObjectAnimator
PropertyValuesHolder
AnimatorSet
ObjectAnimator
public void move(View view) {
ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f)
.setDuration(1000).start();//1 操纵的对象 2 所需要操纵的对象属性 3 动画变化的范围
}
public void click(View view) {
Log.i(TAG, "click: 点击了imageview");
}
创建一个ofFloat的静态方法,三个参数分别为
- 绑定好的imageview
- 一个平移x轴的字符串(所要操作的对象的属性)
- 第三个参数为动画变化的范围,这是一个可变的字符串。
将这三个参数定义好,再设置一下持续时间以及start()方法,我们就实现了一开始TranslateAnimation类的几行代码了。现在我们运行程序就可以实现同样的效果了。
同时,我们运行项目再次点击平移后机器人图标,发现Log日志可以打印了
那么ObjectAnimator能够实现AnimationSet的动画集合的方法吗,当然可以了
public void move(View view) {
ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f).setDuration(1000).start();//1 操纵的对象 2 所需要操纵的对象属性 3 动画变化的范围
ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f).setDuration(1000).start();//异步过程
ObjectAnimator.ofFloat(imageView, "translationY", 0f, 200f).setDuration(1000).start();
}
public void click(View view) {
Log.i(TAG, "click: 点击了imageview");
}
我定义了同时从y轴,x轴方向偏移,再加上一个旋转360度的动画,我们来运行一下试一试。
我们发现,动画在一秒内完成三个动作,同样实现了AnimationSet的效果。而且,我们运行项目再次点击平移后机器人图标,发现Log日志可以打印了。
因为此类需要不停的在onDraw方法进行绘制,特别浪费手机的GPU资源,我们该如何优化了,这时候,另一个类的出现就在某些方面替代了ObjectAnimator
PropertyValuesHolder
PropertyValuesHolder在优化方面优于ObjectAnimator,至于原理,我也不知道,现在我们来讲一下同样的效果,PropertyValuesHolder该如何实现
public void move(View view) {
//优化
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("translationX", 0f, 200f);
PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY", 0f, 200f);
ObjectAnimator.ofPropertyValuesHolder(imageView, p1, p2, p3).setDuration(1000).start();
}
public void click(View view) {
Log.i(TAG, "click: 点击了imageview");
}
通过调用PropertyValuesHolder的静态方法ofFloat,参数与ObjectAnimator.ofFloat参数一致。
最后我们通过ObjectAnimator.ofPropertyValuesHolder方法,将我们绑定好的imageview与定义的三个propertyValuesHolder对象传入。
通过方法链的形式启动动画
最后运行一下项目,发现效果与ObjectAnimator定义时效果一致。
这时候细心的同学应该会发现,AnimationSet是可以定义动画顺序的,那么现在该如何实现呢,别急,还有我们说的最后一个类 AnimatorSet
AnimatorSet
public void move(View view) {
//更加丰富的控制效果
ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "translationY", 0f, 200f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1, animator2, animator3);
//set.playSequentially(animator1, animator2, animator3);
//set.play(animator1).with(animator2);
//set.play(animator2).after(animator3);
set.setDuration(1000);
set.start();
}
public void click(View view) {
Log.i(TAG, "click: 点击了imageview");
}
我们用ObjectAnimator类创建了三个对象
调用Animator的playTogether方法,将三个对象传入最后start,我们发现也实现了上两个类的效果。
我们如何定义顺序呢,我们可以调用set.playSequentially方法,传入,运行项目我们发现顺序就是按照我们传入的三个参数的动画效果执行的。
set.play方法以及with(),after(),before(),又为我们实现了a动画和b动画同时运行完之后接着再运行c动画的需求,我们就可以更加细节化的处理动画了。
在下一节,我们将实现动画的监听事件
源代码
github源代码