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.