Android动画之帧动画和补间动画

Android动画分类

  Android动画可以说是Android里面比较重要的部分,它分为三种:`帧动画`、`补间动画`和`属性动画`。由于帧动画和补间动画相对属性动画来说,简单一些,所以把这俩放在一起记录,属性动画单独拿出来记录。

帧动画

  帧动画其实就是通过顺序播放一系列图片从而产生动画的效果,就像电视画面一样,可以简单的理解为图片切换,Android系统提供了一个类`AnimationDrawable`来使用帧动画,使用起来也比较简单。

首先需要通过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="@drawable/loading1" android:duration="500" />
    <item android:drawable="@drawable/loading2" android:duration="500" />
    <item android:drawable="@drawable/loading3" android:duration="500" />
    <item android:drawable="@drawable/loading4" android:duration="500" />
    <item android:drawable="@drawable/loading5" android:duration="500" />
    <item android:drawable="@drawable/loading6" android:duration="500" />
    <item android:drawable="@drawable/loading7" android:duration="500" />
    <item android:drawable="@drawable/loading8" android:duration="500" />

</animation-list>

其中,android:onshot如果为true,表示动画只播放一次停止在最后一帧上,如果设置为false表示动画循环播放。

然后将上述的Drawable作为View的背景并通过Drawable来播放动画即可。

TextView textview = (TextView) findViewById(R.id.textview);
textview.setBackgroundResource(R.drawable.animation_frame);
AnimationDrawable drawable = (AnimationDrawable) textview.getBackground();
drawable.start();

帧动画的使用比较简单,但是如果图片很多而且很大时,容易引起OOM,所以在使用帧动画时应该尽量避免使用过多尺寸较大的图片。

补间动画

补间动画也叫View动画,它的作用对象是View,它只支持平移动画、缩放动画、旋转动画和渐变动画这四种动画效果。这四种变换效果分别对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotationAnimation和AlphaAnimation。和属性动画一样,这四种动画既可以通过代码方式动态创建,也可以通过xml方式来定义,他们在xml中分别对应的标签是<translate><scale><rotate><alpha>。其中渐变动画指的是改变View的透明度。

通过XML定义

首先需要创建动画的XML,这个xml文件存放在res/anim/目录下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true"
    android:fillBefore="false"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatMode="reverse"
    android:shareInterpolator="true"
    android:startOffset="0">

    <!-- 平移动画 -->
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="10"
        android:toYDelta="10" />

    <!-- 缩放动画 -->
    <scale
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="2.5"
        android:toYScale="2.5" />

    <!-- 旋转动画 -->
    <rotate
        android:fromDegrees="0"
        android:toDegrees="360" />

    <!-- 渐变动画 -->
    <alpha
        android:fromAlpha="1"
        android:toAlpha="0.5" />

</set>

然后在代码中应用上面的动画:

final Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);

findViewById(R.id.textview).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        v.startAnimation(animation);
    }
});

效果如图:

补间动画
补间动画

上面的xml中是一系列动画组合在一起的,但每种动画标签都可以单独使用。<set>标签表示动画集合,对应AnimationSet类,它可以包含若干个子动画,并且在它内部还可以嵌套其他的动画集合。

千万不要把这里的AnimationSet和属性动画中的动画集合AnimatorSet搞混,虽然都是动画集合,但AnimationSet是android.view.animation包下的类,而AnimatorSet是android.animation包下的类。前者只对View起作用。

再来看看xml中各个标签以及属性的含义:

  • android:interpolator表示动画集合采用的插值器,不指定的话默认就是accelerate_decelerate_interpolator加减速插值器。

  • android:shareInterpolator表示集合中的动画是否和集合共享同一个插值器,如果集合不指定插值器,那么子动画就需要单独指定所需要的插值器或者使用默认值。

  • android:duration表示动画执行的时间

  • android:fillAfter表示动画结束以后View是否停留在结束位置,true表示停留在结束位置,false则表示回到View原始的位置

  • android:startOffset表示动画延迟执行的时间

  • android:zAdjustment允许在动画播放期间,调整播放内容在Z轴方向的顺序:

    • normal(0):正在播放的动画内容保持当前的Z轴顺序

    • top(1):在动画播放期间,强制把当前播放的内容放到其他内容的上面

    • bottom(-1):在动画播放期间,强制把当前播放的内容放到其他内容之下

