前言
有些时候我们需要这样一个需求,比如屏保程序当规定时间不做任何操作就显示一张图片,又或者是安卓TV的菜单程序当用户在规定时间不操作的时候菜单自动关闭,还有很多其他的例子。
有两种实现方式一种是创建一个线程来计时另一种是使用Handler,而我会教大家使用Handler来实现,两种方式都差不多。都是实现计时,如果中途用户有操作就重新计时,等到时间到就执行某段程序。
下面先贴一份整体代码,然后我再讲解里面一些方法的用途
整体代码
MainActivity.java
package com.my.screensaverdemo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
public class MainActivity extends Activity implements Screensaver.OnTimeOutListener {
private Screensaver mScreensaver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScreensaver = new Screensaver(5000); //定时5秒
mScreensaver.setOnTimeOutListener(this); //监听
mScreensaver.start(); //开始计时
}
/**
* 当触摸就会执行此方法
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mScreensaver.resetTime(); //重置时间
return super.dispatchTouchEvent(ev);
}
/**
* 当使用键盘就会执行此方法
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
mScreensaver.resetTime(); //重置时间
return super.dispatchKeyEvent(event);
}
/**
* 时间到就会执行此方法
*/
@Override
public void onTimeOut(Screensaver screensaver) {
Log.d("MainActivity", "时间到了");
}
@Override
protected void onDestroy() {
super.onDestroy();
mScreensaver.stop(); //停止计时
}
}
Screensaver.java
package com.my.screensaverdemo;
import android.os.Handler;
import android.os.Message;
/**
* 计时程序
*/
public class Screensaver {
private static final int MSG_TIMEOUT = 0x0;
private static final int SECOND = 1000;
private static final int MINUTE = SECOND * 60;
private static final int DEFAULT_TIMEOUT = MINUTE * 3; //默认为3分钟
private OnTimeOutListener mOnTimeOutListener;
private int mScreensaverTiemout = DEFAULT_TIMEOUT;//默认为3分钟
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == MSG_TIMEOUT){
//调用回调通知接收者
if(mOnTimeOutListener != null){
mOnTimeOutListener.onTimeOut(Screensaver.this);
}
start(); //重新开始计时
}
}
};
public Screensaver(int screensaverTiemout) {
mScreensaverTiemout = screensaverTiemout;
}
public Screensaver() {
}
/**
* 开始计时
*/
public void start() {
Message message = mHandler.obtainMessage(MSG_TIMEOUT); //包装消息
mHandler.sendMessageDelayed(message, mScreensaverTiemout); //延时发送消息
}
/**
* 停止计时
*/
public void stop() {
mHandler.removeMessages(MSG_TIMEOUT); //移除消息
}
/**
* 重置时间
*/
public void resetTime() {
stop();
start();
}
public void setScreensaverTiemout(int mScreensaverTiemout) {
this.mScreensaverTiemout = mScreensaverTiemout;
}
public int getScreensaverTiemout() {
return mScreensaverTiemout;
}
public void setOnTimeOutListener(OnTimeOutListener onTimeOutListener) {
this.mOnTimeOutListener = onTimeOutListener;
}
/**
* 屏幕保护时间到监听
*/
public interface OnTimeOutListener {
void onTimeOut(Screensaver screensaver);
}
}
实现原理
我们先来看一下MainActivity
mScreensaver = new Screensaver(5000); //定时5秒
mScreensaver.setOnTimeOutListener(this); //监听
mScreensaver.start(); //开始计时
第一行代码是创建一个计时器对象然后设置定时时间为5秒
第二行代码是设置监听,就是计时时间到的时候回调执行public void onTimeOut(Screensaver screensaver)方法
第三行就是开始计时。
/**
* 时间到就会执行此方法
*/
@Override
public void onTimeOut(Screensaver screensaver) {
Log.d("MainActivity", "时间到了");
}
这段代码就是实现的回调方法,计时时间到的时候会执行,我们可以在这个方法里面执行相应的操作。
/**
* 当触摸就会执行此方法
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mScreensaver.resetTime(); //重置时间
return super.dispatchTouchEvent(ev);
}
/**
* 当使用键盘就会执行此方法
*/
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
mScreensaver.resetTime(); //重置时间
return super.dispatchKeyEvent(event);
}
这段代码是为了当用户执行键盘操作或者触摸操作就重新计时,如果读者不清楚dispatchTouchEvent和dispatchKeyEvent用来做什么的话可以查阅安卓的事件机制。
@Override
protected void onDestroy() {
super.onDestroy();
mScreensaver.stop(); //停止计时
}
最后是当应用退出的时候停止计时
下面我们来看一下Screensaver.java
/**
* 开始计时
*/
public void start() {
Message message = mHandler.obtainMessage(MSG_TIMEOUT); //包装消息
mHandler.sendMessageDelayed(message, mScreensaverTiemout); //延时mScreensaverTiemout毫秒发送消息
}
注释已经解释的很清楚,包装消息然后让它延时发送,当然mHandler是可以中途停止发送消息的。mHandler只是发送一次消息,为了能循环发送消息,所以后面接收消息的程序会再次执行这个方法。
/**
* 停止计时
*/
public void stop() {
mHandler.removeMessages(MSG_TIMEOUT); //移除消息
}
这个方法就是用于停止发送消息
/**
* 重置时间
*/
public void resetTime() {
stop();
start();
}
重置时间就是先让mHandler停止延时发送消息,然后再次启动重新计时。
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == MSG_TIMEOUT){
//调用回调通知接收者
if(mOnTimeOutListener != null){
mOnTimeOutListener.onTimeOut(Screensaver.this);
}
start(); //重新开始计时
}
}
};
这里使用了匿名内部类,是接收消息端,当接收到消息就会执行此方法,里面是先判断是否是MSG_TIMEOUT消息,然后执行回调方法通知实现了接口的MainActivity(对应的是onTimeOut方法),为了能循环计时,后面就加了一段start()。
public void setOnTimeOutListener(OnTimeOutListener onTimeOutListener) {
this.mOnTimeOutListener = onTimeOutListener;
}
/**
* 屏幕保护时间到监听
*/
public interface OnTimeOutListener {
void onTimeOut(Screensaver screensaver);
}
这个就是回调接口了,当计时时间到就会通知实现了接口的对象,MainActivity已经实现了这个方法
mScreensaver.setOnTimeOutListener(this); //监听
/**
* 时间到就会执行此方法
*/
@Override
public void onTimeOut(Screensaver screensaver) {
Log.d("MainActivity", "时间到了");
}
原理就是这样子并没有太多的难点,不过有个问题没有解决,就是鼠标移动操作不能重置时间,当然如果是做手机应用可以完全忽略这点,作者是做投影仪的,有些时候需要使用鼠标,所以对这个比较关心。