Android  摇一摇开发——灵敏度优化

效果最好的在文章最后给出,不想看分析的直接取最后的代码即可。

Android端的摇一摇功能现在使用十分广泛,从最开始微信凭借摇一摇的功能大火,到现在很多的APP都具备摇一摇的功能。因此摇一摇的开发也变得十分广泛,网上随手一查也能查到很多关于摇一摇的开发代码。摇一摇几乎都是根据Android自带传感的加速度传感器来实现的,检测到手机的加速度,然后做出相应的逻辑判断即可完成摇一摇的判定。

但是每个手机加速度传感器之间是有一定差距的,加上手机性能和手机的软件之间的不同,所以导致相同的代码在不同的手机上的体验有一定的差距。我们应当在不增加太多设计复杂的的基础上,争取让不同手机之间的体验达到一个比较接近的状态。

我体验了微信的摇一摇功能,微信的摇一摇现在比较容易触发,几乎只需要摇一次就可以触发,但是由于微信本身只有进入摇一摇界面才会开启摇一摇的功能,因此用户进入该界面就是想要摇一摇的,因此将摇一摇设置的比较敏感也是符合产品使用场景的。但是有些应用具备全局的摇一摇功能,那么此时就不应该设计的太容易触发,这样就会变成知乎那样,被各种吐槽了。

知乎摇一摇吐槽

如果单纯通过增加加速度阈值来增加触发难度,你会发现当用户真正想要摇一摇的时候会十分困难,可能就直接导致用户不使用摇一摇功能,这样意味着开发的这个功能完全失去了意义。这就需要我们从别的方向来解决这个问题了。

摇一摇的实现代码

说了这么多,现在我们正式进入摇一摇开发的实现。

上文也说道,现在的摇一摇都是通过Android的加速度传感器来实现的。通过检测加速,来判断用户是否在进行摇一摇操作。要获取加速度传感器的数据可以通过SensorManager的类来实现。

具体使用方法也比较简单,就不细说直接给出一段简单的代码:

//获取系统的SensorManager
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);

SensorEventListener listener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO: 添加自己的传感器数据处理代码;

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
};
//注册传感器监听事件
mSensorManager.registerListener(listener,
        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
        SensorManager.SENSOR_DELAY_UI);


//注销传感器监听
mSensorManager.unregisterListener(listener);

需要注意的是registerListener(listener,
​ mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
​ SensorManager.SENSOR_DELAY_UI);当中的SENSOR_DELAY_UI参数,这个参数表示传感器数据变化通知的频率,如果过快会造成性能和电量的消耗。官方提供了四个标准的参数

SENSOR_DELAY_FASTEST get sensor data as fast as possible

SENSOR_DELAY_GAME rate suitable for games

SENSOR_DELAY_NORMAL rate (default) suitable for screen orientation changes

SENSOR_DELAY_UI rate suitable for the user interface

不过这些不是十分准确,大多数时候还是需要我们在onSensorChanged(SensorEvent event)方法中通过时间做过滤才靠谱,摇一摇功能使用SENSOR_DELAY_UI就行。当然你也可以自己设定传感器监听事件触发频率,Android 2.3以上支持。

onSensorChanged(SensorEvent event)的处理

下面进入本文最为关键的部分,onSensorChanged(SensorEvent event)的处理。从上面可以看到,不同传感器的Listener都是同一个接口,包括两个方法:onSensorChanged(SensorEvent event),和onAccuracyChanged(Sensor sensor, int accuracy),我们只有大多数时候只用关心SensorEvent的处理即可。

/**
 * This class represents a {@link android.hardware.Sensor Sensor} event and
 * holds information such as the sensor's type, the time-stamp, accuracy and of
 * course the sensor's {@link SensorEvent#values data}.
 */

public class SensorEvent {
    public final float[] values;

    /**
     * The sensor that generated this event. See
     * {@link android.hardware.SensorManager SensorManager} for details.
     */
    public Sensor sensor;

    /**
     * The accuracy of this event. See {@link android.hardware.SensorManager
     * SensorManager} for details.
     */
    public int accuracy;

    /**
     * The time in nanosecond at which the event happened
     */
    public long timestamp;

    SensorEvent(int valueSize) {
        values = new float[valueSize];
    }
}

SensorEvent对象十分简单,不同的传感器的事件都一样,只是value数组不一样而已,因此我们主要在Listener当中处理传感器的数据即可。

加速度传感器的返回数据为X轴、Y轴和Z轴方向的加速度。现在网上大多数摇一摇的代码都是直接判断3个方向的加速度是否达到某一个阈值,如果达到那么触发摇一摇事件。这样的处理会就会出现之前讨论的问题,要么很容易触发、要么太难触发,导致功能白做。因此有必要对该方法进行更多,更细致的处理。

摇动事件拆解分析

简单的一次摇动手机的事件可以分解为:

  • 向某个方向加速运动然后速度达到最大
  • 减速到速度为零随后开始反向加速运动
  • 反向加速到最大之后,再减速到速度为零

