Android入门之路 - Frame帧动画

学无止境,年轻人照顾好身体 ~

在Android3.0之前的动画方面,我们主要使用的为视图动画,而视图动画有俩种表现方式:

动画效果

帧动画的实现和flash动画相同,均有一张张图片快速组合而成
在这里插入图片描述

Android动画一般都有俩种实现方式,帧动画也不外如是

  • 静态方式 - xml
  • 动态方式 - 代码动态设置

Look here:Frame动画主要基于AnimationDrawable类,通过第一层源码发现其内部的扩展方法其实并不多,仅仅400多行,所以功能有限咯 ~

静态方式

特点:动画效果相对固定,全局皆可引用

  • 首先将帧动画所需要的分解图放置于res - drawable文件夹内,之后在drawable下新建帧动画的xml

frame_animation.xml
xml内的大致格式务必相同,如顶部xml的声明,还有animation-list与item格式

<?xml version="1.0" encoding="utf-8"?>
<!--oneshot 循环状态:如未设置默认为false状态
true:单次循环
false:无限循环
-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

   <!--
    drawable:对应帧的图片
    duration:持续时间
    -->
    <item
        android:drawable="@drawable/people_1"
        android:duration="100" />
    <item
        android:drawable="@drawable/people_2"
        android:duration="200" />
    <item
        android:drawable="@drawable/people_3"
        android:duration="300" />

</animation-list>
  • 设置方式

将对应Layout内ImageView的src属性设置为之前我们写好的 animation.xml,如下 ~

    <ImageView
        android:layout_marginTop="20dp"
        android:id="@+id/iv_effect1"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:src="@drawable/frame_animation"/>
  • 使用方式

MainActivity

package nk.com.frameanimation;

import android.graphics.drawable.AnimationDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private AnimationDrawable mAnimation1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView mStart = findViewById(R.id.btn_start);
        TextView mStop = findViewById(R.id.btn_stop);

        ImageView mAnimationImg1 = findViewById(R.id.iv_effect1);
        mAnimation1 = (AnimationDrawable) mAnimationImg1.getDrawable();
        
        /**
         * 播放动画
         * */
        mStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mAnimation1.isRunning()) {
                    mAnimation1.start();
                }
            }
        });

        /**
         * 停止动画
         * */
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAnimation1.stop();
            }
        });
    }
}

Preview
在这里插入图片描述
activity_main

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/ll_parent"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="开始动画" />

        <TextView
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止动画" />
    </LinearLayout>

    <ImageView
        android:layout_marginTop="20dp"
        android:id="@+id/iv_effect1"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:src="@drawable/frame_animation"
        app:layout_constraintTop_toBottomOf="@+id/ll_parent" />
        
</android.support.constraint.ConstraintLayout>

动态方式

特点:动画效果相对活动,同时一般该动画仅作用于当前类~

将帧动画所需图片放置于res - drawable/mipmap文件夹之下,动画创建、动画设置等操作均在代码中动态进行设置 ~

MainActivity

package nk.com.frameanimation;

import android.graphics.drawable.AnimationDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private AnimationDrawable mAnimation1;
    private AnimationDrawable mAnimation2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView mStart = findViewById(R.id.btn_start);
        TextView mStop = findViewById(R.id.btn_stop);

        /**
         * 动态设置帧动画
         * */
        ImageView mAnimationImg2 = findViewById(R.id.iv_effect2);
        mAnimation2 = new AnimationDrawable();
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_0), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_1), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_2), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_3), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_4), 200);
        //设置为循环播放,代码设置默认为true  true:仅播放一次  false:无限循环
        mAnimation2.setOneShot(false);
        // 设置ImageView的背景为AnimationDrawable
        mAnimationImg2.setBackgroundDrawable(mAnimation2);

        /**
         * 播放动画
         * */
        mStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mAnimation2.isRunning()) {
                    mAnimation2.start();
                }
            }
        });

        /**
         * 停止动画
         * */
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAnimation2.stop();
            }
        });
    }
}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/ll_parent"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="开始动画" />

        <TextView
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止动画" />
    </LinearLayout>

    <ImageView
        android:layout_marginTop="20dp"
        android:id="@+id/iv_effect2"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:src="@drawable/frame_animation"
        app:layout_constraintTop_toBottomOf="@+id/ll_parent" />

</android.support.constraint.ConstraintLayout>

好奇百科

在开发中遇到的一些好奇点,还有一些问题点,这是一个求知、求解的过程 ~

小优化

主要记录播放动画时的一个isRunning的小判断,如下:

        /**
         * 播放动画
         * */
        mStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            	//判断当前动画是否处于播放状态,非播放状态才可开始播放动画
                if (!mAnimation1.isRunning()) {
                    mAnimation1.start();
                }
            }
        });
        
        /**
         * 停止动画
         * */
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAnimation1.stop();
            }
        });

至于停止动画为何没有这个判断,其实通过源码可以发现不是没有此类判断,而是其内部自行封装了此判断而已 ~
在这里插入图片描述

帧动画分解图为何可以分别放在drawable、mipmap文件目录下,有何区别

根据查询反馈在Eclipse中根据图片的像素大小把,png或.jpg的图片放在drawable文件夹下,如drawable-hdpi,drawable-ldpi,drawable-mdpi,drawable-xhdpi,drawable-xxhdpi.同一张图片放在不同的drawable文件夹下,显示的时候会有所不同,但是在xml中引用的时候是一样的~

Android Studio则不一样,没有这么多的drawable目录了,而只有一个drawable文件夹。只需要把图片放里面就好。尝试过新建drawable-hdpi,drawable-ldpi文件夹,是不能被ide显示的,应该是不支持了吧。

