我需要添加一个摇功能,将刷新我的Android应用程序。
我所找到的所有文档都涉及实现SensorListener,但Eclipse告诉我它已被弃用,并建议使用SensorEventListener。
有人知道如何创建这个震动控制器吗?
我需要添加一个摇功能,将刷新我的Android应用程序。
我所找到的所有文档都涉及实现SensorListener,但Eclipse告诉我它已被弃用,并建议使用SensorEventListener。
有人知道如何创建这个震动控制器吗?
当前回答
我真的很喜欢彼得dk的回答。我自作主张对他的代码做了一些调整。
文件:ShakeDetector.java
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.FloatMath;
public class ShakeDetector implements SensorEventListener {
// The gForce that is necessary to register as shake. Must be greater than 1G (one earth gravity unit)
private static final float SHAKE_THRESHOLD_GRAVITY = 2.7F;
private static final int SHAKE_SLOP_TIME_MS = 500;
private static final int SHAKE_COUNT_RESET_TIME_MS = 3000;
private OnShakeListener mListener;
private long mShakeTimestamp;
private int mShakeCount;
public void setOnShakeListener(OnShakeListener listener) {
this.mListener = listener;
}
public interface OnShakeListener {
public void onShake(int count);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
@Override
public void onSensorChanged(SensorEvent event) {
if (mListener != null) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float gX = x / SensorManager.GRAVITY_EARTH;
float gY = y / SensorManager.GRAVITY_EARTH;
float gZ = z / SensorManager.GRAVITY_EARTH;
// gForce will be close to 1 when there is no movement.
float gForce = FloatMath.sqrt(gX * gX + gY * gY + gZ * gZ);
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
final long now = System.currentTimeMillis();
// ignore shake events too close to each other (500ms)
if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now ) {
return;
}
// reset the shake count after 3 seconds of no shakes
if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now ) {
mShakeCount = 0;
}
mShakeTimestamp = now;
mShakeCount++;
mListener.onShake(mShakeCount);
}
}
}
}
另外,不要忘记需要向SensorManager注册一个ShakeDetector实例。
// ShakeDetector initialization
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mShakeDetector = new ShakeDetector();
mShakeDetector.setOnShakeListener(new OnShakeListener() {
@Override
public void onShake(int count) {
handleShakeEvent(count);
}
});
mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
其他回答
下面是我的颤抖手势检测代码:
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
/**
* Listener that detects shake gesture.
*/
public class ShakeEventListener implements SensorEventListener {
/** Minimum movement force to consider. */
private static final int MIN_FORCE = 10;
/**
* Minimum times in a shake gesture that the direction of movement needs to
* change.
*/
private static final int MIN_DIRECTION_CHANGE = 3;
/** Maximum pause between movements. */
private static final int MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE = 200;
/** Maximum allowed time for shake gesture. */
private static final int MAX_TOTAL_DURATION_OF_SHAKE = 400;
/** Time when the gesture started. */
private long mFirstDirectionChangeTime = 0;
/** Time when the last movement started. */
private long mLastDirectionChangeTime;
/** How many movements are considered so far. */
private int mDirectionChangeCount = 0;
/** The last x position. */
private float lastX = 0;
/** The last y position. */
private float lastY = 0;
/** The last z position. */
private float lastZ = 0;
/** OnShakeListener that is called when shake is detected. */
private OnShakeListener mShakeListener;
/**
* Interface for shake gesture.
*/
public interface OnShakeListener {
/**
* Called when shake gesture is detected.
*/
void onShake();
}
public void setOnShakeListener(OnShakeListener listener) {
mShakeListener = listener;
}
@Override
public void onSensorChanged(SensorEvent se) {
// get sensor data
float x = se.values[SensorManager.DATA_X];
float y = se.values[SensorManager.DATA_Y];
float z = se.values[SensorManager.DATA_Z];
// calculate movement
float totalMovement = Math.abs(x + y + z - lastX - lastY - lastZ);
if (totalMovement > MIN_FORCE) {
// get time
long now = System.currentTimeMillis();
// store first movement time
if (mFirstDirectionChangeTime == 0) {
mFirstDirectionChangeTime = now;
mLastDirectionChangeTime = now;
}
// check if the last movement was not long ago
long lastChangeWasAgo = now - mLastDirectionChangeTime;
if (lastChangeWasAgo < MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE) {
// store movement data
mLastDirectionChangeTime = now;
mDirectionChangeCount++;
// store last sensor data
lastX = x;
lastY = y;
lastZ = z;
// check how many movements are so far
if (mDirectionChangeCount >= MIN_DIRECTION_CHANGE) {
// check total duration
long totalDuration = now - mFirstDirectionChangeTime;
if (totalDuration < MAX_TOTAL_DURATION_OF_SHAKE) {
mShakeListener.onShake();
resetShakeParameters();
}
}
} else {
resetShakeParameters();
}
}
}
/**
* Resets the shake parameters to their default values.
*/
private void resetShakeParameters() {
mFirstDirectionChangeTime = 0;
mDirectionChangeCount = 0;
mLastDirectionChangeTime = 0;
lastX = 0;
lastY = 0;
lastZ = 0;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
把这个添加到你的活动中:
private SensorManager mSensorManager;
private ShakeEventListener mSensorListener;
...
在onCreate()中添加:
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorListener = new ShakeEventListener();
mSensorListener.setOnShakeListener(new ShakeEventListener.OnShakeListener() {
public void onShake() {
Toast.makeText(KPBActivityImpl.this, "Shake!", Toast.LENGTH_SHORT).show();
}
});
and:
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(mSensorListener,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() {
mSensorManager.unregisterListener(mSensorListener);
super.onPause();
}
下面是另一个代码:
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
public class AccelerometerListener implements SensorEventListener {
private SensorManager sensorManager;
private List<Sensor> sensors;
private Sensor sensor;
private long lastUpdate = -1;
private long currentTime = -1;
private Main parent;
private Timer timer;
private int shakes;
private static final Handler mHandler = new Handler();
private float last_x, last_y, last_z;
private float current_x, current_y, current_z, currenForce;
private static final int FORCE_THRESHOLD = 500;
private final int DATA_X = SensorManager.DATA_X;
private final int DATA_Y = SensorManager.DATA_Y;
private final int DATA_Z = SensorManager.DATA_Z;
public AccelerometerListener(Main parent) {
SensorManager sensorService = (SensorManager) parent
.getSystemService(Context.SENSOR_SERVICE);
this.sensorManager = sensorService;
if (sensorService == null)
return;
this.sensors = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensors.size() > 0) {
sensor = sensors.get(0);
}
this.parent = parent;
}
public void start() {
if (sensor == null)
return;
sensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_GAME);
}
public void stop() {
if (sensorManager == null)
return;
sensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor s, int valu) {
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
currentTime = System.currentTimeMillis();
if ((currentTime - lastUpdate) > 50) {
long diffTime = (currentTime - lastUpdate);
lastUpdate = currentTime;
current_x = event.values[DATA_X];
current_y = event.values[DATA_Y];
current_z = event.values[DATA_Z];
currenForce = Math.abs(current_x + current_y + current_z - last_x
- last_y - last_z)
/ diffTime * 10000;
if (currenForce > FORCE_THRESHOLD) {
shakeDetected();
}
last_x = current_x;
last_y = current_y;
last_z = current_z;
}
}
private void shakeDetected() {
shakes++;
if (shakes == 1) {
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (shakes > 3) {
mHandler.post(new Runnable() {
public void run() {
// shake
}
});
}
shakes = 0;
}
}, 500);
}
}
}
我真的很喜欢彼得dk的回答。我自作主张对他的代码做了一些调整。
文件:ShakeDetector.java
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.FloatMath;
public class ShakeDetector implements SensorEventListener {
// The gForce that is necessary to register as shake. Must be greater than 1G (one earth gravity unit)
private static final float SHAKE_THRESHOLD_GRAVITY = 2.7F;
private static final int SHAKE_SLOP_TIME_MS = 500;
private static final int SHAKE_COUNT_RESET_TIME_MS = 3000;
private OnShakeListener mListener;
private long mShakeTimestamp;
private int mShakeCount;
public void setOnShakeListener(OnShakeListener listener) {
this.mListener = listener;
}
public interface OnShakeListener {
public void onShake(int count);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
@Override
public void onSensorChanged(SensorEvent event) {
if (mListener != null) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float gX = x / SensorManager.GRAVITY_EARTH;
float gY = y / SensorManager.GRAVITY_EARTH;
float gZ = z / SensorManager.GRAVITY_EARTH;
// gForce will be close to 1 when there is no movement.
float gForce = FloatMath.sqrt(gX * gX + gY * gY + gZ * gZ);
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
final long now = System.currentTimeMillis();
// ignore shake events too close to each other (500ms)
if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now ) {
return;
}
// reset the shake count after 3 seconds of no shakes
if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now ) {
mShakeCount = 0;
}
mShakeTimestamp = now;
mShakeCount++;
mListener.onShake(mShakeCount);
}
}
}
}
另外,不要忘记需要向SensorManager注册一个ShakeDetector实例。
// ShakeDetector initialization
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mShakeDetector = new ShakeDetector();
mShakeDetector.setOnShakeListener(new OnShakeListener() {
@Override
public void onShake(int count) {
handleShakeEvent(count);
}
});
mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
这里还有另一个实现,它是基于这里的一些技巧以及来自Android开发人员网站的代码。
MainActivity.java
public class MainActivity extends Activity {
private ShakeDetector mShakeDetector;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ShakeDetector initialization
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mShakeDetector = new ShakeDetector(new OnShakeListener() {
@Override
public void onShake() {
// Do stuff!
}
});
}
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() {
mSensorManager.unregisterListener(mShakeDetector);
super.onPause();
}
}
ShakeDetector.java
package com.example.test;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class ShakeDetector implements SensorEventListener {
// Minimum acceleration needed to count as a shake movement
private static final int MIN_SHAKE_ACCELERATION = 5;
// Minimum number of movements to register a shake
private static final int MIN_MOVEMENTS = 2;
// Maximum time (in milliseconds) for the whole shake to occur
private static final int MAX_SHAKE_DURATION = 500;
// Arrays to store gravity and linear acceleration values
private float[] mGravity = { 0.0f, 0.0f, 0.0f };
private float[] mLinearAcceleration = { 0.0f, 0.0f, 0.0f };
// Indexes for x, y, and z values
private static final int X = 0;
private static final int Y = 1;
private static final int Z = 2;
// OnShakeListener that will be notified when the shake is detected
private OnShakeListener mShakeListener;
// Start time for the shake detection
long startTime = 0;
// Counter for shake movements
int moveCount = 0;
// Constructor that sets the shake listener
public ShakeDetector(OnShakeListener shakeListener) {
mShakeListener = shakeListener;
}
@Override
public void onSensorChanged(SensorEvent event) {
// This method will be called when the accelerometer detects a change.
// Call a helper method that wraps code from the Android developer site
setCurrentAcceleration(event);
// Get the max linear acceleration in any direction
float maxLinearAcceleration = getMaxCurrentLinearAcceleration();
// Check if the acceleration is greater than our minimum threshold
if (maxLinearAcceleration > MIN_SHAKE_ACCELERATION) {
long now = System.currentTimeMillis();
// Set the startTime if it was reset to zero
if (startTime == 0) {
startTime = now;
}
long elapsedTime = now - startTime;
// Check if we're still in the shake window we defined
if (elapsedTime > MAX_SHAKE_DURATION) {
// Too much time has passed. Start over!
resetShakeDetection();
}
else {
// Keep track of all the movements
moveCount++;
// Check if enough movements have been made to qualify as a shake
if (moveCount > MIN_MOVEMENTS) {
// It's a shake! Notify the listener.
mShakeListener.onShake();
// Reset for the next one!
resetShakeDetection();
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Intentionally blank
}
private void setCurrentAcceleration(SensorEvent event) {
/*
* BEGIN SECTION from Android developer site. This code accounts for
* gravity using a high-pass filter
*/
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8f;
// Gravity components of x, y, and z acceleration
mGravity[X] = alpha * mGravity[X] + (1 - alpha) * event.values[X];
mGravity[Y] = alpha * mGravity[Y] + (1 - alpha) * event.values[Y];
mGravity[Z] = alpha * mGravity[Z] + (1 - alpha) * event.values[Z];
// Linear acceleration along the x, y, and z axes (gravity effects removed)
mLinearAcceleration[X] = event.values[X] - mGravity[X];
mLinearAcceleration[Y] = event.values[Y] - mGravity[Y];
mLinearAcceleration[Z] = event.values[Z] - mGravity[Z];
/*
* END SECTION from Android developer site
*/
}
private float getMaxCurrentLinearAcceleration() {
// Start by setting the value to the x value
float maxLinearAcceleration = mLinearAcceleration[X];
// Check if the y value is greater
if (mLinearAcceleration[Y] > maxLinearAcceleration) {
maxLinearAcceleration = mLinearAcceleration[Y];
}
// Check if the z value is greater
if (mLinearAcceleration[Z] > maxLinearAcceleration) {
maxLinearAcceleration = mLinearAcceleration[Z];
}
// Return the greatest value
return maxLinearAcceleration;
}
private void resetShakeDetection() {
startTime = 0;
moveCount = 0;
}
// (I'd normally put this definition in it's own .java file)
public interface OnShakeListener {
public void onShake();
}
}
我修改了@peceps的答案,并使它的kotlin版本。我还添加了LifecycleOwner参数,使其能够感知生命周期。
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import kotlin.math.abs
/**
* Listener that detects shake gesture.
*/
class ShakeEventListener(
lifecycleOwner: LifecycleOwner,
private val sensorManager: SensorManager,
private val onShake: () -> Unit = {}
) : SensorEventListener, DefaultLifecycleObserver {
/** Time when the gesture started. */
private var mFirstDirectionChangeTime: Long = 0
/** Time when the last movement started. */
private var mLastDirectionChangeTime: Long = 0
/** How many movements are considered so far. */
private var mDirectionChangeCount = 0
/** The last x position. */
private var lastX = 0f
/** The last y position. */
private var lastY = 0f
/** The last z position. */
private var lastZ = 0f
init {
sensorManager.registerListener(
this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI
)
// observe lifecycle state
lifecycleOwner.lifecycle.addObserver(this)
}
override fun onResume(owner: LifecycleOwner) {
sensorManager.registerListener(
this,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI
)
}
override fun onPause(owner: LifecycleOwner) {
sensorManager.unregisterListener(this)
}
override fun onSensorChanged(se: SensorEvent) {
// get sensor data
val x = se.values[0]
val y = se.values[1]
val z = se.values[2]
// calculate movement
val totalMovement = abs(x + y + z - lastX - lastY - lastZ)
if (totalMovement > MIN_FORCE) {
// get time
val now = System.currentTimeMillis()
// store first movement time
if (mFirstDirectionChangeTime == 0L) {
mFirstDirectionChangeTime = now
mLastDirectionChangeTime = now
}
// check if the last movement was not long ago
val lastChangeWasAgo = now - mLastDirectionChangeTime
if (lastChangeWasAgo < MAX_PAUSE_BETWEEN_DIRECTION_CHANGE) {
// store movement data
mLastDirectionChangeTime = now
mDirectionChangeCount++
// store last sensor data
lastX = x
lastY = y
lastZ = z
// check how many movements are so far
if (mDirectionChangeCount >= MIN_DIRECTION_CHANGE) {
// check total duration
val totalDuration = now - mFirstDirectionChangeTime
if (totalDuration < MAX_TOTAL_DURATION_OF_SHAKE) {
onShake()
resetShakeParameters()
}
}
} else {
resetShakeParameters()
}
}
}
/**
* Resets the shake parameters to their default values.
*/
private fun resetShakeParameters() {
mFirstDirectionChangeTime = 0
mDirectionChangeCount = 0
mLastDirectionChangeTime = 0
lastX = 0f
lastY = 0f
lastZ = 0f
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
companion object {
/** Minimum movement force to consider. */
private const val MIN_FORCE = 10
/**
* Minimum times in a shake gesture that the direction of movement needs to
* change.
*/
private const val MIN_DIRECTION_CHANGE = 3
/** Maximum pause between movements. */
private const val MAX_PAUSE_BETWEEN_DIRECTION_CHANGE = 200
/** Maximum allowed time for shake gesture. */
private const val MAX_TOTAL_DURATION_OF_SHAKE = 400
}
}
在您的活动中,添加以下代码,使其检测震动事件:
ShakeEventListener(this, sensorManager){
// onShake logic
}