前言
动画在Android中是一个相当于重要的知识点,使用场景也很多,炫酷的界面效果少不了动画来提升,这里我们就先来说说Android中的动画,在说Android的动画之前,我们先来了解一下动画这个效果的通用含义。
什么是动画?
我们在平常生活中,经常会遇到动画这个词,大家也不要想歪了,可不是大家理解的岛国xx片哦!,那动画究竟是什么呐?在不同的场景中,这个词的名词解释都不一样,那我们这里也来讨论一下动画是什么?
1,动画就是动起来的画
2,动画最基本的一点就是能够随一定的时间间隔而变化
这个定义也是我总结的,感觉还挺契合这个概念,如果大家有异议,请告诉我,反正我也不会改的。
动画属性
动画的概念已经有了,那通用的动画有哪些属性呐,或者说具备哪些属性,比如我有如下一个动画:
从这个动画我们能够得出哪些属性?
描述 | 结论 |
---|---|
作用的对象 | traget |
持续时间 | duration |
从哪儿来 | from |
到哪儿去 | to |
开始时间 | beginTime |
重复次数 | repeatCount |
怎么去 | timeLine |
上面我大致总结了一下公共的属性,这些属性针对任何动画都适用,可能还总结的不够全,这里只是起到一个抛砖引玉的作用。
动画组合
定义有了,属性有了,还有啥公共是属性?那就是动画的执行效果了,一般有单个动画,就是一次只执行一个动画,还有就是组合动画了,多个动画可以组合成,串行,并行,串并行动画。
对公共的动画我们大致就先说这么多,现在开始转移到我们的主题内容,Android动画。接下的我们就来说说在Android中的动画的一些概念和怎么使用!
Android中的动画分类
- 视图动画
- Tween动画
- 平移动画
- 旋转动画
- 缩放动画
- 渐变动画
- Frame动画
- AnimationDrawable
- Tween动画
- 属性动画
视图动画
到这里,我们终于今天本章的主题了。这里主要来首先来说说视图动画,在下一章中我们会说说属性动画。视图动画与属性动画都同属于Android,但是实现方式是完全不同的。
注意:视图动画只能作用于View,能够实现的效果也仅限于上述的几种和他们动画的组合。
Android中的属性对应关系
前面我们总结了动画的通用属性,那我们这里就来看看Android中这些属性的对应关系:
描述 | 结论 | Android属性 |
---|---|---|
作用的对象 | traget | View |
持续时间 | duration | duration |
从哪儿来 | from | fromXXX |
到哪儿去 | to | toXXX |
开始时间 | beginTime | startOffset |
重复次数 | repeatCount | repeactCount |
怎么去 | timeLine | interpolator |
我们可以看到在Android中,属性对应的名称大致跟通用的总结的一致,至少看到名字就能理解意思。因此我们后面在实现动画的时候就按照这些属性来配置就ok了。
Tween动画的结构
- Animation
- AlphaAnimation
- TranslateAnimation
- RotateAnimation
- ScaleAnimation
- AnimationSet
这里前四个是单个动画,后面一个是动画集,他可以组合前面的动画。
Android动画实现
视图动画主要有两种方式来实现:
1,xml实现,首先在res/anim目录下定义动画的xml文件,之后带代码中用AnimationUtils.loadAnimation()加载
2, 代码实现,直接new对应的动画实例来实现动画。
我们来看几个动画效果,之后实例进行操作一遍:
xml实现
在res/anim下定义动画文件,我们分别定义渐变,平移,旋转,缩放四个动画文件:
渐变动画,名称为alpha.xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:startOffset="1000"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="2000">
</alpha>
fromAlpha表示动画开始的透明度,toAlpha表示动画结束的透明度,1.0表示完全不透明,0.0便是完全透明,两个都是浮点数,duration便是动画的持续时间,单位为毫秒,2000毫秒表示2秒。startOffset表示动画开始的延迟时间,点击开始1秒后开始执行,单位也是毫秒。
平移动画,名称为translate.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100%p"
android:toYDelta="100%p">
</translate>
fromXDelta,fromYDelta表示动画开始时的x,y坐标,toXDelta,toYDelta动画结束的x,y坐标,这里需要注意的是他可以设置三种类型的值,比如我们设置50:
50: 表示50个像素,是绝对值
50%: 表示自身大小一半,记住这里是可以设置%号的,他等于0.5,如果设置0.5表示绝对值0.5
50%p :表示父元素大小的一半,p表示相对于父元素
缩放动画,名称为scale.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromXScale="1"
android:fromYScale="1"
android:toYScale="2"
android:toXScale="2"
android:pivotX="50%"
android:pivotY="0%">
</scale>
fromXScale,fromXScale动画开始时x,y的缩放大小,toXScale,toXScale:动画结束的x,y缩放大小,1表示本身大小,数值越大,缩放比例越大,pivotX,piovtY:缩放的中心坐标位置,可以设置值与平移动画可以设置的类似:
50: 表示50个像素,是绝对值
50%: 表示自身中心位置
50%p :表示父元素的中心位置,p表示相对于父元素
旋转动画,名称为rotate.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%">
</rotate>
fromDegrees动画开始的角度,toDegress动画结束的角度,正数为顺时针,负数为逆时针,pivotX,piovtY:旋转的中心坐标位置,设置的方式与缩放相似。
xml文件定义好后,我们就需要在代码中进行使用,可以先定义一个view,之后在界面中find出来,之后对view设置动画:
private void rotate() {
Animation rotate = AnimationUtils.loadAnimation(this, R.anim.rotate);
imageView.startAnimation(rotate);
}
private void scale() {
Animation scale = AnimationUtils.loadAnimation(this, R.anim.scale);
imageView.startAnimation(scale);
}
private void translate() {
Animation translate = AnimationUtils.loadAnimation(this, R.anim.translate);
imageView.startAnimation(translate);
}
private void alpha() {
Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha);
imageView.startAnimation(alpha);
}
在代码中,都需要先load动画,采用AnimationUtils来进行load,之后将load出来的动画设置到view上就可以了。
代码实现
上面我们采用xml实现四种动画,接下来我们用代码来实现这个四种动画:
private void rotate() {
RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(3000);
imageView.startAnimation(rotate);
}
private void scale() {
ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(3000);
imageView.startAnimation(scale);
}
private void translate() {
TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
translate.setDuration(3000);
imageView.startAnimation(translate);
}
private void alpha() {
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setDuration(3000);
imageView.startAnimation(alpha);
}
从代码可以看出,四种动画都分别对应了不同的类,已经有四种现场的动画类可以使用。每次使用只需要new对应的实例,设置不同的参数,参数设置也很简单。这里就不在详细说了。
动画的开始于结束
动画我们已经实现了,但是如果你需要在动画的执行过程中,做某些操作怎么办呐?比如在动画开始的时候提示一下动画的开始,在动画的结束时,我们提示动画的结束,这我们又能怎么做呐?
系统也完全考虑到这个问题,我们可以对动画设置监听,可以对动画设置AnimationListener,如下:
rotate.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//todo
}
@Override
public void onAnimationEnd(Animation animation) {
// todo next anim
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
设置动画监听,在动画开始结束等时机,系统都会回调对应的函数,比如onAnimationStart就是告诉动画开始执行了。这样我们就可以根据不同的回调做想要做的事情。
组合动画
我们在抽象动画的公共属性说过,动画有单个动画,还有组合动画,上面我们所实现的都是单个动画,那怎么实现组合动画,组合动画又分为串行动画,并行动画,或者既有串行又有并行。这些又怎么实现:
- 多个单个动画来实现
- 串行动画,可以定义多个动画,分别对每一个动画设置监听,在上一个动画结束时,开始下一个动画,也可以设置动画的startOffset,下一个动画的startOffset为上一个动画的startOffset与duration之和
- 并行动画,定义多个动画,设置同样的startOffset与duration
- 采用AnimationSet来实现,下面我们主要才实现一下该方式
我们需要实现如下效果的动画,由于将MP4转换成gif,效果变得很差,将就着看吧:
这里也可以用两种方式实现,xml实现与代码实现,这里万变不离其宗,只不过是将上面的单个动画安装需要的效果组合一下就ok,下面我们用xml来实现一下该动画:
定义一个love_flay.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="3000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="-300"></translate>
<rotate
android:duration="3000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="3000"
android:toDegrees="-45"></rotate>
<scale
android:duration="3000"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="3000"
android:toXScale="2"
android:toYScale="2"></scale>
<alpha
android:duration="3000"
android:fromAlpha="1.0"
android:startOffset="3000"
android:toAlpha="0.0"></alpha>
</set>
代码加载上面的定义的xml。
private void loveAnim() {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.love_fly);
love.startAnimation(animation);
}
这样我们就实现了上面要求的效果,那代码方式又怎么实现? 首先定义多个单个动画,之后采用AnimationSet来实现,将定义的动画都add到set中,比如我们需要实现一个在平移的过程中放大并且逐渐消失的动画:
private void alpha() {
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setDuration(3000);
TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
translate.setDuration(3000);
ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(3000);
AnimationSet set = new AnimationSet(true);
set.addAnimation(alpha);
set.addAnimation(translate);
set.addAnimation(alpha);
imageView.startAnimation(set);
}
new AnimationSet(true),true表示是否公用插值器,后面我们会详细说说插值器。
这里我们就已经说完了怎么实现组合动画,之后我们来讲讲插值器是个啥!
插值器
说道插值器,我们首先要知道插值器是干什么用的?
插值器控制动画在时间轴上的变换效果
我们先来几个效果:
我们分别就这几个效果来做一下说明系统到底是怎么来控制效果的,首先我们来看看AccelerateDecelerateInterpolator,该插值器是先加速后减速。
插值器中主要是根据getInterpolation来获取某一个时间点的值,AccelerateDecelerateInterpolator的调用函数为(float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; 输入范围为0到1,根据这个函数我们可以生产如下的图片:
从函数图可以看出,当输入在0到1中时,函数先加速后减速,系统就是靠这个来控制动画的执行效果的。
我们在来看CycleInterpolator,该插值器有一个震荡的效果,我们来看看他的调用函数:(float)(Math.sin(2 * mCycles * Math.PI * input)); mCycles = 2, input( 0, 1),mCycles为2表示震荡两次,我们来看看函数图:
可以看到在0到1中循环了两次。
从上面的可以看到如果你想实现特定的效果,只需要实现特定的插值器就好了。
动画的最终位置
上面我们执行了多个动画,都发现每次执行完成后都回到了初始位置,那我们怎么才能将动画停止在最终位置?其实很容易实现该效果,只需要设置动画的setFillAfter为true就可以了。
动画的响应
我们对动画设置点击事件,代码如下:
public class LocationActivity extends AppCompatActivity {
private ImageView love;
public static void start(Context context) {
Intent intent = new Intent();
intent.setClass(context, LocationActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);
findViews();
setViewListener();
}
private void findViews() {
love = (ImageView) findViewById(R.id.love);
love.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(LocationActivity.this, "clicked", Toast.LENGTH_LONG).show();
}
});
}
private void setViewListener() {
findViewById(R.id.set).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loveAnim();
}
});
}
private void loveAnim() {
TranslateAnimation translate = new TranslateAnimation(0,0,0,-200);
translate.setDuration(3000);
translate.setFillAfter(true);
love.startAnimation(translate);
}
}
执行后,发现动画在最终位置不能点击,而点击事件还是在初始位置。
视图动画是不可交互动画,点击事件还是在初始位置
帧动画
我们来看看如下效果:
首先我们需要定义一个动画文件,但是该动画文件不是放在res/anim下,而是放置drawable下面。
定义一个play_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/play_1" android:duration="50" />
<item android:drawable="@drawable/play_2" android:duration="50" />
<item android:drawable="@drawable/play_3" android:duration="50" />
<item android:drawable="@drawable/play_4" android:duration="50" />
<item android:drawable="@drawable/play_5" android:duration="50" />
<item android:drawable="@drawable/play_6" android:duration="50" />
<item android:drawable="@drawable/play_7" android:duration="50" />
<item android:drawable="@drawable/play_8" android:duration="50" />
<item android:drawable="@drawable/play_9" android:duration="50" />
<item android:drawable="@drawable/play_10" android:duration="50" />
<item android:drawable="@drawable/play_11" android:duration="50" />
<item android:drawable="@drawable/play_12" android:duration="50" />
<item android:drawable="@drawable/play_13" android:duration="50" />
<item android:drawable="@drawable/play_14" android:duration="50" />
<item android:drawable="@drawable/play_15" android:duration="50" />
<item android:drawable="@drawable/play_16" android:duration="50" />
<item android:drawable="@drawable/play_17" android:duration="50" />
<item android:drawable="@drawable/play_18" android:duration="50" />
</animation-list>
之后在代码中进行加载:
public class FrameActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView frame;
private AnimationDrawable drawable;
public static void start(Context context) {
Intent intent = new Intent();
intent.setClass(context, FrameActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame);
findViews();
setViewsListener();
}
private void findViews() {
frame = (ImageView) findViewById(R.id.frame);
}
private void setViewsListener() {
findViewById(R.id.start).setOnClickListener(this);
findViewById(R.id.end).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start:
startAnim();
break;
case R.id.end:
stopAnim();
break;
}
}
private void stopAnim() {
if(drawable!=null){
if(drawable.isRunning()){
drawable.stop();
}
}
}
private void startAnim() {
if(drawable==null){
frame.setBackgroundResource(R.drawable.play_list);
drawable = (AnimationDrawable) frame.getBackground();
drawable.setOneShot(true);
}
if(!drawable.isRunning()){
drawable.start();
}
}
}
将动画文件设置为view的背景,之后在获取出来为AnimationDrawable,setOneShot如果为true,表示只执行一次,start开始执行动画,isRunning表示动画知否在执行,stop停止动画。其他属性就不一一解释了。
总结
到此视图动画就讲述完了,下一章来说说属性动画。