Android ?搖一搖開發(fā)——靈敏度優(yōu)化

效果最好的在文章最后給出,不想看分析的直接取最后的代碼即可擒权。

Android端的搖一搖功能現(xiàn)在使用十分廣泛垮刹,從最開始微信憑借搖一搖的功能大火,到現(xiàn)在很多的APP都具備搖一搖的功能培他。因此搖一搖的開發(fā)也變得十分廣泛,網(wǎng)上隨手一查也能查到很多關(guān)于搖一搖的開發(fā)代碼遗座。搖一搖幾乎都是根據(jù)Android自帶傳感的加速度傳感器來實(shí)現(xiàn)的靶壮,檢測到手機(jī)的加速度,然后做出相應(yīng)的邏輯判斷即可完成搖一搖的判定员萍。

但是每個(gè)手機(jī)加速度傳感器之間是有一定差距的腾降,加上手機(jī)性能和手機(jī)的軟件之間的不同,所以導(dǎo)致相同的代碼在不同的手機(jī)上的體驗(yàn)有一定的差距碎绎。我們應(yīng)當(dāng)在不增加太多設(shè)計(jì)復(fù)雜的的基礎(chǔ)上螃壤,爭取讓不同手機(jī)之間的體驗(yàn)達(dá)到一個(gè)比較接近的狀態(tài)。

我體驗(yàn)了微信的搖一搖功能筋帖,微信的搖一搖現(xiàn)在比較容易觸發(fā)奸晴,幾乎只需要搖一次就可以觸發(fā),但是由于微信本身只有進(jìn)入搖一搖界面才會(huì)開啟搖一搖的功能日麸,因此用戶進(jìn)入該界面就是想要搖一搖的寄啼,因此將搖一搖設(shè)置的比較敏感也是符合產(chǎn)品使用場景的。但是有些應(yīng)用具備全局的搖一搖功能代箭,那么此時(shí)就不應(yīng)該設(shè)計(jì)的太容易觸發(fā)墩划,這樣就會(huì)變成知乎那樣,被各種吐槽了嗡综。

知乎搖一搖吐槽

如果單純通過增加加速度閾值來增加觸發(fā)難度乙帮,你會(huì)發(fā)現(xiàn)當(dāng)用戶真正想要搖一搖的時(shí)候會(huì)十分困難,可能就直接導(dǎo)致用戶不使用搖一搖功能极景,這樣意味著開發(fā)的這個(gè)功能完全失去了意義察净。這就需要我們從別的方向來解決這個(gè)問題了。

搖一搖的實(shí)現(xiàn)代碼

說了這么多盼樟,現(xiàn)在我們正式進(jìn)入搖一搖開發(fā)的實(shí)現(xiàn)氢卡。

上文也說道,現(xiàn)在的搖一搖都是通過Android的加速度傳感器來實(shí)現(xiàn)的晨缴。通過檢測加速译秦,來判斷用戶是否在進(jìn)行搖一搖操作。要獲取加速度傳感器的數(shù)據(jù)可以通過SensorManager的類來實(shí)現(xiàn)。

具體使用方法也比較簡單诀浪,就不細(xì)說直接給出一段簡單的代碼:

//獲取系統(tǒng)的SensorManager
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);

SensorEventListener listener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO: 添加自己的傳感器數(shù)據(jù)處理代碼;

    }

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

    }
};
//注冊傳感器監(jiān)聽事件
mSensorManager.registerListener(listener,
        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
        SensorManager.SENSOR_DELAY_UI);


//注銷傳感器監(jiān)聽
mSensorManager.unregisterListener(listener);

需要注意的是registerListener(listener,
? mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
? SensorManager.SENSOR_DELAY_UI);當(dāng)中的SENSOR_DELAY_UI參數(shù)延都,這個(gè)參數(shù)表示傳感器數(shù)據(jù)變化通知的頻率雷猪,如果過快會(huì)造成性能和電量的消耗。官方提供了四個(gè)標(biāo)準(zhǔn)的參數(shù)

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

不過這些不是十分準(zhǔn)確晰房,大多數(shù)時(shí)候還是需要我們在onSensorChanged(SensorEvent event)方法中通過時(shí)間做過濾才靠譜求摇,搖一搖功能使用SENSOR_DELAY_UI就行。當(dāng)然你也可以自己設(shè)定傳感器監(jiān)聽事件觸發(fā)頻率殊者,Android 2.3以上支持与境。

onSensorChanged(SensorEvent event)的處理

下面進(jìn)入本文最為關(guān)鍵的部分,onSensorChanged(SensorEvent event)的處理猖吴。從上面可以看到摔刁,不同傳感器的Listener都是同一個(gè)接口,包括兩個(gè)方法:onSensorChanged(SensorEvent event)海蔽,和onAccuracyChanged(Sensor sensor, int accuracy)共屈,我們只有大多數(shù)時(shí)候只用關(guān)心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數(shù)組不一樣而已,因此我們主要在Listener當(dāng)中處理傳感器的數(shù)據(jù)即可幌衣。

加速度傳感器的返回?cái)?shù)據(jù)為X軸矾削、Y軸和Z軸方向的加速度。現(xiàn)在網(wǎng)上大多數(shù)搖一搖的代碼都是直接判斷3個(gè)方向的加速度是否達(dá)到某一個(gè)閾值豁护,如果達(dá)到那么觸發(fā)搖一搖事件哼凯。這樣的處理會(huì)就會(huì)出現(xiàn)之前討論的問題,要么很容易觸發(fā)楚里、要么太難觸發(fā)挡逼,導(dǎo)致功能白做。因此有必要對該方法進(jìn)行更多腻豌,更細(xì)致的處理家坎。