<translate>标签表示平移动画,它的作用效果是使View在水平和竖直方向上移动,它的下列属性含义:

  • android:fromXDelta 水平方向的起始值
  • android:fromYDelta 竖直方向的起始值
  • android:toXDelta 水平方向的结束值
  • android:toYDelta 竖直方向的结束值

'<scale>'标签表示缩放动画,它的所用效果是使View能够放大缩小,它的下列属性含义:

  • android:fromXScale 水平方向缩放的起始值
  • android:fromYScale 竖直方向缩放的起始值
  • android:toXScale 水平方向缩放的结束值
  • android:toYScale 竖直方向缩放的结束值

另外,如果你想指定缩放的轴点,需要用这两个属性:

  • android:pivotX 缩放轴点的x坐标
  • android:pivotY 缩放轴点的y坐标

不指定轴点时,缩放动画会以View的左上角为轴点进行缩放。

<rotate>标签表示旋转动画,它的作用效果是使View进行旋转。它的下列属性的含义:

  • android:fromDegrees 旋转开始的角度
  • android:toDegrees 旋转结束的角度

和scale动画一样,旋转动画也可以用android:pivotX和android:pivotY来指定旋转的轴点,默认也是View的左上角。

<alpha>标签表示渐变动画,它的作用效果是改变View的透明度,其下列属性含义:

  • android:fromAlpha 起始透明度值
  • android:toAlpha 结束透明度值

透明度的值从0到1之间。

在代码中创建补间动画

final TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 200);
translateAnimation.setDuration(2000);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);

findViewById(R.id.textview).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        v.startAnimation(translateAnimation);
    }
});

但是这种方式好像有个缺陷,比如我有多个动画想要同时执行,而View的startAnimation方法只能开始一个动画。比如下面这样写:

final TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 200);
translateAnimation.setDuration(2000);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);

final ScaleAnimation scaleAnimation = new ScaleAnimation(1, 1.5f, 1, 2);
scaleAnimation.setDuration(2000);

findViewById(R.id.textview).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        v.startAnimation(translateAnimation);
        v.startAnimation(scaleAnimation);
    }
});

View就只会执行后面的缩放动画,如果要多个动画同时执行,就只能用xml的方式定义,然后AnimationUtils.loadAnimation加载出来了。这里如果有错,希望大家指正。

监听动画的执行

TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 200);
        
Animation.AnimationListener animationListener = new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {}

    @Override
    public void onAnimationEnd(Animation animation) {}

    @Override
    public void onAnimationRepeat(Animation animation) {}
};
translateAnimation.setAnimationListener(animationListener);

针对ViewGroup的动画

帧动画和补间动画都是针对View的动画,而LayoutAnimationLayoutAnimationController则是针对ViewGroup的做动画的两个类。LayoutAnimation为ViewGroup指定一个动画,这样当它的子View出场时都会具有这种动画效果,比较常见的有ListView和GridView还有RecyclerView的item的出场效果。

在xml中定义LayoutAnimation:

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/animation_item"
    android:animationOrder="normal"
    android:delay="0.5">

</layoutAnimation>

android:animation表示作用在子View上的动画效果,它里面的文件animation_item也是定义在anim目录下的:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:shareInterpolator="true">

    <translate
        android:fromXDelta="-500"
        android:toXDelta="0" />

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />

</set>

android:animationOrder表示子View播放动画的顺序:normal[顺序出场] | random[随机出场] | reverse[逆向出场]

android:delay表示子View开始动画的延迟时间,注意这里的时间单位既不是秒也不是毫秒,比如我们这里子View动画的duration是500毫秒,那么这个dalay0.5表示每个子元素都需要延迟250毫秒才开始做动画,第一个item延迟250毫秒,第二延迟500,第三个750...

android:interpolator表示所有子View执行动画的插值器

在xml中为ViewGroup指定andorid:layoutAnimation属性:以RecyclerView为例

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutAnimation="@anim/layout_animation">

</android.support.v7.widget.RecyclerView>
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
List<String> titles = new ArrayList<>();
for (int i = 0; i < 50; i++) {
    titles.add("LayoutAnimation的运用" + i);
}
mRecyclerView.setAdapter(new RecyclerViewAdapter(this, titles));

