最近学习自定义View过程中,频繁使用到动画效果的控制,由于对这部分了解尚少,因此在这里系统的学习一下。
Android 中动画可以分为三种:View动画、帧动画和属性动画。View动画通过对场景中的对象进行平移、旋转、缩放、改变透明度等变换,从而产生动画效果,它是一种渐进式动画;帧动画通过顺序播放一系列图像从而产生动画效果,但图片过多过大时可能导致OOM;属性动画通过动态改变对象的属性从而达到动画效果,属性动画为API 11 新特性,在低版本中可以通过兼容库来使用它。下面我们先来看View动画。
View 动画的种类
View动画的作用对象是View,它支持四种动画效果,分别是平移动画、旋转动画、缩放动画和透明度动画,它们分别对应着 Animation 的四个子类:TranslateAnimation、RotateAnimation、ScaleAnimation、AlphaAnimation,如下表:
| 名称 | xml标签 | 子类 | 效果 | 
|---|---|---|---|
| 平移动画 | <translate> | TranslateAnimation | 移动View | 
| 缩放动画 | <scale> | ScaleAnimation | 缩小或放大View | 
| 旋转动画 | <rotate> | RotateAnimation | 旋转View | 
| 透明度动画 | <alpha> | AlphaAnimation | 改变View的透明度 | 
这四种动画既可以通过xml来定义,也可以通过代码来动态创建,不过通过xml定义可读性更好。下面来看一下如何通过xml定义。
首先在res目录下新建一个 anim 目录,在这个目录下新建一个 Animation resource file ,即:res/anim/anim_filename.xml
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android"></set>
<set/> 标签
这是创建的文件的结构,它默认有一个 <set/> 标签,表示动画集合,因为一个动画效果可以由上面的四种动画组合起来,下面我们来看一下它的一些属性:
- 基本属性: 
| 属性 | 作用 | 属性值 | 
|---|---|---|
| duration | 设置动画持续时间(ms) | 例如:5000 即:5s | 
| fillAfter | 设置动画结束后View是否恢复初始状态 | true 或 false | 
| fillBefore | 设置动画结束后View变换效果是否保留(放大、缩小、透明度) | true 或 false | 
| repeatMode | 设置动画重复模式 | reverse —— 倒序回放 restart —— 重置开始 | 
| repeatCount | 设置动画重复次数(在具体动画中指定才会生效) | 例如:10 即重复10次(特别:-1表示无限循环) | 
| startOffset | 设置动画延迟开始时间(ms)(即start之后等待一段时间开始) | 例如:1000 即延迟1s开始 | 
- 进阶属性: 
| 属性 | 作用 | 属性值 | 
|---|---|---|
| interpolator | 设置动画的插值器,使基本动画效果以匀速、加速、减速、抛物线速率等速率变化 | 默认为 @android:/anim/accelerate_decelerate_interpolator 即加速减速插值器 | 
| shareInterpolator | 设置集合中动画是否和集合共享一个插值器 | true 或 false(设置为 false 则需要单独为集合中动画设置插值器) | 
<translate/> 标签
- 基本属性(坐标均相对于View自身而言,View左上角即(0, 0)) 
| 属性 | 描述 | 
|---|---|
| fromXDelta | 起始 x 坐标 | 
| fromYDelta | 起始 y 坐标 | 
| toXDelta | 结束 x 坐标 | 
| toYDelta | 结束 y 坐标 | 
- 示例 
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fillAfter="true" android:fillBefore="true" android:repeatMode="reverse" android:startOffset="100"> <translate android:fromXDelta="0.0" android:fromYDelta="0.0" android:toXDelta="200.0" android:toYDelta="100.0" android:repeatCount="-1"/></set>
<rotate/> 标签
- 基本属性(坐标均相对于View自身而言,View左上角即(0,0)) 
| 属性 | 描述 | 
|---|---|
| fromDegrees | 旋转初始角度 | 
| toDegrees | 旋转结束角度 | 
| pivotX | 旋转轴点 x 坐标(默认0) | 
| pivotY | 旋转轴点 y 坐标(默认0) | 
注意:旋转轴点默认为View左上角,即(0, 0)
- 示例 
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fillAfter="true" android:fillBefore="true" android:repeatMode="reverse" android:startOffset="100" > <rotate android:fromDegrees="0" android:toDegrees="-180" android:pivotX="100" android:pivotY="-50" android:repeatCount="-1"/></set>
<scale/> 标签
- 基本属性(坐标均相对于View自身而言,View左上角即(0,0)) 
| 属性 | 描述 | 
|---|---|
| fromXScale | 初始X轴缩放比例(1.0 表示初始大小) | 
| fromYScale | 初始Y轴缩放比例(1.0 表示初始大小) | 
| toXScale | 结束X轴缩放比例(相对于View自身大小) | 
| toYScale | 结束Y轴缩放比例(相对于View自身大小) | 
| pivotX | 缩放轴点 x 坐标(默认0) | 
| pivotY | 缩放轴点 y 坐标(默认0) | 
注意:旋转轴点默认为View左上角,即(0, 0)
- 示例 
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fillAfter="true" android:fillBefore="true" android:repeatMode="reverse" android:startOffset="100" > <scale android:fromXScale="1.0" android:fromYScale="1.0" android:toXScale="2.0" android:toYScale="2.0" android:pivotX="0" android:pivotY="0" android:repeatCount="-1"/></set>
<rotate/> 标签
- 基本属性(坐标均相对于View自身而言,View左上角即(0,0)) 
| 属性 | 描述 | 
|---|---|
| fromAlpha | 起始透明度(1.0表示正常状态) | 
| toAlpha | 结束透明度(0.0~1.0之间) | 
注意:旋转轴点默认为View左上角,即(0, 0)
- 示例 
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:fillAfter="true" android:fillBefore="true" android:repeatMode="reverse" android:startOffset="100" > <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:repeatCount="-1"/></set>
xml定义动画的调用方式:
调用 AnimationUtils 类的 loadAnimation(Context context, @AnimRes int id) 方法可以得到一个 Animation 对象,第二个参数就是定义动画的 Amimation 资源文件,然后调用控件的 startAnimation(Animation animation) 方法将 Animation 对象传入即可。下面看代码:
public class ViewAnimActivity extends AppCompatActivity {    @BindView(R.id.tv_translate) TextView tvTranslate;    @BindView(R.id.tv_rotate) TextView tvRotate;    @BindView(R.id.tv_scale) TextView tvScale;    @BindView(R.id.tv_alpha) TextView tvAlpha;    private Animation translateAnimation;    private Animation rotateAnimation;    private Animation scaleAnimation;    private Animation alphaAnimation;    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_anim);
        ButterKnife.bind(this);
        translateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_anim01);
        rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_anim02);
        scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.view_anim03);
        alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.view_anim04);
    }    @OnClick(R.id.but_start)    public void onClock() {
        tvTranslate.startAnimation(translateAnimation);
        tvRotate.startAnimation(rotateAnimation);
        tvScale.setAnimation(scaleAnimation);
        tvAlpha.setAnimation(alphaAnimation);
    }
}接下来看看上面这四个示例的效果:
代码动态创建View动画
当然,上面的动画效果也可以用Java代码实现,以第一个平移动画为例:
TranslateAnimation mTranslateAnimation = new TranslateAnimation(0.0f, 200.0f, 0.0f, 100.0f); mTranslateAnimation.setDuration(1000); mTranslateAnimation.setRepeatMode(Animation.REVERSE); mTranslateAnimation.setRepeatCount(Animation.INFINITE);//即 -1 mTranslateAnimation.setFillAfter(true); mTranslateAnimation.setFillBefore(true); tvTranslate.startAnimation(mTranslateAnimation);
这段代码实现的效果与前面使用xml定义的平移动画的效果是一模一样的,理解起来也很简单。另外,通过 Animation 的 setAnimationListener() 方法,可以给 View 动画添加过程监听,接口源码如下:
    public static interface AnimationListener {        /**
         * <p>Notifies the start of the animation.</p>
         *
         * @param animation The started animation.
         */
        void onAnimationStart(Animation animation);        /**
         * <p>Notifies the end of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which reached its end.
         */
        void onAnimationEnd(Animation animation);        /**
         * <p>Notifies the repetition of the animation.</p>
         *
         * @param animation The animation which was repeated.
         */
        void onAnimationRepeat(Animation animation);
    }这三个接口分别在动画开始、结束及重复的时候回调。
