背景

项目中,需要给用户反馈提供一个入口,参考蒲公英sdk的做法,通过摇晃手机,打开反馈入口。

效果图如下:

仿蒲公英摇晃手机显示反馈问题窗口_时间间隔

实现方案

  1. 添加摇晃监测类
  • SensorManagerHelper
class SensorManagerHelper(private val context: Context) : SensorEventListener {

// 速度阈值,当摇晃速度达到这值后产生作用
private val SPEED_SHRESHOLD = 6000
// 两次检测的时间间隔
private val UPTATE_INTERVAL_TIME = 50
// 传感器管理器
private var sensorManager: SensorManager? = null
// 传感器
private var sensor: Sensor? = null
// 重力感应监听器
lateinit var onShakeListener: () -> Unit
// 手机上一个位置时重力感应坐标
private var lastX: Float = 0.toFloat()
private var lastY: Float = 0.toFloat()
private var lastZ: Float = 0.toFloat()
// 上次检测时间
private var lastUpdateTime: Long = 0

init {
start()
}

/**
* 开始检测
*/
private fun start() {
// 获得传感器管理器
sensorManager = context
.getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager != null) {
// 获得重力传感器
sensor = sensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
// 注册
if (sensor != null) {
sensorManager!!.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_GAME)
}
}

/**
* 停止检测
*/
fun stop() {
sensorManager!!.unregisterListener(this)
}

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

/**
* 重力感应器感应获得变化数据
* android.hardware.SensorEventListener#onSensorChanged(android.hardware
* .SensorEvent)
*/
override fun onSensorChanged(event: SensorEvent) {
// 现在检测时间
val currentUpdateTime = System.currentTimeMillis()
// 两次检测的时间间隔
val timeInterval = currentUpdateTime - lastUpdateTime
// 判断是否达到了检测时间间隔
if (timeInterval < UPTATE_INTERVAL_TIME) return
// 现在的时间变成last时间
lastUpdateTime = currentUpdateTime
// 获得x,y,z坐标
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
// 获得x,y,z的变化值
val deltaX = x - lastX
val deltaY = y - lastY
val deltaZ = z - lastZ
// 将现在的坐标变成last坐标
lastX = x
lastY = y
lastZ = z
val speed = Math.sqrt((deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ).toDouble()) / timeInterval * 10000
// 达到速度阀值,发出提示
if (speed >= SPEED_SHRESHOLD) {
onShakeListener()
}
}
}
  • ShakeManager
object ShakeManager {
private var isShakeListenerOn = false
private var dialog: FeedbackDialog? = null
private var mActivity: WeakReference<Activity>? = null

fun initSensor(context: Context) {
val sensorHelper = SensorManagerHelper(context)
sensorHelper.onShakeListener = this::onShake
}

fun start(activity: Activity) {
mActivity = WeakReference(activity)
isShakeListenerOn = true
}

fun stop() {
isShakeListenerOn = false
}

@Synchronized
private fun onShake() {
val activity = mActivity!!.get()
if (isShakeListenerOn && activity != null) {
closeDialogWhenActivityChanges()
if (dialog == null || hasActivityChanged()) {
dialog = FeedbackDialog(activity)
}
if (!dialog!!.isShowing && !activity.isFinishing) {
dialog!!.show()
}
}
}

/**
* 因为dialog是绑定到activity的,如果activity变化了,就关闭之前的dialog,后面摇晃手机再次生成dialog。
*/
private fun closeDialogWhenActivityChanges() {
if (dialog == null || dialog!!.activity == null || dialog!!.activity.isFinishing || dialog!!.activity.isDestroyed) {
return
}
if (dialog != null && hasActivityChanged()) {
dialog!!.dismiss()
}
}

private fun hasActivityChanged(): Boolean {
var result = false
val activity = mActivity!!.get()
if (activity != null && activity != dialog!!.activity) {
result = true
}
return result
}
}
  1. 在Applicationd的onCreate方法中启动重力感应监听
ShakeManager.initSensor(applicationContext)
  1. 在Activity的onResume方法中允许打开反馈窗口,onPause中不允许。
public override fun onResume() {
super.onResume()
ShakeManager.start(this)
}

public override fun onPause() {
super.onPause()
ShakeManager.stop()
}

Demo源代码:

https://gitee.com/cxyzy1/feedbackDemo.git

点击关注专栏,查看最新技术分享

更多技术总结好文,请关注:「程序园中猿」

仿蒲公英摇晃手机显示反馈问题窗口_重力感应_02仿蒲公英摇晃手机显示反馈问题窗口_重力感应_03