适配器代码:

package com.shenhuniurou.tweenanimation;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

/**
 * Created by shenhuniurou on 2017/4/16.
 */

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TextViewHolder> {

    private final LayoutInflater mLayoutInflater;
    private final Context mContext;
    private List<String> mTitles;
    private OnItemClickListener listener;

    interface OnItemClickListener {
        public void OnItemClick(int position);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    public RecyclerViewAdapter(Context context, List<String> titles) {
        mTitles = titles;
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
    }

    @Override
    public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new TextViewHolder(mLayoutInflater.inflate(R.layout.item_text, parent, false));
    }

    @Override
    public void onBindViewHolder(TextViewHolder holder, int position) {
        holder.mTextView.setText(mTitles.get(position));
    }

    @Override
    public int getItemCount() {
        return mTitles == null ? 0 : mTitles.size();
    }

    public class TextViewHolder extends RecyclerView.ViewHolder {

        TextView mTextView;

        TextViewHolder(View view) {
            super(view);
            mTextView = (TextView) view.findViewById(R.id.textview);
        }
    }

}

最后运行的效果如图:

LayoutAnimation
LayoutAnimation

除了在XML中指定LayoutAnimation外,还可以在代码中通过LayoutAnimationController类来实现:

RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);

Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mRecyclerView.setLayoutAnimation(controller);

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
List<String> titles = new ArrayList<>();
for (int i = 0; i < 50; i++) {
    titles.add("LayoutAnimation的运用" + i);
}
mRecyclerView.setAdapter(new RecyclerViewAdapter(this, titles));

LayoutAnimationController适用于ListView,对于GridView就必须适用GridLayoutAnimationController,如果是RecyclerView,当它的LayoutManager是LinearLayourManager时就可以使用LayoutAnimationController,但如果设置成GridLayoutManager,就不能使用LayoutAnimationController了,否则会出现类转换异常。

转场动画

补间动画还可以作为转场动画使用,即Activity的切换动画效果。Android系统本身有默认的切换动画,但是我们可以自定义。

主要用到的方法是overridePendingTransition(int enterAnim, int exitAnim);这个方法必须在调用startActivity或者finish后被调用才能生效。enterAnim是Activity被打开时调用的动画资源,exitAnim是Activity退出时的动画资源。系统内自带的一些可以用android.R.anim.fade_in这种形式调用。下面是我自定义的一个转场动画资源:

enter_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="100%p"
        android:toXDelta="0" />
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />

    <scale
        android:fromXScale="0.8"
        android:fromYScale="0.8"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1" />
</set>

exit_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:toXDelta="-100%p" />
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />

    <scale
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotY="50%"
        android:toXScale="0.5"
        android:toYScale="0.5" />
</set>

效果如图:

转场动画
转场动画

只要掌握了补间动画的这些知识,完全可以根据自己的需求的审美自定义出符合你要求的转场动画。

转场动画除了可以调用overridePendingTransition这个方法来实现,还可以通过配置style来实现:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>

    <item name="android:windowAnimationStyle">@style/activityAnimation</item>

</style>

<style name="activityAnimation" parent="@android:style/Animation.Activity">
    <item name="android:activityOpenEnterAnimation"></item>
    <item name="android:activityOpenExitAnimation"></item>
    <item name="android:activityCloseEnterAnimation"></item>
    <item name="android:activityCloseExitAnimation"></item>
</style>

就可以实现和上图一样的切换效果了。

开发过程中遇到一个奇怪的问题,当View进行缩放动画后,有效点击的部分还是View原来的位置,而在View上其他的部位会出现点击无效的情况。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,651评论 0 10
  • 转载一篇高质量博文,原地址请戳这里转载下来方便今后查看。1 背景不能只分析源码呀,分析的同时也要整理归纳基础知识,...
    Elder阅读 1,932评论 0 24
  • 2016年10月22日 晴 之前身边朋友自杀,刹那间整个人都不好了,心里无法接受,到现在还是无法接受,害怕,突...
    栁圊薇楊阅读 217评论 0 0
  • ​文/白小起 我觉得男女平等吧,首先,第一,你就得承认你是男的我是女的,我们就是不同的东西。就好像我是猫你是狗,我...
    一个可爱的搬运工阅读 356评论 0 4