安卓案例:利用定时器实现逐帧动画
一、运行效果
单击【开始】按钮,可以看到逐帧动画效果:
单击【停止】按钮,可以停止动画效果。
二、涉及知识点
1、图像视图(ImageView)
2、按钮(Button)
3、定时器(Timer)
4、定时器任务(TimerTask)
5、消息处理器(Handler)
6、数组与循环
三、实现步骤
1、创建安卓应用GameSpecialEffect
2、将逐帧动画素材(23张图片)拷贝到mipmap目录里
3、主布局资源文件acitivity_main.xml
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:gravity="center"
android:orientation="vertical"
tools:context="net.hw.game_special_effect.MainActivity">
android:id="@+id/iv_bomb"
android:layout_width="250dp"
android:layout_height="250dp"
android:src="@mipmap/img1" />
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
android:id="@+id/btn_start"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:background="#cccccc"
android:onClick="doStart"
android:text="@string/start" />
android:id="@+id/btn_stop"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="#cccccc"
android:onClick="doStop"
android:text="@string/stop" />
4、字符串资源文件strings.xml
安卓案例:游戏特效
开始
停止
5、主界面类MainActivity
package net.hw.game_special_effect;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by howard on 3/9/2018.
*/
public class MainActivity extends Activity {
/**
* 定时器
*/
private Timer timer;
/**
* 定时器任务
*/
private TimerTask task;
/**
* 消息处理器
*/
private Handler handler;
/**
* 图像视图
*/
private ImageView ivBomb;
/**
* 图像资源标识数组
*/
private int[] imgIds;
/**
* 图像索引
*/
private int imgIndex;
/**
* 图像总数
*/
private final int IMG_COUNT = 23;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 利用布局资源文件设置用户界面
setContentView(R.layout.activity_main);
// 通过资源标识获得控件实例
ivBomb = findViewById(R.id.iv_bomb);
// 初始化图像标识数组
imgIds = new int[IMG_COUNT];
for (int i = 0; i < IMG_COUNT; i++) {
/**
* 参数1:资源文件名
* 参数2:资源文件类型
* 参数3:包名
*/
imgIds[i] = getResources().getIdentifier("img" + (i + 1), "mipmap", "net.hw.game_special_effect");
}
// 创建消息处理器,处理从定时器任务里发送过来的消息
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
/*
消息内容msg.what存放的是当前图像索引
通过它从图像标识数组里获取当前图像的资源标识
然后通过此资源标识设置图像控件要显示的图像
*/
ivBomb.setImageResource(imgIds[msg.what]);
}
};
}
/**
* 开始按钮单击事件处理方法
*
* @param view
*/
public void doStart(View view) {
// 实例化定时器
timer = new Timer();
// 实例化定时器任务
task = new TimerTask() {
@Override
public void run() {
// 更新图像索引
imgIndex++;
imgIndex = imgIndex % IMG_COUNT;
// 将更新后的图像索引作为消息内容发送给主线程
handler.sendEmptyMessage(imgIndex);
}
};
// 对定时器任务进行调度
/**
* 参数1:定时器任务
* 参数2:首次执行延迟时间
* 参数3:定时执行的周期(毫秒)
*/
timer.schedule(task, 0, 100);
}
/**
* 停止按钮单击事件处理方法
*
* @param view
*/
public void doStop(View view) {
// 取消定时器任务
timer.cancel();
}
}
运行程序,测试运行效果:
思考题:能够将定时器任务里的三行代码精简成一行代码?
但是这个代码有点小问题,程序运行时间足够长,会导致imgIndex溢出。
继续修改代码如下:
四、小结
利用定时器(Timer对象)开辟子线程,定时执行任务(TimerTask对象),在任务里利用消息器的sendEmptyMessage方法或SendMessage方法发送消息,而在主线程创建消息处理器(Handler对象),在它的handleMessage(Message msg)方法里接收由子线程发送的消息,更新主线程的UI,从而实现动画效果。
其实,Timer与Handler结合实现动画,跟Thread与Handler结合实现动画,其实处理思路都是一样的。
五、课堂练习