View动画的特殊使用场景
前面是关于View动画的四种基本形式,除此之外,View动画还可以在一些特殊情景下使用,比如在 ViewGroup 中可以控制子元素的显示效果,在 Activity 中可以实现不同Activity之间的切换效果。
LayoutAnimation 的使用
LayoutAnimation 作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素显示时都会具有这种动画效果,这种效果通常被用在ListView上,下面来看一下具体用法:
- LayoutAnimation 基本属性 
| 属性 | 描述 | 
|---|---|
| delay | 子元素开始动画的时间延迟,例如子元素的动画 duration 指定为 1000ms,delay指定为0.1 表示每个子元素都会在前一个子元素显示之后延迟 100ms 开始显示动画 | 
| animationOrder | 子元素动画的顺序,normal——顺序显示、reverse——倒序显示、random——随机显示 | 
| animation | 指定子元素显示动画 | 
- 示例 
 1.定义子 item 显示动画:
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:shareInterpolator="true" android:interpolator="@android:anim/accelerate_interpolator"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0"/> <translate android:fromXDelta="500.0" android:toXDelta="0.0"/></set>
- 定义LayoutAnimation(xml定义和代码动态定义都可以) 
<?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/layout_anim_item"/>
- 给ListView 指定 LayoutAnimation 
<ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layoutAnimation="@anim/layout_anim01"/>
- ListView 的使用照常(简单起见,这里使用系统内置的item布局,给出关键代码) 
ArrayAdapter<String> mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, listData); mListView.setAdapter(mAdapter);
最后来看一下实现效果:
- 代码创建LayoutAnimation:通过 LayoutAnimationController 来实现 
Animation animation = AnimationUtils.loadAnimation(this, R.anim.layout_anim_item); LayoutAnimationController controller = new LayoutAnimationController(animation); controller.setDelay(0.5f); controller.setOrder(LayoutAnimationController.ORDER_NORMAL); mListView.setLayoutAnimation(controller); ArrayAdapter<String> mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, listData); mListView.setAdapter(mAdapter);
Activity的切换效果
Activity有默认的切换效果,不过这个效果我们是可以自定义的,主要用到overridePendingTransition(int enterAnim, int exitAnim) 这个方法,这个方法必须在 startActivity(Intent intent) 或者 finish() 方法之后才会生效。这个方法接收两个参数:
- enterAnim—— Activity被打开时,所需要的动画资源 
- exitAnim—— Activity被暂停时, 所需要的动画资源 
Intent intent = new Intent(this, ViewAnimActivity.class); startActivity(intent); overridePendingTransition(R.anim.activity_enter_anim, R.anim.activity_exit_anim);
作者:laughter_jiang
链接:https://www.jianshu.com/p/149ececfa006