一次简单的摇动可以粗略的分解上面三个步骤,其中加速度最开始为正向(此时为正向加速过程),随后加速度反向(对应减速和方向加速的过程),之后再次反向(对应反向运动减速到正向加速的过程)。可以看出来摇动手机的时候加速度不断在变化方向。变化的频率和我们晃动的频率正相关,同时两次加速方向应该反向。但是根据参数我们可以看出来,系统将加速度分解到三个方向,这样描述加速度就包含了方向信息在里面。因此对应两次加速度的夹角如果接近180度,那么说明加速度方向进行了一次转向,所以可以对应一次晃动。如果一切都如预想中一样,那么最为合理的代码应当是判断加速度反向,那么记录一次反向,在一定时间内完成设定次数的加速度反向,那么判定为一次摇动。判定反向用到了空间中两向量之间夹角的计算公式,最终的代码如下:

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
    if (sensorEvent.timestamp - mLastTimestamp < MIN_TIME_BETWEEN_SAMPLES_NS) {
        return;
    }
    float ax = sensorEvent.values[0];
    float ay = sensorEvent.values[1];
    float az = sensorEvent.values[2] - SensorManager.GRAVITY_EARTH;

    Log.e(TAG, "ACCELEROMETER: " + ax +"+++"+ ay+"+++"+az);

    if (Math.sqrt(ax*ax + ay*ay + az*az) > SENSOR_VALUE ){
//      Log.e(TAG, "ACCELEROMETER: " + ax +"+++"+ ay+"+++"+az);

        if (lastAz == 0 && lastAx == 0 && lastAy == 0){
            lastAy += ay;
            lastAz += az;
            lastAx += ax;
            return;
        }
        float product = ax*lastAx + ay*lastAy + az*lastAz;
        float length = (float) (Math.sqrt(ax*ax + ay*ay + az*az) * Math.sqrt(lastAx*lastAx + lastAy*lastAy + lastAz*lastAz));
        Log.e(TAG, "cos: "+ product/length);
        if(product/length < -0.9){//cosA == -1时表示反向
            Log.e(TAG, "cos: "+ product/length);
            Log.e(TAG, "ACCELEROMETER: " + Math.sqrt(ax*ax + ay*ay + az*az));
            Log.e(TAG, "ACCELEROMETER: " + ax +"+++"+ ay+"+++"+az);
            lastAz = az;
            lastAy = ay;
            lastAx = ax;
            recordShake(sensorEvent.timestamp);
        }
    }
    if (sensorEvent.timestamp - lastShakeTimestamp > SHAKING_TIME_WINDOW){
        reset();
    }
}

问题

理想很丰满,现实很骨感。由于手机的加速度传感器默认手机是水平放置在桌面的,此时Z轴减去重力加速度基本为零。但是用户使用手机的时候手机的方位往往不是水平的,此时就会造成上述方法的完全失效。如果再考虑什么手机的摆放方位,那么问题将会变得十分复杂,显得很没必要。因此大多数厂商采用了一种取巧的方式来实现,只要加速度传感器的三个方向中有任意一个方向反向,那么直接判定为一次加速度反向,在一段时间内加速度反向达到一定次数之后就判定为用户正在摇动手机。更改后的代码如下:

@Override
public void onSensorChanged(SensorEvent sensorEvent) {
    if (sensorEvent.timestamp - mLastTimestamp < MIN_TIME_BETWEEN_SAMPLES_NS) {
        return;
    }

    float ax = sensorEvent.values[0];
    float ay = sensorEvent.values[1];
    float az = sensorEvent.values[2] - SensorManager.GRAVITY_EARTH;

    mLastTimestamp = sensorEvent.timestamp;

    if (Math.abs(ax) > REQUIRED_FORCE && ax * lastAX <= 0) {
        recordShake(sensorEvent.timestamp);
        lastAX = ax;
    } else if (Math.abs(ay) > REQUIRED_FORCE && ay * lastAY <= 0) {
        recordShake(sensorEvent.timestamp);
        lastAY = ay;
    } else if (Math.abs(az) > REQUIRED_FORCE && az * lastAZ <= 0) {
        recordShake(sensorEvent.timestamp);
        lastAZ = az;
    }

    maybeShake(sensorEvent.timestamp);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 最近在搞一个自己的App,突发一个想法,给App加一个摇一摇随机查看图片的功能,这样可以使得用户在使用App的时候...
    _SOLID阅读 19,137评论 1 31
  • Android系统提供了对传感器的支持,如果手机设备的硬件提供了这些传感器,Android应用可以通过传感器来获取...
    trampcr阅读 4,304评论 3 12
  • 传感器学习 通过手机来感受温度、压力、重力和光线等。 在Android2.3 gingerbread系统中,goo...
    0a61c9729dbc阅读 852评论 0 3
  • Android传感器定义 Android 传感器相关术语微机电传感器(MEMS)MEMS 通常制作在规格很小的硅芯...
    Jannonx阅读 4,331评论 0 1
  • 种下关爱她人事业,连接家族能量,关爱她人等种子 一、 接妹妹到上班地点适应环境 今天头晕,找不到眼镜,但我还是在1...
    心灵陪伴阅读 220评论 0 0