Android에서 흔들림 이벤트를 감지하는 방법은 무엇입니까?


117

Android에서 흔들림 이벤트를 어떻게 감지합니까? 흔들림 방향을 어떻게 감지 할 수 있습니까?

흔들림이 발생하면 이미지 뷰에서 이미지를 변경하고 싶습니다.


29
나는 봤는데,이 질문이 최고 결과로 끝났다 ...
— zmbq

stackoverflow.com/a/54745560/4307338 이 답변을 확인하실 수 있습니다
— Hoque MD Zahidul

답변:


174

코드 관점에서 SensorListener를 구현해야합니다.

public class ShakeActivity extends Activity implements SensorListener

SensorManager를 획득해야합니다.

sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);

그리고 원하는 플래그로이 센서를 등록합니다.

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

onSensorChange () 메서드에서 흔들림인지 여부를 결정합니다.

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

흔들림 임계 값은 다음과 같이 정의됩니다.

private static final int SHAKE_THRESHOLD = 800;

흔들림 동작을 감지하는 다른 방법도 있습니다. 이 링크를 보세요 . (링크가 작동하지 않거나 링크가 작동하지 않으면 이 웹 아카이브를보십시오 .)

Android 흔들림 감지 수신기에 대한 이 예제 를 살펴보세요 .

참고 : SensorListener 는 더 이상 사용되지 않습니다. SensorEventListener대신 사용할 수 있습니다 . 다음은 SensorEventListener를 사용한 간단한 예입니다.

감사.


16
SensorListener 클래스를 사용하지 마십시오. 더 이상 사용되지 않으므로 SensorEventListener를 사용하십시오.
— picaso 2012

1
링크는 여기 archive.org에 기사의 ... 죽은 : web.archive.org/web/20100324212856/http://www.codeshogun.com/...이
— Pilot_51

장치에 따라 감도가 달라지는 것을 알았습니다. 갤럭시 넥서스에서 완벽하게 수용 가능한 흔들림 감지처럼 보였던 것은 같은 앱을 실행하는 갤럭시 III에서 훨씬 더 격렬한 흔들림이어야합니다. 이 장치에 대해 덜 민감하게 만들면 넥서스와 같은 것에 너무 민감해질 것입니다. 흠흠.
— topwik

왜 10000을 곱하고 분모에 괄호가 있어야합니까?
— 요다

3
이것은 작동하지만 가속도를 계산하기 위해 수학을 확인하고 싶을 수 있습니다. 센서가 반환하는 값은 m / s ^ 2입니다. 시간으로 나눌 필요가 없습니다. 또한 가속도 벡터를 어떻게 곱하는지 살펴볼 수 있습니다.
— Tad

67

Google은 많은 도움을줍니다 .

/* 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]이제 더 이상 사용되지 않습니다.
— Rany Albeg Wein

@RanyAlbegWein 확실한가요? 문서와 IDE를 확인했지만 취소 선이나 지원 중단 경고가 표시되지 않았습니다.
— Neon Warge

Deprecated ... 대부분은 모든 것을 사용하지 않습니다.
— Pratik Butani

34

라이브러리 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
과소 평가 된 답변입니다.
— Morgoth

이게 더 좋습니다. 이 라이브러리는 Square에서 개발했습니다.
— Sadda Hussain 2010 년


19

이 질문에 대한 많은 솔루션이 이미 있지만 게시하고 싶었습니다.

  • API 3에서 폐기 된 라이브러리를 사용하지 않습니다.
  • 가속도의 크기를 정확하게 계산합니다.
  • 흔들림 이벤트 사이에 타임 아웃을 올바르게 적용합니다.

다음은 그러한 해결책입니다.

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

타이머를 초기화하려면 :

// 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 재정의 할 메서드 :

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

다 끝나면

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

9

이후 SensorListener이 되어 사용되지 않는 때문에 다음 코드를 사용 :

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

그때:

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

자세한 내용은 여기에서 찾을 수 있습니다.

Android : 흔들고 싶어


1
마지막으로 확인한 저역 통과 필터는 [알파 * 전류 + (1- 알파) * 이전]이고 0.9 (알파?)는 센서의 샘플 속도에 완전히 의존합니다. 예를 들어 100ms의 경우 알파는 0.146ish 여야하며 가정 할 수 없습니다. 모든 장치는 정확히 동일한 센서 속도를 사용합니다.
— escape-llc

저는 Android를 처음 접했습니다. 화면이 흔들릴 때 onResume 및 onPause가 호출되는 이유를 이해하도록 도와 주시겠습니까? 나는 그 부분을 이해하지 못한다.
— Pikamander2 2017-10-08

1
@ Pikamander2 sensorManager에 리스너를 등록해야합니다. onResume에서 리스너를 등록하고 onPause에서 리스너를 등록 취소하므로 사용자가 앱을 떠났을 때이 리스너는 사용자가 앱으로 돌아올 때까지 작동하지 않습니다.
— Saeid N.


3

이것은 Kotlin및 사용입니다SensorEventListener

새 수업 만들기 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
    }
}

주요 활동

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

마지막으로이 코드를 Manifests에 추가하여 전화기에 가속도계가 있는지 확인하십시오.

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

1

다음을 수행하십시오.

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;

이것을 onCreate 메소드에 넣으십시오.

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

그리고 이제 주요 부분입니다.

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
}

나는 ... 사용자가 전화를 세 번 흔들었다 경우 탐지하려고 한 다음 오디오를 재생하고
— SI8

이와 같은 코드 (및 기타 유사한 예제)는 반드시 흔들림이 아닌 지속적인 가속 기간 만 감지합니다. 실제로 가속과 음의 가속이 뒤 따르는 앞뒤 움직임은 감지하지 못합니다.
— escape-llc

1

이 코드를 다음 위치에 추가하는 것을 잊지 마십시오 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();
}

또는이 항목에 대한 링크 를 제공합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.