Wie erkennt man ein Shake-Ereignis mit Android?

117

Wie kann ich ein Shake-Ereignis mit Android erkennen? Wie kann ich die Verwacklungsrichtung erkennen?

Ich möchte das Bild in einer Bildansicht ändern, wenn Verwacklungen auftreten.

Kanivel
quelle
29
Ich googelte und endete mit dieser Frage als Top-Ergebnis ...
zmbq
stackoverflow.com/a/54745560/4307338 Sie können diese Antwort überprüfen
Hoque MD Zahidul

Antworten:

173

Aus Code-Sicht müssen Sie den SensorListener implementieren:

public class ShakeActivity extends Activity implements SensorListener

Sie müssen einen SensorManager erwerben:

sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

Und registrieren Sie diesen Sensor mit den gewünschten Flags:

sensorMgr.registerListener(this,
SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_GAME);

In Ihrer onSensorChange () -Methode bestimmen Sie, ob es sich um einen Shake handelt oder nicht:

public void onSensorChanged(int sensor, float[] values) {
  if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
    long curTime = System.currentTimeMillis();
    // only allow one update every 100ms.
    if ((curTime - lastUpdate) > 100) {
      long diffTime = (curTime - lastUpdate);
      lastUpdate = curTime;

      x = values[SensorManager.DATA_X];
      y = values[SensorManager.DATA_Y];
      z = values[SensorManager.DATA_Z];

      float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime * 10000;

      if (speed > SHAKE_THRESHOLD) {
        Log.d("sensor", "shake detected w/ speed: " + speed);
        Toast.makeText(this, "shake detected w/ speed: " + speed, Toast.LENGTH_SHORT).show();
      }
      last_x = x;
      last_y = y;
      last_z = z;
    }
  }
}

Die Schüttelschwelle ist definiert als:

private static final int SHAKE_THRESHOLD = 800;

Es gibt auch einige andere Methoden, um Verwacklungsbewegungen zu erkennen. Schau dir diesen Link an. (Wenn dieser Link nicht funktioniert oder der Link tot ist, sehen Sie sich dieses Webarchiv an .)

Schauen Sie sich dieses Beispiel für Android Shake Detect Listener an.

Hinweis: SensorListener ist veraltet. wir können SensorEventListenerstattdessen verwenden. Hier ist ein kurzes Beispiel mit SensorEventListener.

Vielen Dank.

N-FREUDE
quelle
16
Verwenden Sie keine SensorListener-Klasse. Verwenden Sie SensorEventListener.
Picaso
1
Der Link ist tot ... Hier ist der Artikel auf archive.org: web.archive.org/web/20100324212856/http://www.codeshogun.com/…
Pilot_51
Ich finde, dass Sie die Empfindlichkeit je nach Gerät ändern wird. Was auf dem Galaxy Nexus als völlig akzeptable Shake-Erkennung erschien, muss auf einer Galaxy III, auf der dieselbe App ausgeführt wird, ein viel heftigeres Shake sein. Wenn ich es für dieses Gerät weniger empfindlich mache, ist es für so etwas wie den Nexus zu empfindlich. hmmmmmmm.
Topwik
Warum mit 10000 multipliziert und sollte der Nenner in Klammern stehen?
Yoda
3
Dies funktioniert, aber Sie sollten die Mathematik überprüfen, um die Beschleunigung zu berechnen: Die von den Sensoren zurückgegebenen Werte sind in m / s ^ 2 angegeben - es besteht keine Notwendigkeit, durch die Zeit zu teilen. Vielleicht möchten Sie auch sehen, wie Sie die Beschleunigungsvektoren multiplizieren.
Tad
67

Google hilft sehr .

/* The following code was written by Matthew Wiggins
 * and is released under the APACHE 2.0 license
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 */
package com.hlidskialf.android.hardware;

import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.content.Context;
import java.lang.UnsupportedOperationException;

public class ShakeListener implements SensorListener 
{
  private static final int FORCE_THRESHOLD = 350;
  private static final int TIME_THRESHOLD = 100;
  private static final int SHAKE_TIMEOUT = 500;
  private static final int SHAKE_DURATION = 1000;
  private static final int SHAKE_COUNT = 3;

  private SensorManager mSensorMgr;
  private float mLastX=-1.0f, mLastY=-1.0f, mLastZ=-1.0f;
  private long mLastTime;
  private OnShakeListener mShakeListener;
  private Context mContext;
  private int mShakeCount = 0;
  private long mLastShake;
  private long mLastForce;

  public interface OnShakeListener
  {
    public void onShake();
  }

