Android中的帧动画(Frame Animation)

动画可以为你的app注入活力与个性。让我们来看看实现动画的一个子类:帧动画(Frame Animation),从名字上可以看出,这种动画是一帧一帧的绘制出来的。

在谷歌的Material Design官方专题中,花了整整一页来介绍 Delightful Details ,其中有帧动画的绝佳例子。


真是精美的动画!不幸的是,这个页面上没有丁点关于如何实现这种效果的参考链接,所以我就来帮忙了!我们将讲解一遍如何制作空心心形到实心心形的过渡动画,然后讲解与之反向的动画。效果如下:

图片序列

帧动画的原理很简单:就像老式电影胶卷那样,快速掠过一些列的图片,“帧”其实就是一张图片,因此创建一个自定义帧动画的第一步就是建立图片序列。

我们有两种选择:使用xml的drawable(比如shape drawable)或者是使用实际的图片。简便起见,我们直接使用下面的一些列PNG图片:





在产品级的应用中,我们还需要保证图片尺寸可以适配不同的屏幕分辨率。但是现在,我们将所有的图片都扔到res/drawable-mdpi目录下完事。我推荐图片的命名采用自描述的方式,比如ic_heart_0.png, ic_heart_1.png以此类推。。。这样我们就不需要查看图片就知道图片的顺序。

但谁叫我是屌丝呢,我选择将图片按照填充的百分比程度命名。

XML Drawable

现在我们已经有了要掠一遍的图片,下一步就是为动画定义一个XML的Drawable,我们又遇到了两种选择:Animation-list和Animated-selector。

Animation-List

Animation-list是帧动画的默认选择,因为在API 1的时候就有了,同时它非常简单。就是简单的掠过指定顺序和持续时间的图片序列。

这里是填充到实心效果的Animation-list的例子,在res/drawable/animation_list_filling.xml中:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="true">

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_0"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_25"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_50"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_75"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_100"/>

</animation-list>

列表中的每一个item都指向图片序列中的一张图片。我们只需把它们的顺序摆放正确并且添加一个毫秒为单位的持续时间即可。

下面是实现变为空心效果的Animation-list,在res/drawable/animation_list_emptying.xml中:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="true">

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_100"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_75"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_50"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_25"/>

    <item
        android:duration="500"
        android:drawable="@drawable/ic_heart_0"/>

</animation-list>

你可能注意到了,在两个代码片段中都有android:oneshot=”true”,这是animation-list的一个属性,表示播放完一次动画之后便停止动画。如果这个属性值设置为“false”,则动画会重复播放。

在实际产品中,500毫秒时间太长,但是作为演示,我有意夸大了这个时间。还有一点,5帧图片对于产生流畅的过渡来说还是不够多。使用多少帧以及每帧的显示时间取决于个人。作为参考,我觉得15毫秒的15帧图片就可以非常流畅了。

Animated-Selector

Animated-selector要稍微复杂一些,因为它是基于状态的。根据View的状态(比如选中与激活状态),selector将使用提供的Transition来过渡到正确的状态。Animated-selector只在Lollipop上有效,因此我们需要在-v21 package中也定义一个xml。

下面是一个Animated-selector的例子,放在res/drawable-v21/selector.xml中:

<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/on"
        android:state_activated="true">
        <bitmap
            android:src="@drawable/ic_heart_100"/>
    </item>

    <item
        android:id="@+id/off">
        <bitmap
            android:src="@drawable/ic_heart_0"/>
    </item>

    <transition
        android:fromId="@+id/on"
        android:toId="@+id/off"
        android:drawable="@drawable/animation_emptying">
    </transition>

    <transition
        android:fromId="@id/off"
        android:toId="@id/on"
        android:drawable="@drawable/animation_filling">
    </transition>

</animated-selector>

仔细观察它是如何将前面定义的Animation-list引用为Transition的。

这个animated-selector没有任何问题,但是我们需要考虑非Lollipop设备。我们在res/drawable/selector.xml中定义一个没有动画的selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_activated="true">
        <bitmap android:src="@drawable/ic_heart_100"/>
    </item>

    <item
        android:state_activated="false">
        <bitmap android:src="@drawable/ic_heart_0"/>
    </item>

</selector>

现在我们的selector在任意设备上都可以工作。因为我们只使用了一般的selector,如果在Lollipop以前的设备上,animated-selector将直接跳过过渡动画,直接到结束状态。当然Lollipop设备是有我们在animated-selector中定义的过渡效果的。

在上面的代码片段中,animated-selector只关注了android:state_activated属性。就如一般的selector一样,我为不同的状态定义了不同的item,但不同的是,我们还定义了不同状态间动画过渡的transition。在这个例子中,我直接将transition指向前面定义好了的animation-list。

现在我们有了四个xml文件:一个充到实心效果的xml,实心到空心的xml,两个在空心实心之间切换的xml。

设置ImageView

现在可以设置一些图片来玩了。我们这里有三个ImageView,分别对应前面定义的三个XML Drawable。将下面的代码放到你的Activity的布局中:

<ImageView
    android:id="@+id/imageview_animation_list_filling"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/animation_list_filling"
    />

