Android ile sallama olayı nasıl tespit edilir?


Yanıtlar:


174

Kod açısından, SensorListener'ı uygulamanız gerekir:

public class ShakeActivity extends Activity implements SensorListener

Bir SensorManager edinmeniz gerekecek:

sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

Ve bu sensörü istenen işaretlerle kaydedin:

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

OnSensorChange () yönteminizde, sallama olup olmadığını belirlersiniz:

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;
    }
  }
}

Sarsıntı eşiği şu şekilde tanımlanır:

private static final int SHAKE_THRESHOLD = 800;

Sarsıntı hareketini algılamak için başka yöntemler de vardır. bu bağlantıya bak . (Bu bağlantı çalışmazsa veya bağlantı kesilirse , bu web arşivine bakın .).

Android sarsıntı algılama dinleyicisi için bu örneğe bir göz atın .

Not: SensorListener kullanımdan kaldırıldı. SensorEventListeneronun yerine kullanabiliriz . İşte SensorEventListener kullanarak hızlı bir örnek.

Teşekkürler.


16
SensorListener sınıfını kullanmayın. çünkü depricated. SensorEventListener'ı kullanın.
picaso

1
Bağlantı kesildi
Pilot_51

Cihaza göre hassasiyetin değişeceğini görüyorum. galaksi bağlantı noktasında tamamen kabul edilebilir sarsıntı tespiti gibi görünen şey, aynı uygulamayı çalıştıran bir galaksi III üzerinde çok daha şiddetli bir sarsıntı olmalı. Bu cihazı daha az hassas hale getirirsem, bağlantı noktası gibi bir şeyde çok hassas olur. hmmmmmmm.
topwik

Neden 10000 ile çarpılır ve paydada parantez olmalı mı?
Yoda

3
Bu işe yarıyor, ancak ivmeyi hesaplamak için matematiğe bakmak isteyebilirsiniz: Sensörlerin döndürdüğü değerler m / s ^ 2 cinsindendir - zamana bölmeye gerek yoktur. Ayrıca, ivme vektörlerini nasıl çarptığınıza da bakmak isteyebilirsiniz.
Tad

67

Google çok yardımcı oluyor .

/* 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];
    }
  }

}

3
java.lang.SecurityException: Requires VIBRATE permission
Pratik Butani

2
SensorManager.DATA_[X,Y,Z]artık kullanımdan kaldırıldı.
Rany Albeg Wein

@RanyAlbegWein emin misin? Belgeleri ve IDE'yi kontrol ettim ve üstü çizili veya kullanımdan kaldırma uyarıları almadı.
Neon Warge

Kullanımdan kaldırıldı ... Çoğunlukla her şeyi kullanımdan kaldırdı.
Pratik Butani

34

Ayrıca kütüphane Sismik'e de göz atabilirsiniz.

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();
  }
}

1
Yetersiz cevap.
Morgoth

Bu daha iyi. Bu kütüphane Square tarafından geliştirilmiştir.
Sadda Hussain


19

Bu soruya halihazırda birçok çözüm var, ancak şunu göndermek istedim:

  • API 3'te kısıtlanmış bir kitaplık kullanmaz
  • İvmenin büyüklüğünü doğru hesaplar
  • Shake olayları arasında doğru bir zaman aşımı uygular

İşte böyle bir çözüm:

// 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;

Zamanlayıcıyı başlatmak için:

// 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 geçersiz kılma yöntemleri:

@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
}

Tamamen bittiğinde

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

9

Yana SensorListener edilir kaldırılmış böylece şu kodu kullanın:

/* 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();
}

Sonra:

/* 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;

Tüm ayrıntıları içeren soru burada bulunabilir:

Android: Onu sallamak istiyorum


1
En son kontrol ettiğim düşük geçiş filtresi [alfa * akım + (1-alfa) * önceki] ve 0,9 (alfa?) Tamamen sensörün örnekleme hızına bağlıdır, örneğin 100 ms için alfa 0,146 ish olmalıdır ve bunu varsayamazsınız tüm cihazlar aynı sensör oranını kullanır.
escape-llc

Android'de oldukça yeniyim. Ekran titrediğinde onResume ve onPause'un neden çağrıldığını anlamama yardım edebilir misin? O kısmı anlamıyorum.
Pikamander2

1
@ Pikamander2 SensorManager'da bir dinleyici kaydetmemiz gerekiyor, onResume'da yaptık ve onPause'da dinleyicinin kaydını sildik, böylece kullanıcı uygulamadan ayrıldığında bu dinleyici kullanıcı uygulamaya geri dönene kadar çalışmayacaktır.
Saeid N.


3

Onun için Kotlin kullanım içindirSensorEventListener

Yeni sınıf oluştur 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
    }
}

Ana Faaliyetiniz

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()
            }
        })
    }
}

Son olarak, telefonun bir ivmeölçere sahip olduğundan emin olmak için bu kodu Manifestlere ekleyin

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

1

Aşağıdakileri yapın:

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;

Bunu onCreate yöntemine koyun.

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

Ve şimdi ana kısım.

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
}

Kullanıcının telefonu üç kez sallayıp ardından bir ses dosyası çaldığını tespit etmeye çalışıyorum ...
Si8

Bunun gibi bir kod (ve diğer birçok benzer örnek) yalnızca sürekli hızlanma periyodunu algılar, mutlaka sallanmayı değil. İleri-geri hareketi, aslında ivmeyi takiben negatif ivmeyi algılamayacaktır.
escape-llc

1

Bu kodu şuraya eklemeyi unutmayın: 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();
}

Ya da sana bu şeyler hakkında bir bağlantı veriyorum .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.