  public ShakeListener(Context context) 
  { 
    mContext = context;
    resume();
  }

  public void setOnShakeListener(OnShakeListener listener)
  {
    mShakeListener = listener;
  }

  public void resume() {
    mSensorMgr = (SensorManager)mContext.getSystemService(Context.SENSOR_SERVICE);
    if (mSensorMgr == null) {
      throw new UnsupportedOperationException("Sensors not supported");
    }
    boolean supported = mSensorMgr.registerListener(this, SensorManager.SENSOR_ACCELEROMETER, SensorManager.SENSOR_DELAY_GAME);
    if (!supported) {
      mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
      throw new UnsupportedOperationException("Accelerometer not supported");
    }
  }

  public void pause() {
    if (mSensorMgr != null) {
      mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
      mSensorMgr = null;
    }
  }

  public void onAccuracyChanged(int sensor, int accuracy) { }

  public void onSensorChanged(int sensor, float[] values) 
  {
    if (sensor != SensorManager.SENSOR_ACCELEROMETER) return;
    long now = System.currentTimeMillis();

    if ((now - mLastForce) > SHAKE_TIMEOUT) {
      mShakeCount = 0;
    }

    if ((now - mLastTime) > TIME_THRESHOLD) {
      long diff = now - mLastTime;
      float speed = Math.abs(values[SensorManager.DATA_X] + values[SensorManager.DATA_Y] + values[SensorManager.DATA_Z] - mLastX - mLastY - mLastZ) / diff * 10000;
      if (speed > FORCE_THRESHOLD) {
        if ((++mShakeCount >= SHAKE_COUNT) && (now - mLastShake > SHAKE_DURATION)) {
          mLastShake = now;
          mShakeCount = 0;
          if (mShakeListener != null) { 
            mShakeListener.onShake(); 
          }
        }
        mLastForce = now;
      }
      mLastTime = now;
      mLastX = values[SensorManager.DATA_X];
      mLastY = values[SensorManager.DATA_Y];
      mLastZ = values[SensorManager.DATA_Z];
    }
  }

}
Vincent Mimoun-Prat
quelle
3
java.lang.SecurityException: Requires VIBRATE permission
Pratik Butani
2
SensorManager.DATA_[X,Y,Z]sind jetzt veraltet.
Rany Albeg Wein
@ RanyAlbegWein bist du sicher? Ich habe die Dokumente und die IDE überprüft und es wurden keine durchgestrichenen oder veralteten Warnungen angezeigt.
Neon Warge
Veraltet ... Meistens alles veraltet.
Pratik Butani
33

Sie können auch einen Blick auf die Bibliothek Seismic werfen

public class Demo extends Activity implements ShakeDetector.Listener {
  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    ShakeDetector sd = new ShakeDetector(this);
    sd.start(sensorManager);

    TextView tv = new TextView(this);
    tv.setGravity(CENTER);
    tv.setText("Shake me, bro!");
    setContentView(tv, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
  }

  @Override public void hearShake() {
    Toast.makeText(this, "Don't shake me, bro!", Toast.LENGTH_SHORT).show();
  }
}
Borys
quelle
1
Unterschätzte Antwort.
Morgoth
Dieses hier ist besser. Diese Bibliothek wurde von Square entwickelt.
Sadda Hussain
19

Es gibt bereits viele Lösungen für diese Frage, aber ich wollte eine veröffentlichen, die:

  • Verwendet keine in API 3 beraubte Bibliothek
  • Berechnet die Größe der Beschleunigung korrekt
  • Wendet eine Zeitüberschreitung zwischen Shake-Ereignissen korrekt an

Hier ist eine solche Lösung:

// variables for shake detection
private static final float SHAKE_THRESHOLD = 3.25f; // m/S**2
private static final int MIN_TIME_BETWEEN_SHAKES_MILLISECS = 1000;
private long mLastShakeTime;
private SensorManager mSensorMgr;

So initialisieren Sie den Timer:

// Get a sensor manager to listen for shakes
mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

// Listen for shakes
Sensor accelerometer = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (accelerometer != null) {
    mSensorMgr.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}

SensorEventListener Methoden zum Überschreiben:

@Override
public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
        long curTime = System.currentTimeMillis();
        if ((curTime - mLastShakeTime) > MIN_TIME_BETWEEN_SHAKES_MILLISECS) {

            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];

            double acceleration = Math.sqrt(Math.pow(x, 2) +
                    Math.pow(y, 2) +
                    Math.pow(z, 2)) - SensorManager.GRAVITY_EARTH;
            Log.d(APP_NAME, "Acceleration is " + acceleration + "m/s^2");

            if (acceleration > SHAKE_THRESHOLD) {
                mLastShakeTime = curTime;
                Log.d(APP_NAME, "Shake, Rattle, and Roll");
            }
        }
    }
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // Ignore
}