搖動(dòng)事件拆解分析

簡單的一次搖動(dòng)手機(jī)的事件可以分解為:

  • 向某個(gè)方向加速運(yùn)動(dòng)然后速度達(dá)到最大
  • 減速到速度為零隨后開始反向加速運(yùn)動(dòng)
  • 反向加速到最大之后,再減速到速度為零

一次簡單的搖動(dòng)可以粗略的分解上面三個(gè)步驟吝梅,其中加速度最開始為正向(此時(shí)為正向加速過程)虱疏,隨后加速度反向(對應(yīng)減速和方向加速的過程),之后再次反向(對應(yīng)反向運(yùn)動(dòng)減速到正向加速的過程)苏携∽龅桑可以看出來搖動(dòng)手機(jī)的時(shí)候加速度不斷在變化方向。變化的頻率和我們晃動(dòng)的頻率正相關(guān),同時(shí)兩次加速方向應(yīng)該反向装蓬。但是根據(jù)參數(shù)我們可以看出來著拭,系統(tǒng)將加速度分解到三個(gè)方向,這樣描述加速度就包含了方向信息在里面牍帚。因此對應(yīng)兩次加速度的夾角如果接近180度儡遮,那么說明加速度方向進(jìn)行了一次轉(zhuǎn)向,所以可以對應(yīng)一次晃動(dòng)暗赶。如果一切都如預(yù)想中一樣鄙币,那么最為合理的代碼應(yīng)當(dāng)是判斷加速度反向,那么記錄一次反向蹂随,在一定時(shí)間內(nèi)完成設(shè)定次數(shù)的加速度反向十嘿,那么判定為一次搖動(dòng)。判定反向用到了空間中兩向量之間夾角的計(jì)算公式岳锁,最終的代碼如下:

@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時(shí)表示反向
            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();
    }
}

問題

理想很豐滿绩衷,現(xiàn)實(shí)很骨感。由于手機(jī)的加速度傳感器默認(rèn)手機(jī)是水平放置在桌面的激率,此時(shí)Z軸減去重力加速度基本為零唇聘。但是用戶使用手機(jī)的時(shí)候手機(jī)的方位往往不是水平的,此時(shí)就會(huì)造成上述方法的完全失效柱搜。如果再考慮什么手機(jī)的擺放方位迟郎,那么問題將會(huì)變得十分復(fù)雜,顯得很沒必要聪蘸。因此大多數(shù)廠商采用了一種取巧的方式來實(shí)現(xiàn)宪肖,只要加速度傳感器的三個(gè)方向中有任意一個(gè)方向反向,那么直接判定為一次加速度反向健爬,在一段時(shí)間內(nèi)加速度反向達(dá)到一定次數(shù)之后就判定為用戶正在搖動(dòng)手機(jī)控乾。更改后的代碼如下:

@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);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市娜遵,隨后出現(xiàn)的幾起案子蜕衡,更是在濱河造成了極大的恐慌,老刑警劉巖设拟,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慨仿,死亡現(xiàn)場離奇詭異,居然都是意外死亡纳胧,警方通過查閱死者的電腦和手機(jī)镰吆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跑慕,“玉大人万皿,你說我怎么就攤上這事摧找。” “怎么了牢硅?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵蹬耘,是天一觀的道長。 經(jīng)常有香客問我减余,道長综苔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任佳励,我火速辦了婚禮休里,結(jié)果婚禮上蛆挫,老公的妹妹穿的比我還像新娘赃承。我一直安慰自己,他們只是感情好悴侵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布瞧剖。 她就那樣靜靜地躺著,像睡著了一般可免。 火紅的嫁衣襯著肌膚如雪抓于。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天浇借,我揣著相機(jī)與錄音捉撮,去河邊找鬼。 笑死妇垢,一個(gè)胖子當(dāng)著我的面吹牛巾遭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闯估,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼灼舍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涨薪?” 一聲冷哼從身側(cè)響起骑素,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刚夺,沒想到半個(gè)月后献丑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侠姑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年阳距,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结借。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筐摘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咖熟,我是刑警寧澤圃酵,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站馍管,受9級特大地震影響郭赐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜确沸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一捌锭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罗捎,春花似錦观谦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倒得,卻和暖如春泻红,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霞掺。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工谊路, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菩彬。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓缠劝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挤巡。 傳聞我的和親對象是個(gè)殘疾皇子剩彬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 最近在搞一個(gè)自己的App,突發(fā)一個(gè)想法矿卑,給App加一個(gè)搖一搖隨機(jī)查看圖片的功能喉恋,這樣可以使得用戶在使用App的時(shí)候...
    _SOLID閱讀 19,135評論 1 31
  • Android系統(tǒng)提供了對傳感器的支持,如果手機(jī)設(shè)備的硬件提供了這些傳感器母廷,Android應(yīng)用可以通過傳感器來獲取...
    trampcr閱讀 4,299評論 3 12
  • 傳感器學(xué)習(xí) 通過手機(jī)來感受溫度轻黑、壓力、重力和光線等琴昆。 在Android2.3 gingerbread系統(tǒng)中氓鄙,goo...
    0a61c9729dbc閱讀 852評論 0 3
  • Android傳感器定義 Android 傳感器相關(guān)術(shù)語微機(jī)電傳感器(MEMS)MEMS 通常制作在規(guī)格很小的硅芯...
    Jannonx閱讀 4,323評論 0 1
  • 種下關(guān)愛她人事業(yè),連接家族能量业舍,關(guān)愛她人等種子 一抖拦、 接妹妹到上班地點(diǎn)適應(yīng)環(huán)境 今天頭暈升酣,找不到眼鏡,但我還是在1...
    心靈陪伴閱讀 219評論 0 0