Google 2015的相关资料

drawable/
For bitmap files (PNG, JPEG, or GIF), 9-Patch image files, and XML files that
describe Drawable shapes or Drawable objects that contain multiple states
(normal, pressed, or focused). See the Drawable resource type.

mipmap/
For app launcher icons. The Android system retains the resources in this folder
(and density-specific folders such as mipmap-xxxhdpi) regardless of the screen
resolution of the device where your app is installed. This behavior allows launcher
apps to pick the best resolution icon for your app to display on the home screen.
For more information about using the mipmap folders, see Managing Launcher Icons
as mipmap Resources.

个人理解

随着Android发展越来越完善,其内部功能也相对慢慢的扩展开来,我觉得miamap的诞生本质就是去帮助drawable分担压力的,毕竟drawable承载内容太多,如动画xml、shape xml、各分辨率图片等等,而mipmap功能单一没有耦合,主要负责图片相关的适配功能,所以我还是主要比较图片存储于mipmap的 ~

AndroidStudio开发者注意:帧动画的分解图可以存于drawable也可以存于mipmap,但是如果采用As进行静态xml方式去实现帧动画效果的话,在其xml内部item调用mipmap图片的话不会自动提示,需要自己写好路径,这样As依旧可以识别 ~

首次动画未加载完全,仅显示第一帧

仅解决用户onCreate生命周期内首次加载某个界面时未加载完动画的场景,如开发中是某些特定的事件场景调用的话,并不会涉及这个问题!

方式一:

        // 为了防止在onCreate方法中只显示第一帧的解决方案之一
        imgView.post(new Runnable() {
            @Override
            public void run() {
                animationDrawable.start();
            }
        });

方式二:

   @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        if (!animationDrawable.isRunning()) {
            animationDrawable.start();
        }
        super.onWindowFocusChanged(hasFocus);
    }

完整案例

此案例的最终实现效果就是您最上方所看的实践效果,完整Demo已上传此处,随时可供下载 ~

MainActivity

package nk.com.frameanimation;

import android.graphics.drawable.AnimationDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private AnimationDrawable mAnimation1;
    private AnimationDrawable mAnimation2;
    private AnimationDrawable mAnimation3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView mStart = findViewById(R.id.btn_start);
        TextView mStop = findViewById(R.id.btn_stop);

        ImageView mAnimationImg1 = findViewById(R.id.iv_effect1);
        mAnimation1 = (AnimationDrawable) mAnimationImg1.getDrawable();

        // 为了防止在onCreate方法中只显示第一帧的解决方案之一
      /*  mAnimationImg1.post(new Runnable() {
            @Override
            public void run() {
                mAnimation1.start();
            }
        });
      */
      
        /**
         * 动态设置帧动画 - wifi
         * */
        ImageView mAnimationImg2 = findViewById(R.id.iv_effect2);
        mAnimation2 = new AnimationDrawable();
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_0), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_1), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_2), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_3), 200);
        mAnimation2.addFrame(getResources().getDrawable(R.mipmap.ic_wifi_4), 200);
        //设置为循环播放,代码设置默认为true  true:仅播放一次  false:无限循环
//        mAnimation2.setOneShot(true);
        // 设置ImageView的背景为AnimationDrawable
        mAnimationImg2.setBackgroundDrawable(mAnimation2);

        /**
         * 动态设置帧动画 - 美团加载动画
         * */
        ImageView mAnimationImg3 = findViewById(R.id.iv_effect3);
        mAnimation3 = new AnimationDrawable();
        mAnimation3.addFrame(getResources().getDrawable(R.mipmap.meituan_1), 100);
        mAnimation3.addFrame(getResources().getDrawable(R.mipmap.meituan_2), 100);
        mAnimation3.addFrame(getResources().getDrawable(R.mipmap.meituan_3), 100);
        mAnimation3.addFrame(getResources().getDrawable(R.mipmap.meituan_4), 100);
        //设置为循环播放,代码设置默认为true  true:仅播放一次  false:无限循环
        mAnimation3.setOneShot(false);
        // 设置ImageView的背景为AnimationDrawable
        mAnimationImg3.setBackgroundDrawable(mAnimation3);

        /**
         * 播放动画
         * */
        mStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mAnimation1.isRunning()) {
                    mAnimation1.start();
                }
                if (!mAnimation2.isRunning()) {
                    mAnimation2.start();
                }
                if (!mAnimation3.isRunning()) {
                    mAnimation3.start();
                }
            }
        });

        /**
         * 停止动画
         * */
        mStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAnimation1.stop();
                mAnimation2.stop();
                mAnimation3.stop();
            }
        });
    }

    /*    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        if (!mAnimation1.isRunning()) {
            mAnimation1.start();
            mAnimation2.start();
        }
        super.onWindowFocusChanged(hasFocus);
    }*/
}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/ll_parent"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="开始动画" />

        <TextView
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:text="停止动画" />
    </LinearLayout>

    <ImageView
        android:layout_marginTop="20dp"
        android:id="@+id/iv_effect1"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:src="@drawable/frame_animation"
        app:layout_constraintTop_toBottomOf="@+id/ll_parent" />

    <View
        android:layout_marginTop="25dp"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000000"
        app:layout_constraintTop_toBottomOf="@+id/iv_effect1" />

    <ImageView
        android:id="@+id/iv_effect2"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="50dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_effect1" />

    <View
        android:layout_marginTop="25dp"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000000"
        app:layout_constraintTop_toBottomOf="@+id/iv_effect2" />

    <ImageView
        android:id="@+id/iv_effect3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_effect2" />
</android.support.constraint.ConstraintLayout>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远方那座山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值