声明:本篇文章主要是学习《Android开发艺术与探索》时做的学习笔记。
概述
Android动画可分为三种:View动画丶帧动画和属性动画,其实帧动画也属于View动画的一种,只不过它和平移丶旋转等常见的View动画在表现形式上略有不同而已。
1. View动画通过对场景里的对象不断做图像变换(平移丶缩放丶旋转丶透明度)从而产生动画效果,是一种渐进式动画,并且View动画支持自定义。
2. 帧动画则是通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,很显然,帧动画如果图片过多过大会导致OOM。
3. 属性动画通过动态改变对象的属性从而达到动画效果,属性动画是API 11的新特性,在低版本无法直接使用属性动画,但是我们依然可以通过兼容库来使用它。
一丶View动画
View动画的作用对象是View,支持4中动画效果:平移丶缩放丶旋转和透明度动画。
View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation丶ScaleAnimation丶RotateAnimation和AlphaAnimation。对于View动画,可以通过代码动态创建,也可通过XML来定义,建议采用XML来定义View动画,因为XML格式的动画可读性好。
1. View动画的使用
首先在res/anim/下创建动画XML文件,View动画的描述语法一般都是固定的,具体如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true">
<alpha
android:duration="2000"
android:fromAlpha="0"
android:toAlpha="1" />
<scale
android:duration="2000"
android:fromXScale="0"
android:toXScale="1"
android:fromYScale="0"
android:toYScale="1"
android:pivotX="50%"
android:pivotY="50%" />
<translate
android:duration="2000"
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"/>
<rotate
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
</set>
上面的动画是一个动画集合,当然也可以只有单个动画。标签表示动画集合,对应的是AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的。需要注意的是android:pivotX属性一般指定为百分比,50%则表示在其中心点的位置。
并且,对于标签,它有两个比较重要的属性:
android:interpolator
表示动画集合所采用的插值器,插值器影响动画的速度,比如非匀速动画就需要插值器来控制动画的播放过程。这个属性可以不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
android:shareInterpolator
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独制定所需要的插值器或者使用默认值。
要使用上面XML定义的动画,代码如下:
ImageView mImageView = (ImageView) findViewById(R.id.mImageView);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_view);
mImageView.startAnimation(animation);
并且除了在XML中定义动画,还可以通过代码的形式来应用动画,比如想要将一张图片的透明度在2000ms内从0变为1,具体代码如下(想要实现其他动画类似):
ImageView mImageView = (ImageView) findViewById(R.id.mImageView);
AlphaAnimation animation = new AlphaAnimation(0,1);
animation.setDuration(2000);
mImageView.startAnimation(animation);
2. View动画的监听器
通过Animation的setAnimationListener方法可以给View动画添加过程监听,下面是具体的代码演示:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Log.e("TAG","动画开始");
}
@Override
public void onAnimationEnd(Animation animation) {
Log.e("TAG","动画结束");
}
@Override
public void onAnimationRepeat(Animation animation) {
Log.e("TAG","动画重新开始播放");
}
});
3. View动画的特殊使用场景
①LayoutAnimation
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场也会具有这种动画效果。这种效果常用于ListView上,那么我们的ListView的item都会以一定的动画形式出现。LayoutAnimation也是一个View动画,为了给ViewGroup的子元素添加出场效果,遵循以下几步:
(1)在定义LayoutAnimation,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/anim_view">
</layoutAnimation>
LayoutAnimation有几个比较重要的属性:
android:delay
表示子元素开始动画的时间延迟,比如子元素入场动画的时间周期为300ms,那么0.5就代表每个子元素都需要延迟150ms才能播放入场动画。总体来说,第一个子元素延迟150ms开始播放入场动画,第二个子元素延迟300ms开始播放入场动画,依次类推。
android:animationOrder
表示子元素动画的顺序,有三种选项:nomal丶reverse丶和random,其中normal表示顺序显示,reverse表示逆向显示,random则表示随机播放入场动画。
android:animation
为子元素指定具体的入场动画。
(2)为子元素指定具体的入场动画,如下所示:
在res/anim/anim_view
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:fromXDelta="500"
android:toXDelta="0"/>
</set>
(3)为ViewGroup指定android:layoutAnimation属性:android:layoutAnimation=”@anim/anim_layout”。对于ListView来说,这样ListView的item就具有出场动画了,这种方式适用于所有ViewGroup,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/mListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="@anim/anim_layout"/>
</RelativeLayout>
到这里,LayoutAnimation的用法基本已经讲完,为了更好的体现它的效果,这里我写了一个小Demo,具体的布局文件都采用的是上面步骤里的,这里只贴MainActivity的代码:
- MainActivity
package com.wangjian.annimationtest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
private int[] listData = {R.mipmap.a1,R.mipmap.a2,R.mipmap.a3,R.mipmap.a4,R.mipmap.a5,R.mipmap.a6,};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.mListView);
mListView.setAdapter(new MyAdapter());
}
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return listData.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = View.inflate(MainActivity.this, R.layout.item, null);
holder = new ViewHolder();
holder.imageView = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.imageView.setBackgroundResource(listData[position]);
return convertView;
}
}
class ViewHolder{
ImageView imageView;
}
}
代码很简单,就是给ListView的每个item填充一张图片,并且采用ViewHolder进行了复用操作。
运行结果如下:
通过运行结果我们会发现一个问题,那就是后面几张图片并没有入场动画,原因很简单,是因为与ListView的复用之间的冲突,由于它们是被复用的,出场动画在之前已经播放过了。目前我还没有找到比较好的解决方案,已经解决了的同学还望留言分享一下,或者采用其他方式去实现。
除了在XML中指定LayoutAnimation外,还可以通过LayoutAnimationController来实现,具体代码如下:
mListView = (ListView) findViewById(R.id.mListView);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_view);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mListView.setLayoutAnimation(controller);
②实现Activity和Fragment的切换效果
Activity之间的切换效果主要用到overridePendingTransition(int enterAnim, int exitAnim)方法,并且这个方法必须在startActivity(intent)或者finish()之后调用才能生效。
enterAnim:Activity被打开时,所需要的动画资源id;
exitAnim:Activity被暂停时,所需要的动画资源id。
并且Fragment也可以添加切换动画,由于Fragment是在API 11中新引入的类,因此我们一般使用support-v4这个兼容包。要为Fragment也可以添加切换动画,主要通过FragmentTransaction中的setCustomAnimations()方法来添加切换动画。这个切换动画需要是View动画,之所以不能采用属性动画是因为属性动画也是API 11新引入的,会有兼容性的问题。
二丶 帧动画
帧动画是顺序播放一组预先定义好的图片,类似于电影的播放。不同于View动画,系统提供了另外一个类AnimationDrawable来使用帧动画。要使用帧动画,首先需要在res/drawable/通过XML文件来定义一个AnimationDrawable,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@mipmap/a1" android:duration="500" />
<item android:drawable="@mipmap/a2" android:duration="500" />
<item android:drawable="@mipmap/a3" android:duration="500" />
<item android:drawable="@mipmap/a4" android:duration="500" />
<item android:drawable="@mipmap/a5" android:duration="500" />
<item android:drawable="@mipmap/a6" android:duration="500" />
</animation-list>
然后将上述的Drawable作为View的背景并通过Drawable来播放动画即可:
ImageView mImageView = (ImageView) findViewById(R.id.mImageView);
mImageView.setBackgroundResource(R.drawable.animdrawable);
AnimationDrawable drawable = (AnimationDrawable) mImageView.getBackground();
drawable.start();
帧动画使用简单,其中 android:oneshot代表帧动画的自动执行, 如果为true,表示动画只播放一次停止在最后一帧上,如果设置为false表示动画循环播放。帧动画容易造成OOM,所以在使用时应尽量避免使用过多尺寸较大的图片。