在应用中为了能达到更好的用户交互体验,很多应用都采用了所谓的“摇一摇”来实现一些如随机速配、自动遴选等功能。在Android应用开发中为了实现这一功能需要运用到传感器相关API。下面通过一个Demo我们一起来看看如何实现“摇一摇”功能。
首先我们需要定义一个类ShakeListener ,这个类实现了SensorEventListener接口,我们在这个类中定义一个接口OnShakeListener用于通知界面开始摇动和停止摇动,在界面需要监听摇动事件时首先需要通过得到ShakeListener 的一个实例,并在onResume()中需要调用ShakeListener类的register()方法注册监听,在onPause()、onStop()、onDestroy()中需要调用ShakeListener类的unregister()方法方法来注销监听,这么做的目的是为了在黑屏或者按下Home键将应用置于后台运行后不再监听摇动同时保证回到界面后可以继续监听摇动。
package cn.Longmaster.iPeng.Listener;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
/**
* 手机摇动监听器
*
* @author Jone
*/
public class ShakeListener implements SensorEventListener
{
/** 速度阈值,当摇晃速度达到这值后产生作用 */
private static final int SPEED_SHRESHOLD = 3000;
/** 两次检测的时间间隔 */
private static final int UPTATE_INTERVAL_TIME = 70;
/** 最大间隔时间,超过该时间未再次摇动认为已停止摇动 */
private static final int MAX_INTERVAL_TIME = 500;
/** 摇动监听器 */
private OnShakeListener mOnShakeListener;
/** 传感器管理类 */
private SensorManager mSensorManager;
/** 传感器 */
private Sensor mSensor;
private Handler mHandler;
// 手机上一个位置时重力感应坐标
private float mLastX;
private float mLastY;
private float mLastZ;
// 上次检测时间
private long mLastUpdateTime;
public ShakeListener(Context context)
{
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
// 获得加速度传感器
if (mSensorManager != null)
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mHandler = new Handler();
}
/**
* 注册监听,本方法应该在Activity的onResume()中调用
*/
public void register()
{
if (mSensor != null)
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
/**
* 注销监听器,必须在Activity的onPause()、onStop()、onDestroy()中调用本方法,防止内存溢出同时防止在黑屏或者按下Home键后任可回调摇动
*/
public void unregister()
{
mSensorManager.unregisterListener(this);
}
/**
* 设置摇动回调监听
*
* @param onShakeListener
* 摇动回调监听
*/
public void setOnShakeListener(OnShakeListener onShakeListener)
{
mOnShakeListener = onShakeListener;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
// do nothing
}
@Override
public void onSensorChanged(SensorEvent event)
{
// 现在检测时间
long currentUpdateTime = System.currentTimeMillis();
// 两次检测的时间间隔
long timeInterval = currentUpdateTime - mLastUpdateTime;
// 判断是否达到了检测时间间隔
if (timeInterval < UPTATE_INTERVAL_TIME)
return;
// 现在的时间变成last时间
mLastUpdateTime = currentUpdateTime;
// 获得x,y,z坐标
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
// 获得x,y,z的变化值
float deltaX = x - mLastX;
float deltaY = y - mLastY;
float deltaZ = z - mLastZ;
// 将现在的坐标变成last坐标
mLastX = x;
mLastY = y;
mLastZ = z;
// 返回最近的双近似的平方根
double speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) / timeInterval * 10000;
// 达到速度阀值,发出提示
if (speed >= SPEED_SHRESHOLD)
{
mOnShakeListener.onShakeStart();
mHandler.removeCallbacks(runnable);
mHandler.postDelayed(runnable, MAX_INTERVAL_TIME);
}
}
Runnable runnable = new Runnable()
{
@Override
public void run()
{
mOnShakeListener.onShakeFinish();
}
};
/**
* 摇动监听接口
*
* @author Jone
*/
public interface OnShakeListener
{
/**
* 开始新的一次摇动
*/
public abstract void onShakeStart();
/**
* 停止结束
*/
public abstract void onShakeFinish();
}
}
下面我们来写一个简单的Activity用于测试,界面布局很简单,只有一个文本区域用来显示摇动的次数:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/activity_shake_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="18sp"
android:textColor="#ff0000"/>
</LinearLayout>
接下来编写Activity类中的代码,其中R.raw.shake是一个文件类型为ogg的音效:
package com.researchdemo.tools;
import android.app.Activity;
import android.app.Service;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.os.Vibrator;
import android.widget.TextView;
import com.researchdemo.R;
import com.researchdemo.tools.ShakeListener.OnShakeListener;
/**
* 摇一摇界面
*
* @author Jone
*/
public class ShakeUI extends Activity
{
/** 摇动回调监听器 */
private ShakeListener mShakeListener;
/** 信息显示文本控件 */
private TextView mInfo;
/** 摇动次数 */
private int mCount;
/** 用于播放音效 */
private SoundPool mSoundPool;
/** 音效资源 */
private int mSoundSource;
/** 震动 */
private Vibrator mVibrator;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shake);
initView();
initData();
}
private void initView()
{
mInfo = (TextView) findViewById(R.id.activity_shake_info);
}
private void initData()
{
mShakeListener = new ShakeListener(this);
mSoundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 100);
mSoundSource = mSoundPool.load(this, R.raw.shake, 100);
mVibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
}
@Override
protected void onResume()
{
mShakeListener.setOnShakeListener(onShakeListener);
mShakeListener.register();
super.onResume();
}
@Override
protected void onPause()
{
mShakeListener.unregister();
super.onPause();
}
@Override
protected void onStop()
{
mShakeListener.unregister();
super.onStop();
}
@Override
protected void onDestroy()
{
mShakeListener.unregister();
mSoundPool.release();
super.onDestroy();
}
OnShakeListener onShakeListener = new OnShakeListener()
{
@Override
public void onShakeStart()
{
mCount++;
mInfo.setText("摇动" + mCount + "次!!");
mVibrator.vibrate(500);
mSoundPool.play(mSoundSource, 1, 1, 0, 0, 1);
}
@Override
public void onShakeFinish()
{
mInfo.setText("摇动完成!!");
}
};
}