Android学习笔记(七)| Android动画(上)—— View动画

参考书籍:《Android开发艺术探索》 任玉刚
如有错漏,请批评指出!

最近学习自定义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>
  1. 定义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"/>
  1. 给ListView 指定 LayoutAnimation
<ListView
    android:id="@+id/list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutAnimation="@anim/layout_anim01"/>
  1. 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);

具体的动画效果自己定义即可。


上一篇:Android学习笔记(六)| View的工作原理(下)
下一篇:Android学习笔记(八)| Android动画(中)—— 帧动画

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容