Làm thế nào để phát hiện sự kiện rung lắc với Android?


117

Làm cách nào để phát hiện sự kiện rung lắc với Android? Làm cách nào tôi có thể phát hiện hướng lắc?

Tôi muốn thay đổi hình ảnh trong chế độ xem hình ảnh khi xảy ra rung.


29
Tôi googled, và kết thúc với câu hỏi này như là kết quả đầu ...
zmbq

stackoverflow.com/a/54745560/4307338 bạn có thể kiểm tra câu trả lời này
Hoque MD Zahidul

Câu trả lời:


174

Từ quan điểm mã, bạn cần triển khai SensorListener:

public class ShakeActivity extends Activity implements SensorListener

Bạn sẽ cần phải có một SensorManager:

sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

Và đăng ký cảm biến này với các cờ mong muốn:

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

Trong phương thức onSensorChange (), bạn xác định xem đó có phải là rung lắc hay không:

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

Ngưỡng rung được xác định là:

private static final int SHAKE_THRESHOLD = 800;

Ngoài ra còn có một số phương pháp khác để phát hiện chuyển động rung lắc. nhìn vào liên kết này . (Nếu liên kết đó không hoạt động hoặc liên kết đã chết, hãy xem kho lưu trữ web này .).

Hãy xem ví dụ này cho trình nghe phát hiện rung của android.

Lưu ý: SensorListener không được dùng nữa. chúng ta có thể sử dụng SensorEventListenerthay thế. Đây là một ví dụ nhanh bằng cách sử dụng SensorEventListener.

Cảm ơn.


16
không sử dụng lớp SensorListener. bởi vì nó đã bị cạn kiệt. sử dụng SensorEventListener.
picaso

1
Liên kết là chết ... Dưới đây là bài viết về archive.org: web.archive.org/web/20100324212856/http://www.codeshogun.com/...
Pilot_51

tôi thấy rằng bạn sẽ thay đổi độ nhạy tùy theo thiết bị. những gì có vẻ như phát hiện rung lắc hoàn toàn có thể chấp nhận được trên thiên hà nexus phải là một rung lắc dữ dội hơn nhiều trên thiên hà III chạy cùng một ứng dụng. nếu tôi làm cho nó kém nhạy hơn cho thiết bị này, nó sẽ quá nhạy trên một thứ như nexus. hmmmmmmm.
topwik

Tại sao nhân với 10000 và phải có dấu ngoặc đơn ở mẫu số?
Yoda

3
Điều này hoạt động, nhưng bạn có thể muốn kiểm tra toán học để tính gia tốc: Các giá trị do cảm biến trả về tính bằng m / s ^ 2 - không cần chia cho thời gian. Ngoài ra, bạn có thể muốn xem cách bạn nhân các vectơ gia tốc.
Tad

67

Google giúp ích rất nhiều .

/* 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]hiện không được dùng nữa.
Rany Albeg Wein

@RanyAlbegWein bạn có chắc không? Tôi đã kiểm tra tài liệu và IDE và nó không nhận được cảnh báo gạch ngang hoặc không dùng nữa.
Neon Warge

Không được chấp nhận ... Hầu hết mọi thứ đều không được chấp nhận.
Pratik Butani

34

Bạn cũng có thể xem qua Thư viện Seismic

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
Câu trả lời bị đánh giá thấp.
Morgoth

Cái này thì tốt hơn. Thư viện này được phát triển bởi Square.
Sadda Hussain


19

Đã có rất nhiều giải pháp cho câu hỏi này, nhưng tôi muốn đăng một giải pháp rằng:

  • Không sử dụng thư viện được mô tả trong API 3
  • Tính độ lớn của gia tốc một cách chính xác
  • Áp dụng chính xác thời gian chờ giữa các sự kiện rung

Đây là một giải pháp như vậy:

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

Để khởi tạo bộ hẹn giờ:

// 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 phương pháp ghi đè:

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

Khi bạn hoàn thành tất cả

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

9

Kể từ SensorListener đang bị phản đối vì vậy sử dụng đoạn mã sau:

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

Sau đó:

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

Câu hỏi với đầy đủ chi tiết có thể được tìm thấy ở đây:

Android: Tôi muốn lắc nó


1
Lần cuối tôi kiểm tra bộ lọc thông thấp là [alpha * hiện tại + (1-alpha) * trước] và 0,9 (alpha?) Hoàn toàn phụ thuộc vào tốc độ lấy mẫu của cảm biến, ví dụ: đối với 100ms, alpha phải là 0,146 ish và bạn không thể giả định tất cả các thiết bị sử dụng cùng một tỷ lệ cảm biến chính xác.
Escape-llc

Tôi còn khá mới đối với Android. Bạn có thể giúp tôi hiểu tại sao onResume và onPause được gọi khi màn hình rung không? Tôi không hiểu phần đó.
Pikamander2

1
@ Pikamander2 Chúng ta cần đăng ký một trình lắng nghe trong sensorManager, Chúng tôi đã làm điều đó trong onResume và hủy đăng ký trình lắng nghe trong onPause để khi người dùng rời khỏi ứng dụng, trình nghe này sẽ không hoạt động cho đến khi người dùng quay lại ứng dụng.
N. nói


3

Đây là để Kotlinsử dụngSensorEventListener

Tạo lớp học mới 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
    }
}

Hoạt động chính của bạn

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

Cuối cùng thêm mã này vào Manifests để đảm bảo điện thoại có gia tốc kế

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

1

Làm như sau:

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;

Đặt điều này trong phương thức onCreate.

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

Và bây giờ là phần chính.

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
}

Tôi cố gắng để phát hiện nếu người dùng lắc điện thoại ba lần và sau đó chơi một âm thanh ...
Si8

Mã như thế này (và nhiều ví dụ tương tự khác) chỉ phát hiện khoảng thời gian duy trì của gia tốc, không nhất thiết phải rung. Nó sẽ không phát hiện chuyển động tới lui, mà thực chất là gia tốc theo sau là gia tốc âm.
Escape-llc

1

Đừng quên thêm mã này vào 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();
}

Hoặc tôi cung cấp cho bạn một liên kết về công cụ này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.