一、帧动画
1.1 什么是帧动画?
帧动画非常容易理解,其实就是简单的由N张静态图片收集起来,然后我们通过控制依次显示 这些图片,因为人眼"视觉残留"的原因,会让我们造成动画的"错觉",跟放电影的原理一样!
而Android中实现帧动画,一般我们会用到前面讲解到的一个Drawable:AnimationDrawable先编写好Drawable,然后代码中调用start()以及stop()开始或停止播放动画
当然我们也可以在Java代码中创建逐帧动画,创建AnimationDrawable对象,然后调用 addFrame(Drawable frame,int duration)向动画中添加帧,接着调用start()和stop()而已
1.2 帧动画使用与效果
案例一:
运行效果:
帧动画案例一
点击开始按钮让坤坤开始打球
点击停止按钮让坤坤停下来
运行及创建流程:
1.首先把gif的每一帧变成图片导入mipmap目录下,可以用gif单帧提取工具来提取gif的每一帧
2.在drawble下创建ikun_gif动画文件
2.1.创建xml文档
2.2ikun_gif根元素设置为animation_list
2.3编写动画
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
<!--这里的android:oneshot是设置动画是否只是播放一次,true只播放一次,false循环播放!-->
android:oneshot="false"
<item android:drawable="@mipmap/a1" android:duration="80" />
<item android:drawable="@mipmap/a2" android:duration="80" />
<item android:drawable="@mipmap/a3" android:duration="80" />
<item android:drawable="@mipmap/a4" android:duration="80" />
<item android:drawable="@mipmap/a5" android:duration="80" />
<item android:drawable="@mipmap/a6" android:duration="80" />
<item android:drawable="@mipmap/a7" android:duration="80" />
<item android:drawable="@mipmap/a8" android:duration="80" />
<item android:drawable="@mipmap/a9" android:duration="80" />
<item android:drawable="@mipmap/a10" android:duration="80" />
<item android:drawable="@mipmap/a11" android:duration="80" />
<item android:drawable="@mipmap/a12" android:duration="80" />
<item android:drawable="@mipmap/a13" android:duration="80" />
<item android:drawable="@mipmap/a14" android:duration="80" />
<item android:drawable="@mipmap/a15" android:duration="80" />
</animation-list>
2.4编辑activity_main.xml文档:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:padding="20dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_open"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始"
/>
<Button
android:id="@+id/btn_stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止"
/>
<ImageView
android:id="@+id/img_gif"
android:layout_gravity="center"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@drawable/ikun_gif"
/>
</LinearLayout>
2.5编辑MainActivity.java文档
package com.houpu.day33work;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
//设置容器
private Button btn_open;
private Button btn_stop;
private ImageView img_gif;
private AnimationDrawable anim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取页面控件
btn_open=findViewById(R.id.btn_open);
btn_stop=findViewById(R.id.btn_stop);
img_gif=findViewById(R.id.img_gif);
anim= (AnimationDrawable) img_gif.getBackground();
//设置btn_open按钮点击事件
btn_open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用Activity生命周期start启动
anim.start();
}
});
//设置btn_stop按钮点击事件
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用Activity生命周期stop停止
anim.stop();
}
});
}
}
案例二:
使用帧动画在指定地方播放帧动画
运行效果:
帧动画案例二
代码实现
1.创建activity_main2.xml页面布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity2">
</androidx.constraintlayout.widget.ConstraintLayout>
2.实现动画文件
详见帧布局案例一2.3编写动画
3.自定义一个ImageView:FrameView.java,这里通过反射获得当前播放的帧, 然后是否为最后一帧,是的话隐藏控件!
上代码:
package com.houpu.day33work;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
public class FrameView extends androidx.appcompat.widget.AppCompatImageView {
AnimationDrawable anim;
public FrameView(@NonNull Context context) {
super(context);
}
public FrameView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FrameView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setAnim(AnimationDrawable anim) {
this.anim = anim;
}
public void setLocation(int top, int left){
setFrame(left, top, left+200, top+200);
}
@Override
protected void onDraw(Canvas canvas) {
try {
Field field = AnimationDrawable.class.getDeclaredField("mCurFrame");
field.setAccessible(true);
int curframe=field.getInt(anim);
// 执行到最后一帧 隐藏起来
if (curframe == anim.getNumberOfFrames()-1) {
setVisibility(View.VISIBLE);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
super.onDraw(canvas);
}
}
4.处理MainActivity2.java文档,创建一个自定义帧布局 对触摸事件做处理,给控件设置背景等
上代码:
package com.houpu.day33work;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class MainActivity2 extends AppCompatActivity {
private AnimationDrawable anim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 自定义一个帧布局
FrameLayout fly=new FrameLayout(MainActivity2.this);
setContentView(fly);
// 创建自定义控件
FrameView fv=new FrameView(MainActivity2.this);
// 给控件设置背景
fv.setBackgroundResource(R.drawable.ikun_gif);
// 图片不可见
fv.setVisibility(View.INVISIBLE);
// 将背景图转为动画
anim= (AnimationDrawable) fv.getBackground();
// 将动画传递给fv,就是为了动画执行完毕后 图片隐藏起来
fv.setAnim(anim);
// 向布局中添加控件
fly.addView(fv);
fly.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
anim.stop();
float x=event.getX();
float y=event.getY();
fv.setLocation((int) y - 40, (int) x - 20);
fv.setVisibility(View.VISIBLE);
anim.start();
}
return false;
}
});
}
}