Wenn Sie fertig sind

// Stop listening for shakes
mSensorMgr.unregisterListener(this);
Bisschen
quelle
9

Da SensorListener ist veraltet , so verwenden Sie folgenden Code:

/* put this into your activity class */
private SensorManager mSensorManager;
private float mAccel; // acceleration apart from gravity
private float mAccelCurrent; // current acceleration including gravity
private float mAccelLast; // last acceleration including gravity

private final SensorEventListener mSensorListener = new SensorEventListener() {

  public void onSensorChanged(SensorEvent se) {
    float x = se.values[0];
    float y = se.values[1];
    float z = se.values[2];
    mAccelLast = mAccelCurrent;
    mAccelCurrent = (float) Math.sqrt((double) (x*x + y*y + z*z));
    float delta = mAccelCurrent - mAccelLast;
    mAccel = mAccel * 0.9f + delta; // perform low-cut filter
  }

  public void onAccuracyChanged(Sensor sensor, int accuracy) {
  }
};

@Override
protected void onResume() {
  super.onResume();
  mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
  mSensorManager.unregisterListener(mSensorListener);
  super.onPause();
}

Dann:

/* do this in onCreate */
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
mAccel = 0.00f;
mAccelCurrent = SensorManager.GRAVITY_EARTH;
mAccelLast = SensorManager.GRAVITY_EARTH;

Die Frage mit allen Details finden Sie hier:

Android: Ich möchte es schütteln

Saeid N.
quelle
1
Zuletzt habe ich überprüft, ob der Tiefpassfilter [Alpha * Strom + (1-Alpha) * Vorheriger] ist, und 0,9 (Alpha?) Ist vollständig von der Abtastrate des Sensors abhängig, z. B. sollte für 100 ms Alpha 0,146 ish sein, und Sie können nicht davon ausgehen Alle Geräte verwenden genau die gleiche Sensorrate.
Flucht-llc
Ich bin ziemlich neu in Android. Können Sie mir helfen zu verstehen, warum onResume und onPause aufgerufen werden, wenn der Bildschirm wackelt? Ich verstehe diesen Teil nicht.
Pikamander2
1
@ Pikamander2 Wir müssen einen Listener in sensorManager registrieren. Wir haben dies in onResume getan und die Registrierung des Listeners in onPause aufgehoben. Wenn der Benutzer die App verlassen hat, funktioniert dieser Listener erst, wenn der Benutzer zur App zurückkehrt.
Saeid N.
3

Dies ist für Kotlinund VerwendungSensorEventListener

Neue Klasse erstellen ShakeDetector

class ShakeDetector : SensorEventListener {
    private var mListener: OnShakeListener? = null
    private var mShakeTimestamp: Long = 0
    private var mShakeCount = 0
    fun setOnShakeListener(listener: OnShakeListener?) {
        mListener = listener
    }

    interface OnShakeListener {
        fun onShake(count: Int)
    }

    override fun onAccuracyChanged(
        sensor: Sensor,
        accuracy: Int
    ) { // ignore
    }

    override fun onSensorChanged(event: SensorEvent) {
        if (mListener != null) {
            val x = event.values[0]
            val y = event.values[1]
            val z = event.values[2]
            val gX = x / SensorManager.GRAVITY_EARTH
            val gY = y / SensorManager.GRAVITY_EARTH
            val gZ = z / SensorManager.GRAVITY_EARTH
            // gForce will be close to 1 when there is no movement.
            val gForce: Float = sqrt(gX * gX + gY * gY + gZ * gZ)
            if (gForce > SHAKE_THRESHOLD_GRAVITY) {
                val 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)
            }
        }
    }

    companion object {
        /*
     * The gForce that is necessary to register as shake.
     * Must be greater than 1G (one earth gravity unit).
     * You can install "G-Force", by Blake La Pierre
     * from the Google Play Store and run it to see how
     *  many G's it takes to register a shake
     */
        private const val SHAKE_THRESHOLD_GRAVITY = 2.7f
        private const val SHAKE_SLOP_TIME_MS = 500
        private const val SHAKE_COUNT_RESET_TIME_MS = 3000
    }
}

Ihre Hauptaktivität

class MainActivity : AppCompatActivity() {