<ImageView
    android:id="@+id/imageview_animation_list_emptying"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/animation_list_emptying"
    />

 <ImageView
    android:id="@+id/imageview_animated_selector"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/selector"
    />

这只是几个id唯一,背景为我们定义的xml Drawable的ImageView。

开始动画

开始动画的方式在两种实现方法中是不一样的,我们先从animation-list开始:

Animation-List

在Activity中,我们得到ImageView的引用,然后开始动画。如下:

ImageView mImageViewFilling = (ImageView) findViewById(R.id.imageview_animation_list_filling);
((AnimationDrawable) mImageViewFilling.getBackground()).start();

下面是效果:

接下来是它的搭档-反向过程(除了id都是一样的)

ImageView mImageViewEmptying = (ImageView) findViewById(R.id.imageview_animation_list_emptying);
((AnimationDrawable) mImageViewEmptying.getBackground()).start();

效果如下:

这些代码可以放在onCreate(在Activity开始的时候自动开始)或者一个OnClickListener(等待用户触发)中,取决于你自己!

Animated-Selector

当使用Animated-selector的时候,动画将在状态条件满足selector的时候被触发。在我们这个简单的例子中,我们在Activity的onCreate方法中为ImageView添加一个click listener:

mImageViewSelector.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mImageViewSelector.setActivated(!mImageViewSelector.isActivated());
    }
});

当用户点击心形,它将会根据当前的状态在实心与空心之间切换,下面是我的心形循环显示的gif图:

Delightful Details

Frame Animations have the power to surprise and delight users, plus it’s fun to add little personal touches to an app. Go forth and animate!
帧动画可以取悦你的用户,为app增添个性,搞起!

安居客 Android APP 走向平台化 | 开发者说·DTalk

安居客 Android App 距离上次的模块化/组件化重构已经两年多了,重构之后很好的支撑了两年多以来的业务发展。但这个世界总是在向前走的,没有任何一种架构能够一劳永逸的解决所有问题,外部环境的不断变化相应的也要求项目架构做出改变,以此来应对环境变化所带来的挑战。

发布于:3天以前  |  22次阅读  |  详细内容 »

Android View 体系竟然还能这么理解?

很多小伙伴可能在学习view的绘制流程源码的时候有点抓不住重点,所以在分析代码的时候绕来绕去脑袋晕乎乎的。今天我就来给大家化繁为简,只关注它最核心的东西。

发布于:6天以前  |  72次阅读  |  详细内容 »

移动端常见崩溃指标

崩溃分析,是将 Android 和 iOS 平台常见的 APP 崩溃问题进行归类分析,帮助企业根据崩溃指标快速发现、定位问题。

发布于:8天以前  |  101次阅读  |  详细内容 »

Android死锁初探

说到死锁,大家可能都不陌生,每次遇到死锁,总会让计算机产生比较严重的后果,比如资源耗尽,界面无响应等。

发布于:12天以前  |  137次阅读  |  详细内容 »

Android AES加密(Kotlin)

halo~最近工作上写的东西比较简单,感觉分享不出来,最近刚好看到数据加密这一块,感觉挺不错的,也挺好用的,所以下面分享给大家!

发布于:17天以前  |  123次阅读  |  详细内容 »

Android 持续滑动布局 ConsecutiveScrollerLayout 的使用

在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是WebView,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的ScrollView下嵌套多个RecyclerView、WebView、ScrollView来实现。但是这种嵌套的方式不仅会严重影响布局的性能,而且处理滑动事件的冲突也是一件头疼的事,处理不好会严重影响用户操作的体验。

发布于:21天以前  |  147次阅读  |  详细内容 »

Android 升级适配爬坑历程

最近接手了一个公司项目,项目比较老了,从Android 5.0之后就再也没有适配过了,然而重写时间又来不及,然后我的爬坑之旅便开始了。(以下适配方案是按照项目需求顺序来的)

发布于:24天以前  |  226次阅读  |  详细内容 »

2020年,一文点破跨平台开发框架现状

多年来,跨平台移动开发已经获得了最流行软件开发趋势之一的声誉。这并不令人意外,因为采用跨平台开发技术使得软件工程师使用同一代码就能为不同平台构建应用程序,从而节省时间、金钱以及不必要的工作。

发布于:1月以前  |  233次阅读  |  详细内容 »

最多阅读

简化Android的UI开发 8月以前  |  197687次阅读
Android设计与开发工作流 7月以前  |  2329次阅读
Google Enjarify:可代替dex2jar的dex反编译 1年以前  |  2322次阅读
Android多渠道打包工具:apptools 1年以前  |  1942次阅读
Android权限 - 第一篇 1年以前  |  1910次阅读
Google Java编程风格规范(中文版) 1年以前  |  1896次阅读
Stetho 1年以前  |  1829次阅读
Android UI基本技术点 1年以前  |  1825次阅读
30分钟搭建一个android的私有Maven仓库 1年以前  |  1799次阅读
2015 Google IO带来的新 Android 开发工具 1年以前  |  1720次阅读
你应该知道的布局和属性 1年以前  |  1674次阅读
听FackBook工程师讲*Custom ViewGroups* 1年以前  |  1666次阅读
Gradle小知识#3:任务的顺序 1年以前  |  1644次阅读
MVP在Android平台上的应用 1年以前  |  1644次阅读