在前面的文章中我们写了view中的图形的绘制以及水波纹的效果,从中我们可以看出做一些稍微复杂的效果都需要用到动画的功能,在android系统api1中提供了视图动画,在api11的时候提供了属性动画,接下来我们就开始讲讲基本的动画功能。
-
视图动画使用
通过官网我们可以看到视图动画,可以简单分为两类,补间动画和帧动画,补间动画包括位移,旋转,透明度变化,缩放变化。 帧动画就是提供一些列的drawable连续播放。
作用 | 类名 |
---|---|
对view进行位移 | TranslateAnimation |
对view进行旋转 | RotateAnimation |
对view进行缩放 | ScaleAnimation |
设置view的透明度 | AlphAnimation |
drawable 连续播放 | DrawableAnimation |
在使用动画之前首先要明确的是,补间动画只能是view与它的子类可以使用。通过view.startAnimation(Animation) 来实现。所有的动画都有两种方式进行实现。一个是在代码中实现,一个是在xml中创建。 通过AnimationUtils.loadAnimation()来获取实例。
ImageView iv = findViewById(R.id.rotate_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
beginAnimation();
float left = iv.getLeft();
float right = iv.getRight();
int top = iv.getTop();
int bottom = iv.getBottom();
//围绕图片中心点进行旋转,旋转180度
animation = new RotateAnimation(0,180,(right-left)/2,(bottom-top)/2);
//设置旋转所需时间
animation.setDuration(2000);
//fillafter属性设置为true,表示动画执行完毕保存执行之后的状态,不会复位
animation.setFillAfter(true);
//为这个动画设置一个时间插入器,决定动画执行的快慢方式。
animation.setInterpolator(new AccelerateDecelerateInterpolator());
//开始动画
iv.startAnimation(animation);
}
});
这就是一个简单的通过代码来设置旋转动画,又或者我们可以通过创建xml的方式来实现动画,动画的资源文件放在/res/anim目录下, 比如我们做一个平移动画translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100"
android:fromYDelta="0"
android:toXDelta="400"
android:toYDelta="600"
/>
</set>
然在在代码中来实现:
final ImageView iv = findViewById(R.id.scale_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
iv.startAnimation(animation);
}
});
animation = AnimationUtils.loadAnimation(this,R.anim.translate);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
animation.setDuration(2000);
animation.setFillAfter(true);
这就是两种动画实现方式的简单展示。在使用动画的时候我们可以通过设置监听的方式对动画的开始,结束,以及重复进行监听,在回调函数里做出相应的动作。
在大多数的情况下,一个单一的动画无法实现我们的需求。 当需要多个动画组合执行的时候,可以用AnimationSet来讲所有动画集合起来执行。 其实AnimationSet就可以看做是一个动画的集合。 下面就是使用set的一个简单例子。 当时set也可以通过xml文件实现。
private void beginAnimation() {
// 创建动画集合
AnimationSet aniSet = new AnimationSet(false);
float left = iv.getLeft();
float right = iv.getRight();
int top = iv.getTop();
int bottom = iv.getBottom();
Log.e("tag","the left="+left+", right="+right+", top="+top+", bottom="+bottom);
// 透明度动画
AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
alpha.setDuration(time);
aniSet.addAnimation(alpha);
// 旋转动画
RotateAnimation rotate = new RotateAnimation(0,720,(right-left)/2,(bottom-top)/2);
rotate.setDuration(time);
aniSet.addAnimation(rotate);
// 缩放动画
ScaleAnimation scale = new ScaleAnimation(0.1f, 1, 0.1f, 1f,(right-left)/2,(bottom-top)/2);
scale.setDuration(time);
aniSet.addAnimation(scale);
TranslateAnimation translate = new TranslateAnimation(right, left, bottom, top);
translate.setDuration(time);
aniSet.addAnimation(translate);
// 动画监听
aniSet.setAnimationListener(new Animation.AnimationListener() {
// 动画开始
@Override
public void onAnimationStart(Animation animation) {
}
// 动画结束,一般在这里实现页面跳转逻辑
@Override
public void onAnimationEnd(Animation animation) {
// 动画结束后,跳转到主页面
// startActivity(new Intent(RotateActivity.this, MainActivity.class));
}
// 动画重复
@Override
public void onAnimationRepeat(Animation animation) {
}
});
// 把动画设置给llGroup
iv.startAnimation(aniSet);
}
这几个可视动画都是继承Animation,其实动画可以解析成几个模块,首先做什么样的动作(平移,旋转等)。 第二时间(需要多久执行完),第三时间插入器(Interpolator )它决定动画执行的速率。加速执行还是减速或者匀速执行。最后可以添加动画监听。而其实我们所做的各个动画,其实本质上可以看做是矩阵的变换执行结果。想对view动画进行重写必须对矩阵Matrix清楚。这里有一篇矩阵的博客写的很好, 希望大家看看这篇博客,会对矩阵有更清晰的认识。
-
qq抖动
下面我们就通过继承Animation来实现自己所需动画的功能。首先我们分析Animation.java的源码发现子类必须继承实现applyTransformation()这个方法
/**
* Helper for getTransformation. Subclasses should implement this to apply
* their transforms given an interpolation value. Implementations of this
* method should always replace the specified Transformation or document
* they are doing otherwise.
*
* @param interpolatedTime The value of the normalized time (0.0 to 1.0)
* after it has been run through the interpolation function.
* @param t The Transformation object to fill in with the current
* transforms.
*/
protected void applyTransformation(float interpolatedTime, Transformation t) {
}
通过注释说明可以看出interpolatedTime是一个时间因子,他是一个范围从0到1的float类型,它表示动画已经执行的时间占总时间的百分比。transformation表示的相当于在某个时间点的动画的转换。我们可以通过它获取当前变换的矩阵,然后做自己的所需要的操作。
public class MyTranslateAnimation extends Animation{
float x,y,tox,toy;
//这个类的本意是模拟实现translateAnimation,当用作抖动动画使用的时候,参数无意义
public MyTranslateAnimation(float fromx,float tox,float fromy,float toy) {
this.x = fromx;
this.y = fromy;
this.tox = tox;
this.toy = toy;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t){
//模拟的translateAnimation实现
/* float dx = x + (tox -x)*interpolatedTime;
float dy = y + (toy - y)*interpolatedTime;
t.getMatrix().setTranslate(dx, dy);*/
//因为interpolatedTime为0到1,所以乘以10π,表示5个sin周期,这个值越大,晃动的频率就越
//高,
//因为Math.sin(interpolatedTime*10*Math.PI)他的值为[-1,1]所以以向左向右10像素移动。
t.getMatrix().setTranslate((float)(Math.sin(interpolatedTime*10*Math.PI)*10),
(float)Math.sin(interpolatedTime*10*Math.PI)*5);
super.applyTransformation(interpolatedTime,t);
}
}
直接通过以下代码就可以实现都图片的抖动:
public class TranslateActivity extends AppCompatActivity {
MyTranslateAnimation animation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translate);
final ImageView iv = findViewById(R.id.translate_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
iv.startAnimation(animation);
}
});
animation = new MyTranslateAnimation(0,1000,0,500);
animation.setFillAfter(true);
animation.setDuration(2000);
}
其实无论是想实现什么样的动画,都是需要通过时间因子与matrix相互结合然后实现的。就像上面模拟位移动画一样,我们可以看看translateAnimation的源码:
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float dx = mFromXDelta;
float dy = mFromYDelta;
if (mFromXDelta != mToXDelta) {
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
}
if (mFromYDelta != mToYDelta) {
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
}
t.getMatrix().setTranslate(dx, dy);
}
原理上与我们自己写的是一致的, 如果有空可以看看其他几个动画的源码。你就发现其实并不难。
-
总结
实现自己需要的动画,一定要了解矩阵的知识,推荐:https://www.cnblogs.com/fordreamxin/p/4721497.html这个作者写的。