    // The following are used for the shake detection
    private var mSensorManager: SensorManager? = null
    private var mAccelerometer: Sensor? = null
    private var mShakeDetector: ShakeDetector? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initSensor()
    }
    override fun onResume() {
        super.onResume()
        // Add the following line to register the Session Manager Listener onResume
        mSensorManager!!.registerListener(
            mShakeDetector,
            mAccelerometer,
            SensorManager.SENSOR_DELAY_UI
        )
    }

    override fun onPause() { // Add the following line to unregister the Sensor Manager onPause
        mSensorManager!!.unregisterListener(mShakeDetector)
        super.onPause()
    }

    private fun initSensor() {
        // ShakeDetector initialization
        // ShakeDetector initialization
        mSensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
        mAccelerometer = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        mShakeDetector = ShakeDetector()
        mShakeDetector!!.setOnShakeListener(object : OnShakeListener {
            override fun onShake(count: Int) { /*
                 * The following method, "handleShakeEvent(count):" is a stub //
                 * method you would use to setup whatever you want done once the
                 * device has been shook.
                 */
                Toast.makeText(this@MainActivity, count.toString(), Toast.LENGTH_SHORT).show()
            }
        })
    }
}

Fügen Sie diesen Code schließlich zu Manifests hinzu, um sicherzustellen, dass das Telefon über einen Beschleunigungsmesser verfügt

<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />
Mohsen
quelle
1

Mach Folgendes:

private float xAccel, yAccel, zAccel;
private float xPreviousAccel, yPreviousAccel, zPreviousAccel;
private boolean firstUpdate = true;
private final float shakeThreshold = 1.5f;
private boolean shakeInitiated = false;
SensorEventListener mySensorEventListener;
SensorManager mySensorManager;

Fügen Sie dies in die onCreate-Methode ein.

mySensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mySensorManager.registerListener(mySensorEventListener,
            mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
            SensorManager.SENSOR_DELAY_NORMAL);

Und jetzt der Hauptteil.

private boolean isAccelerationChanged() {
    float deltaX = Math.abs(xPreviousAccel - xAccel);
    float deltaY = Math.abs(yPreviousAccel - yAccel);
    float deltaZ = Math.abs(zPreviousAccel - zAccel);
    return (deltaX > shakeThreshold && deltaY > shakeThreshold)
            || (deltaX > shakeThreshold && deltaZ > shakeThreshold)
            || (deltaY > shakeThreshold && deltaZ > shakeThreshold);
}

private void updateAccelParameters(float xNewAccel, float yNewAccel, float zNewAccel) {
    if (firstUpdate) {
        xPreviousAccel = xNewAccel;
        yPreviousAccel = yNewAccel;
        zPreviousAccel = zNewAccel;
        firstUpdate = false;
    }else{
        xPreviousAccel = xAccel;
        yPreviousAccel = yAccel;
        zPreviousAccel = zAccel;
    }
    xAccel = xNewAccel;
    yAccel = yNewAccel;
    zAccel = zNewAccel;
}

private void executeShakeAction() {
    //this method is called when devices shakes
}

public void onSensorChanged(SensorEvent se) {
    updateAccelParameters(se.values[0], se.values[1], se.values[2]);
    if ((!shakeInitiated) && isAccelerationChanged()) {
        shakeInitiated = true;
    }else if ((shakeInitiated) && isAccelerationChanged()){
        executeShakeAction();
    }else if((shakeInitiated) && (!isAccelerationChanged())){
        shakeInitiated = false;
    }
}

public void onAccuracyChanged(Sensor sensor, int accuracy) {
    //setting the accuracy
}
jelic98
quelle
Ich versuche festzustellen, ob der Benutzer das Telefon dreimal geschüttelt und dann ein Audio
abgespielt hat
Code wie dieser (und viele andere ähnliche Beispiele) erkennt nur eine anhaltende Beschleunigungsperiode, nicht unbedingt ein Schütteln. Die Hin- und Herbewegung, bei der es sich tatsächlich um eine Beschleunigung gefolgt von einer negativen Beschleunigung handelt, wird nicht erkannt.
Flucht-llc
1

Vergessen Sie nicht, diesen Code in Ihr MainActivity.java:

MainActivity.java

mShaker = new ShakeListener(this);
mShaker.setOnShakeListener(new ShakeListener.OnShakeListener () {
    public void onShake() {
        Toast.makeText(MainActivity.this, "Shake " , Toast.LENGTH_LONG).show();        
    }
});

@Override
protected void onResume() {
    super.onResume();
    mShaker.resume();
}

@Override
protected void onPause() {
    super.onPause();
    mShaker.pause();
}

Oder ich gebe dir einen Link zu diesem Zeug.

ddd
quelle