Android手機(jī)屏幕敲擊解鎖功能代碼
1.前言
現(xiàn)在市面上有不少Android手機(jī)支持敲擊屏幕解鎖,敲擊屏幕解鎖是一項(xiàng)很實(shí)用的功能,但一來(lái)只支持敲擊屏幕,二來(lái)只能用于解鎖或鎖屏,再者我們應(yīng)用層的開(kāi)發(fā)者切不進(jìn)去,完全無(wú)法玩起來(lái)。開(kāi)發(fā)者,開(kāi)發(fā)者,我們既然身為開(kāi)發(fā)者何不搞點(diǎn)大新聞,那么這次我來(lái)教教各位如何用代碼來(lái)實(shí)現(xiàn)手機(jī)的敲擊識(shí)別,聽(tīng)起來(lái)是不是很有趣,有些躍躍欲試呢。事實(shí)上在ios上已經(jīng)有實(shí)現(xiàn)這個(gè)功能的應(yīng)用:Knock,一款敲擊來(lái)解鎖Mac電腦的應(yīng)用,售價(jià)4.99美元,約為33人民幣。有時(shí)候真想去做ios開(kāi)發(fā),可以開(kāi)心的為自己的應(yīng)用定價(jià),愉悅的掙外快。言歸正傳,既然ios可以實(shí)現(xiàn),那我們Android自然不能落伍,現(xiàn)在我就帶領(lǐng)大家來(lái)用代碼實(shí)現(xiàn)手機(jī)的敲擊識(shí)別吧。
本文以Java為示例語(yǔ)言,以Android為示例平臺(tái)。
2.功能實(shí)現(xiàn)
2.1.實(shí)現(xiàn)思路
說(shuō)到敲擊識(shí)別,你們會(huì)考慮使用什么來(lái)實(shí)現(xiàn)呢,傳感器?對(duì),沒(méi)錯(cuò),作為手機(jī)手勢(shì)姿態(tài)識(shí)別的唯一途徑,我們自然需要使用傳感器來(lái)實(shí)現(xiàn)對(duì)敲擊的識(shí)別,但Android傳感器種類繁多,我們應(yīng)該選擇哪一個(gè)呢?
在Android2.3的時(shí)代,Android系統(tǒng)就已經(jīng)定義了11個(gè)傳感器,到了現(xiàn)在Android6.0的時(shí)代,系統(tǒng)定義的傳感器數(shù)目已經(jīng)達(dá)到26個(gè),這么多傳感器我們到底用哪一個(gè)呢,事實(shí)上我們只需要考慮2.3時(shí)代提供的那11個(gè)傳感器即可,因?yàn)橐环矫婧笃诩尤氲膫鞲衅鞑糠秩缧奶鴤鞲衅鞯刃枰布С?,?dǎo)致很多手機(jī)無(wú)法支持此類傳感器,另一方面2.3時(shí)代的11個(gè)傳感器功能已經(jīng)相當(dāng)強(qiáng)大,可以支持絕大多數(shù)手勢(shì)姿態(tài)的識(shí)別,那么現(xiàn)在我來(lái)列舉一下上述11個(gè)傳感器:
SENSOR_TYPE_ACCELEROMETER 加速度 SENSOR_TYPE_MAGNETIC_FIELD 磁力 SENSOR_TYPE_ORIENTATION 方向 SENSOR_TYPE_GYROSCOPE 陀螺儀 SENSOR_TYPE_LIGHT 光線感應(yīng) SENSOR_TYPE_PRESSURE 壓力 SENSOR_TYPE_TEMPERATURE 溫度 SENSOR_TYPE_PROXIMITY 接近 SENSOR_TYPE_GRAVITY 重力 SENSOR_TYPE_LINEAR_ACCELERATION 線性加速度 SENSOR_TYPE_ROTATION_VECTOR 旋轉(zhuǎn)矢量
關(guān)于這11個(gè)傳感器的詳細(xì)描述,各位可以去http://www.dbjr.com.cn/article/87940.htm查看,事實(shí)上我一直懷疑LG G3的敲擊解鎖與光線傳感器或接近傳感器有關(guān),因?yàn)槲矣檬种笐腋≡贚G G3的頭部正上方時(shí)一直無(wú)法敲擊解鎖,移開(kāi)后恢復(fù)正常,而敲擊鎖屏應(yīng)該只和觸摸屏相關(guān),因?yàn)闊o(wú)論我怎么遮擋傳感器,敲擊鎖屏的功能完全不受影響。
言歸正傳,對(duì)這11個(gè)傳感器有所了解后,我們需要選擇哪個(gè)或哪些傳感器來(lái)實(shí)現(xiàn)功能呢,我們來(lái)模擬一下手機(jī)敲擊的情況,將手機(jī)平放在桌面上,手指敲擊手機(jī)的時(shí)候,手指給了手機(jī)一個(gè)力,同時(shí)桌面給予手機(jī)一個(gè)反作用力,考慮桌面不形變的情況下,手機(jī)受力平衡加速度為0,但這時(shí)手機(jī)的加速度傳感器數(shù)據(jù)是否會(huì)有變化呢,答案是會(huì)的,手機(jī)加速度傳感器的數(shù)據(jù)會(huì)有一段短暫但明顯的變化,為什么呢,手機(jī)受力平衡加速度為0是因?yàn)樗且粋€(gè)整體,但內(nèi)部構(gòu)件還是會(huì)受到相互之間復(fù)雜的力的左右,并非受力的同時(shí)就達(dá)到受力平衡的,其實(shí)換個(gè)思路。用一個(gè)和手機(jī)形狀相似內(nèi)部光滑的容器,容器里面放幾個(gè)玻璃球,敲擊幾下,容器不會(huì)移動(dòng),但玻璃球是不是移動(dòng)了呢。雖然手機(jī)內(nèi)部的構(gòu)件遠(yuǎn)比玻璃球穩(wěn)定,但也得遵循基本法,老老實(shí)實(shí)接受力的作用。
上述場(chǎng)景是平放于桌面的場(chǎng)景,實(shí)際生活的場(chǎng)景往往更加復(fù)雜多樣,但無(wú)論處于哪種場(chǎng)景,毫無(wú)疑問(wèn)對(duì)手機(jī)的敲擊操作都應(yīng)該導(dǎo)致加速度傳感器傳出數(shù)據(jù)的明顯變化,那么我們現(xiàn)在就明白了應(yīng)該選擇什么傳感器作為我們敲擊識(shí)別的工具了吧,但加速度相關(guān)的傳感器有兩個(gè),加速度傳感器和線性加速度傳感器,我們應(yīng)該選擇哪一個(gè)呢,加速度傳感器提供的數(shù)據(jù)是重力影響下的手機(jī)加速度,線性加速傳感器提供的數(shù)據(jù)是排除重力影響的手機(jī)加速度,可以直觀的反映排除重力后手機(jī)的受力情況,很合適用以敲擊識(shí)別,那我們是否就應(yīng)該選擇線性加速度傳感器呢,恰恰相反,我們要選擇加速度傳感器,Android提供的線性加速度傳感器基于軟件的,不同平臺(tái)對(duì)于線性加速傳感器的處理未必相同,事實(shí)上,在敲擊三星S4,LG G3中一款機(jī)型的背面,就出現(xiàn)線性加速度傳感器傳出的數(shù)據(jù)沒(méi)有較大變化的情況,保險(xiǎn)起見(jiàn),我們還是選用基于硬件的加速度傳感器更合適一些。順便吐槽一句,當(dāng)時(shí)看到壓力傳感器的時(shí)候,我還以為監(jiān)測(cè)作用于手機(jī)的壓力的傳感器,那無(wú)疑是很適合用于識(shí)別敲擊,后面看到描述才知道是監(jiān)測(cè)壓強(qiáng)的。
如上所說(shuō),對(duì)手機(jī)的敲擊操作會(huì)導(dǎo)致加速度傳感器傳出數(shù)據(jù)的明顯變化,故而本次功能實(shí)現(xiàn)中,判斷是否有敲擊操作的方法是檢測(cè)手機(jī)線性加速度相比正常情況是否有明顯變化。在功能實(shí)現(xiàn)過(guò)程中為排除重力的影響,需要對(duì)加速度傳感器的數(shù)據(jù)進(jìn)行處理將其轉(zhuǎn)化為線性加速度,因?yàn)檗D(zhuǎn)化為線性加速度是一個(gè)需要校準(zhǔn)的過(guò)程,所以需要先投入一定數(shù)目的數(shù)據(jù)用于校準(zhǔn)以獲得更精確的線性加速度,同時(shí)考慮到現(xiàn)實(shí)生活存在可能導(dǎo)致誤識(shí)別的場(chǎng)景,比如搖動(dòng)手機(jī)會(huì)帶給手機(jī)一個(gè)較長(zhǎng)時(shí)間且明顯的線性加速度變化,所以提出穩(wěn)態(tài)的概念,將手機(jī)處于相對(duì)穩(wěn)定,沒(méi)有長(zhǎng)時(shí)間出現(xiàn)明顯線性加速變化的狀況視為穩(wěn)態(tài),在穩(wěn)態(tài)的情況下才會(huì)進(jìn)行對(duì)敲擊的識(shí)別,另外此次敲擊識(shí)別考慮到對(duì)手機(jī)邊框的敲擊使用可能性過(guò)低,因此僅考慮識(shí)別對(duì)手機(jī)屏幕或背面的敲擊,這樣在識(shí)別的過(guò)程中可忽略X,Y軸的數(shù)據(jù),僅考慮Z軸的線性加速度。
2.2.功能簡(jiǎn)介
本次實(shí)現(xiàn)的功能是識(shí)別對(duì)手機(jī)屏幕或背面的敲擊操作,功能實(shí)現(xiàn)流程: 注冊(cè)傳感器,采集數(shù)據(jù),投入指定數(shù)目的數(shù)據(jù)校準(zhǔn)以獲取較精準(zhǔn)的線性加速度,校準(zhǔn)結(jié)束后判斷當(dāng)前是否穩(wěn)態(tài),如果為非穩(wěn)態(tài),則等待下次數(shù)據(jù),如果為穩(wěn)態(tài),則調(diào)用方法判斷是否存在敲擊操作,在進(jìn)行敲擊識(shí)別的同時(shí)也將處理得到的線性加速度和最近敲擊次數(shù),穩(wěn)態(tài)狀態(tài)顯示到界面上去,
2.3.功能實(shí)現(xiàn)
2.3.1.獲取傳感器數(shù)據(jù)
注冊(cè)傳感器的方法屬于系統(tǒng)原生的方法,就不過(guò)多講解,不過(guò)需要注意一點(diǎn),在注冊(cè)加速度傳感器時(shí)標(biāo)識(shí)傳感器數(shù)據(jù)采樣間隔的參數(shù)最好使用SENSOR_DELAY_GAME,因?yàn)榍脫魧?dǎo)致的加速度數(shù)據(jù)變化很短暫,如果使用SENSOR_DELAY_UI或SENSOR_DELAY_NORMAL往往采集不到敲擊引發(fā)的加速度變化,當(dāng)然如果使用SENSOR_DELAY_FASTEST自然不會(huì)有這個(gè)問(wèn)題,但性能消耗會(huì)比較大。
注冊(cè)傳感器后就可以在回調(diào)方法里等待處理數(shù)據(jù), 下面我給出實(shí)現(xiàn)代碼,綜合代碼講解實(shí)現(xiàn)過(guò)程。
public void onSensorChanged(SensorEvent sensorEvent) { if (sensorEvent.sensor == null) { return; } if (sensorEvent.sensor.getType() == accelerometerSensorType) { float accelerationZ = sensorEvent.values[2]; if (accelerationZ > 0) { recognitionKnockRatio = 20; recognitionUniqueRatio = 10; smoothSectionMaxRatio = 5f; } else { recognitionKnockRatio = 7.5f; recognitionUniqueRatio = 6; smoothSectionMaxRatio = 2.5f; } gravityZ = alpha * gravityZ + (1 - alpha) * accelerationZ; linearAccelerationZ = accelerationZ - gravityZ; if (calibrateLinearAcceleration) { calibrateLinearAccelerationIndex++; if (calibrateLinearAccelerationIndex <= calibrateLinearAccelerationSectionNumber) { return; } calibrateLinearAcceleration = false; } if (sensorDataShowIndex >= sensorDataShowNumber) { sensorDataShowIndex = sensorDataShowNumber - sensorDataShowDurationNumber; Iterator<?> it = linearAccelerationZShowList.listIterator(0); for (int i = 0; i < sensorDataShowDurationNumber; i++) { it.next(); it.remove(); } MainActivity.UpdateSensorData(linearAccelerationZShowList); } linearAccelerationZShowList.add(linearAccelerationZ); sensorDataShowIndex++; if (!stable) { linearAccelerationZList.add(linearAccelerationZ); if (linearAccelerationZList.size() >= stableSectionNumber) { stableRecognition(); linearAccelerationZList.clear(); } return; } knockRecognition(linearAccelerationZ); } }
傳感器數(shù)據(jù)回調(diào)的方法中對(duì)加速度傳感器獲取的數(shù)據(jù)分別進(jìn)行了處理,首先,根據(jù)z軸加速度的正負(fù),為recognitionKnockRatio,recognitionUniqueRatio,smoothSectionMaxRatio三個(gè)變量賦予不同的數(shù)值,至于為什么要進(jìn)行這樣處理,是因?yàn)閷?duì)Android手機(jī)實(shí)際進(jìn)行敲擊操作發(fā)現(xiàn),加速度傳感器對(duì)正面敲擊操作反饋敏感,對(duì)背面敲擊操作反饋相對(duì)遲鈍,反饋到數(shù)據(jù)層面就是,敲擊正面導(dǎo)致的加速度傳感器數(shù)據(jù)變化相比敲擊背面明顯很多,故而針對(duì)敲擊屏幕和敲擊背面要分配不同的數(shù)值,然而事實(shí)上站在手機(jī)的角度,運(yùn)用現(xiàn)在的數(shù)據(jù)是完全無(wú)法分析敲擊操作導(dǎo)致的加速度明顯變化來(lái)源于敲擊正面還是敲擊背面,所以就使用z軸加速度的正負(fù)來(lái)簡(jiǎn)單判斷,畢竟絕大多數(shù)情況下z軸加速度為正,那就是手機(jī)背面偏向地面,用戶更可能敲擊手機(jī)屏幕,而為負(fù)就是手機(jī)屏幕偏向地面,用戶更可能敲擊手機(jī)背面。至于導(dǎo)致敲擊屏幕和敲擊背面加速度傳感器反饋敏感程度不同這種情況的原因不外乎兩個(gè),一是加速度傳感器相比于背面距離屏幕更近,再者就是Android手機(jī)外殼的問(wèn)題了,這一點(diǎn)在LG G3上尤為明顯,LG G3的是有一定弧度的塑料外殼,在背面敲擊引發(fā)的傳感器數(shù)據(jù)變化相比于敲擊屏幕要低很多,而金屬外殼的三星S6,在背面敲擊引發(fā)的傳感器數(shù)據(jù)變化接近于敲擊屏幕。事實(shí)上上述三個(gè)系數(shù)屬于經(jīng)驗(yàn)系數(shù),并且對(duì)于不同類型手機(jī)盡量提供不同的數(shù)值,原因可參見(jiàn)剛才所說(shuō)的LG G3和三星S6,再一次感慨Android手機(jī)的多樣性,Android手機(jī)種類太多,硬件設(shè)計(jì)的不同導(dǎo)致在一款手機(jī)上適用的系數(shù)在另一款手機(jī)上可能完全無(wú)法適用,要是如iphone一樣只有那幾款機(jī)型的話無(wú)疑好處理很多。
接著對(duì)加速度進(jìn)行濾波處理以獲取線性加速度,獲取線性加速度的方法參考了Android SensorEvent源碼中建議的方法:
* <p> * It should be apparent that in order to measure the real acceleration of * the device, the contribution of the force of gravity must be eliminated. * This can be achieved by applying a <i>high-pass</i> filter. Conversely, a * <i>low-pass</i> filter can be used to isolate the force of gravity. * </p> * * <pre class="prettyprint"> * * public void onSensorChanged(SensorEvent event) * { * // alpha is calculated as t / (t + dT) * // with t, the low-pass filter's time-constant * // and dT, the event delivery rate * * final float alpha = 0.8; * * gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]; * gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]; * gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]; * * linear_acceleration[0] = event.values[0] - gravity[0]; * linear_acceleration[1] = event.values[1] - gravity[1]; * linear_acceleration[2] = event.values[2] - gravity[2]; * } * </pre>
通過(guò)高通濾波和低通濾波對(duì)加速度進(jìn)行處理排除重力影響以獲取線性加速度,但在此過(guò)程中是需要傳入一定數(shù)量的數(shù)據(jù)進(jìn)行校準(zhǔn)以獲取較精準(zhǔn)的線性加速度,在這里我們?cè)O(shè)定calibrateLinearAccelerationSectionNumber作為用以校準(zhǔn)數(shù)據(jù)的數(shù)據(jù)長(zhǎng)度,用calibrateLinearAccelerationIndex和calibrateLinearAcceleration來(lái)控制何時(shí)校準(zhǔn)結(jié)束。
校準(zhǔn)結(jié)束后使用linearAccelerationZShowList存儲(chǔ)顯示到應(yīng)用界面上的傳感器線性加速度,接著如果處于非穩(wěn)態(tài),則開(kāi)始穩(wěn)態(tài)識(shí)別,判斷當(dāng)前狀態(tài)是否穩(wěn)態(tài),如果處于穩(wěn)態(tài)狀態(tài)則開(kāi)始敲擊識(shí)別。
2.3.2.穩(wěn)態(tài)識(shí)別
如上文提到的,用戶如果進(jìn)行搖動(dòng)手機(jī)之類的操作,是會(huì)產(chǎn)生明顯的加速度變化,很有可能導(dǎo)致誤識(shí)別的情況,所以在此提出了穩(wěn)態(tài)的概念,即為手機(jī)加速度沒(méi)有長(zhǎng)時(shí)間明顯變化的狀態(tài),延伸到現(xiàn)實(shí)場(chǎng)景就是用戶沒(méi)有對(duì)手機(jī)進(jìn)行明顯移動(dòng)的狀態(tài),嚴(yán)格來(lái)說(shuō),一般用戶在對(duì)手機(jī)進(jìn)行明顯移動(dòng)如搖動(dòng)手機(jī)的同時(shí)進(jìn)行敲擊操作的可能性極低,所以可以將穩(wěn)態(tài)這個(gè)概念正式運(yùn)用到功能實(shí)現(xiàn)中。
已經(jīng)了解穩(wěn)態(tài)這個(gè)概念,那我們應(yīng)該如何定義什么情況屬于穩(wěn)態(tài),什么情況屬于非穩(wěn)態(tài),下面我給出實(shí)現(xiàn)代碼,綜合代碼進(jìn)行講解。
private void stableRecognition() { int exceptionNumber = 0; float accelerationZValue; float minAccelerationZValue = Integer.MAX_VALUE; float maxAccelerationZValue = Integer.MIN_VALUE; for (int i = stableSectionNumber - 1; i >= 0; i--) { accelerationZValue = linearAccelerationZList.get(i); if (Math.abs(accelerationZValue) > maxStableOffset) { exceptionNumber++; } else { if (accelerationZValue > maxAccelerationZValue) { maxAccelerationZValue = accelerationZValue; } else { if (accelerationZValue < minAccelerationZValue) { minAccelerationZValue = accelerationZValue; } } } } stable = exceptionNumber <= maxExceptionNumber; if (stable) { if (linearAccelerationZStableSection == 0) { linearAccelerationZStableSection = (maxAccelerationZValue - minAccelerationZValue) / 2; } if (linearAccelerationZStableSection > maxStableOffset) { linearAccelerationZStableSection = maxStableOffset; } } MainActivity.UpdateStable(stable); LogFunction.log("stable", "" + stable); LogFunction.log("exceptionNumber", "" + exceptionNumber); LogFunction.log("linearAccelerationZStableSection", "" + linearAccelerationZStableSection); }
在此次功能實(shí)現(xiàn)過(guò)程中,判斷穩(wěn)態(tài)的方式是采樣50個(gè)點(diǎn),然后計(jì)算每個(gè)點(diǎn)的絕對(duì)值,如果大于最大偏差maxStableOffset就視為異常點(diǎn),異常點(diǎn)大于最大異常點(diǎn)數(shù)目maxExceptionNumber就視為非穩(wěn)態(tài),反之視為穩(wěn)態(tài)。判斷穩(wěn)態(tài)結(jié)束后,如果處于穩(wěn)態(tài)則將剔除異常點(diǎn)數(shù)據(jù)后的Z軸最大加速度和最小加速度之間差值的一半視為波動(dòng)區(qū)間linearAccelerationZStableSection。maxStableOffset與maxExceptionNumber相同都是經(jīng)驗(yàn)系數(shù),是對(duì)Android手機(jī)實(shí)際提供的不同場(chǎng)景下的線性加速度分析得出的?,F(xiàn)在存在一個(gè)問(wèn)題,那就是如果原本狀態(tài)處于穩(wěn)態(tài),然后用戶突然對(duì)手機(jī)進(jìn)行操作,將手機(jī)狀態(tài)轉(zhuǎn)變?yōu)榉欠€(wěn)態(tài)那要如何處理,不要著急,這個(gè)問(wèn)題會(huì)在敲擊識(shí)別的過(guò)程中進(jìn)行處理的。
2.3.3.敲擊識(shí)別
現(xiàn)在到了整個(gè)功能實(shí)現(xiàn)最核心的地方:敲擊識(shí)別,如上文所說(shuō)敲擊會(huì)引起加速度傳感器數(shù)據(jù)的明顯變化,但是我們要如何使用代碼進(jìn)行檢測(cè)敲擊,以及如何排除用戶對(duì)手機(jī)其他操作引發(fā)的誤識(shí)別問(wèn)題,事實(shí)上這些問(wèn)題都會(huì)在這里進(jìn)行處理,現(xiàn)在我給出實(shí)現(xiàn)代碼,綜合代碼進(jìn)行講解。
private void knockRecognition(float linearAccelerationZ) { float linearAccelerationZAbsolute = Math.abs(linearAccelerationZ); float linearAccelerationZAbsoluteRadio = linearAccelerationZAbsolute / linearAccelerationZStableSection; if (linearAccelerationZAbsoluteRadio > recognitionUniqueRatio) { uniqueLinearAccelerationZList.add(linearAccelerationZ); currentForecastNumber = forecastNumber; } else { if (uniqueLinearAccelerationZList.size() > 0) { if (currentForecastNumber > 0) { currentForecastNumber--; } else { handleUniqueLinearAccelerationZ(); } } } if (linearAccelerationZAbsoluteRadio < smoothSectionMaxRatio) { float offsetWeight = 0.001f; linearAccelerationZStableSection = weightedMean(offsetWeight, linearAccelerationZAbsolute, linearAccelerationZStableSection); } }
knockRecognition就是用來(lái)處理線性加速度進(jìn)而確認(rèn)是否有敲擊操作的方法,首先對(duì)傳入?yún)?shù)線性加速度進(jìn)行處理,獲取線性加速度絕對(duì)值,接著如果線性加速度絕對(duì)值與波動(dòng)區(qū)間的比值大于recognitionUniqueRatio,那就認(rèn)為手機(jī)正在受到力的作用,為確定是敲擊操作還是用戶其他操作,先將線性加速度加入到獨(dú)特線性加速度列表中, 反之如果小于等于recognitionUniqueRatio,那就認(rèn)為手機(jī)處于相對(duì)穩(wěn)定狀態(tài),在此時(shí)如果此時(shí)獨(dú)特線性加速度列表長(zhǎng)度大于0,如果currentForecastNumber大于0,則currentForecastNumber減1,如果currentForecastNumber小于等于0,則開(kāi)始處理獨(dú)特線性加速度列表,而在處理獨(dú)特線性加速度列表的過(guò)程中正式開(kāi)始識(shí)別是否敲擊,以及當(dāng)前狀態(tài)是否轉(zhuǎn)變?yōu)榉欠€(wěn)態(tài)。在進(jìn)行上述操作的同時(shí),如果線性加速度絕對(duì)值與波動(dòng)區(qū)間的比值小于smoothSectionMaxRatio則用線性加速度絕對(duì)值來(lái)平滑波動(dòng)區(qū)間。
在這里,大家肯定對(duì)currentForecastNumber有疑問(wèn),這個(gè)變量代表什么含義,為什么會(huì)有這個(gè)變量,原因是這樣的,一次敲擊可能導(dǎo)致兩個(gè)接近但不連續(xù)的獨(dú)特線性加速度。如果沒(méi)有currentForecastNumber這個(gè)變量就會(huì)導(dǎo)致現(xiàn)實(shí)的一次敲擊可能被識(shí)別為兩次敲擊操作。
而如果線性加速度絕對(duì)值與波動(dòng)區(qū)間的比值小于smoothOffsetMaxRatio則用線性加速度絕對(duì)值來(lái)平滑波動(dòng)區(qū)間,是因?yàn)橐环矫媸謾C(jī)的狀態(tài)可能隨時(shí)改變,波動(dòng)區(qū)間應(yīng)該隨著手機(jī)狀態(tài)的改變跟著改變,另一方面穩(wěn)態(tài)識(shí)別時(shí)計(jì)算的波動(dòng)區(qū)間可能存在問(wèn)題,并不能正確的反映當(dāng)前手機(jī)的加速度波動(dòng),這個(gè)時(shí)候就需要根據(jù)最新的數(shù)據(jù)進(jìn)行學(xué)習(xí)以平滑波動(dòng)區(qū)間,而為什么比值要小于smoothSectionMaxRatio是因?yàn)楸戎荡笥趕moothSectionMaxRatio的基本是非正常情況的線性加速度,不適合用于平滑波動(dòng)區(qū)間,而如果現(xiàn)實(shí)情況中的線性加速度與波動(dòng)區(qū)間比值基本都超過(guò)smoothSectionMaxRatio,那說(shuō)明現(xiàn)在手機(jī)多半處于非穩(wěn)態(tài)狀態(tài)了,等待新的穩(wěn)態(tài)識(shí)別重置波動(dòng)區(qū)間即可,另外如上文所說(shuō),recognitionUniqueRatio,smoothOffsetMaxRatio屬于經(jīng)驗(yàn)系數(shù),完全可以自主設(shè)定。
private void handleUniqueLinearAccelerationZ() { LogFunction.log("linearAccelerationZStableSection", "" + linearAccelerationZStableSection); int recognitionKnockNumber = 1; int uniqueLinearAccelerationZListLength = uniqueLinearAccelerationZList.size(); float accelerationZOffsetAbsolute; float maxAccelerationZOffsetAbsolute = 0; for (int i = 0; i < uniqueLinearAccelerationZListLength; i++) { accelerationZOffsetAbsolute = Math.abs(uniqueLinearAccelerationZList.get(i)); if (maxAccelerationZOffsetAbsolute < accelerationZOffsetAbsolute) { maxAccelerationZOffsetAbsolute = accelerationZOffsetAbsolute; } LogFunction.log("uniqueLinearAccelerationZList index" + i, "" + uniqueLinearAccelerationZList.get(i)); } uniqueLinearAccelerationZList.clear(); LogFunction.log("uniqueLinearAccelerationZListLength", "" + uniqueLinearAccelerationZListLength); if (uniqueLinearAccelerationZListLength > unstableListLength) { stable = false; MainActivity.UpdateStable(stable); return; } LogFunction.log("maxAccelerationZOffsetAbsolute / linearAccelerationZStableSection", "" + (maxAccelerationZOffsetAbsolute / linearAccelerationZStableSection)); if (maxAccelerationZOffsetAbsolute > linearAccelerationZStableSection * recognitionKnockRatio) { LogFunction.log("recognitionKnockRatio", "" + recognitionKnockRatio); LogFunction.log("recognitionUniqueRatio", "" + recognitionUniqueRatio); knockRecognitionSuccess(recognitionKnockNumber); } }
終于到了最后的handleUniqueLinearAccelerationZ方法,顧名思義,就是用來(lái)處理獨(dú)特線性加速度列表的,在這個(gè)方法內(nèi),進(jìn)行了敲擊識(shí)別和穩(wěn)態(tài)狀態(tài)是否轉(zhuǎn)變的判定,如果獨(dú)特線性加速度列表長(zhǎng)度超過(guò)非穩(wěn)態(tài)獨(dú)特線性加速度列表長(zhǎng)度,則認(rèn)為現(xiàn)在手機(jī)狀態(tài)此刻狀態(tài)轉(zhuǎn)變?yōu)榉欠€(wěn)態(tài)并結(jié)束方法,如果發(fā)現(xiàn)加速度偏移數(shù)據(jù)列表中最大偏移值超過(guò)波動(dòng)區(qū)間一定倍數(shù)則識(shí)別為敲擊。
3.總結(jié)
至此,敲擊識(shí)別的流程我們算是走完了。事實(shí)上我提供的敲擊識(shí)別方法還是存在著誤識(shí)別的情況,ios的Knock我使用過(guò),擁有著符合價(jià)格的能力,識(shí)別率相當(dāng)?shù)暮?,不知道他們是通過(guò)機(jī)器學(xué)習(xí)還是別的方法歸結(jié)了一套他們的識(shí)別系數(shù),當(dāng)然我在此提供的敲擊識(shí)別僅僅是一種敲擊識(shí)別的方法,我也無(wú)法說(shuō)它成熟,因?yàn)椴](méi)有經(jīng)過(guò)真正用戶的考驗(yàn),大家完全可以按照自己的思想更換算法甚至更換傳感器來(lái)實(shí)現(xiàn)自己的敲擊識(shí)別,而我在此其實(shí)相當(dāng)于提供一個(gè)實(shí)現(xiàn)思路。
這是第三篇博客了,第一篇博客屬于試水就選擇了做過(guò)的一個(gè)比較偏門但并不好處理的一個(gè)小模塊:為MP3文件寫(xiě)入ID3標(biāo)簽,第二篇博客選擇了一個(gè)很嚴(yán)謹(jǐn)?shù)膶?shí)用模塊:音頻合成,前兩個(gè)模塊都有一個(gè)共同點(diǎn)就是各種規(guī)范已經(jīng)很明確,雖然代碼實(shí)現(xiàn)上可能有所不同但實(shí)現(xiàn)思路必然相同,而第三篇的博客的敲擊檢測(cè)無(wú)疑寬松很多,所以我也是第一次寫(xiě)了實(shí)現(xiàn)思路這一小節(jié),因?yàn)槲乙膊淮_定我的實(shí)現(xiàn)思路是否完全正確,作為傳感器的實(shí)際應(yīng)用是存在著無(wú)數(shù)的可能性,我們完全可以按照自己的想法去嘗試,錯(cuò)了大不了換一個(gè)方向罷了。
以上所述是小編給大家介紹的Android手機(jī)屏幕敲擊解鎖實(shí)現(xiàn)代碼,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Android中系統(tǒng)自帶鎖WalkLock與KeyguardLock用法實(shí)例詳解
- android圖庫(kù)播放幻燈片時(shí)按power鍵滅屏再亮屏顯示keyguard
- Android喚醒、解鎖屏幕代碼實(shí)例
- Android點(diǎn)亮屏幕或屏幕解鎖和鎖定以及其他相關(guān)權(quán)限實(shí)現(xiàn)代碼
- Android編程實(shí)現(xiàn)禁止系統(tǒng)鎖屏與解鎖亮屏的方法
- Android 監(jiān)聽(tīng)鎖屏、解鎖、開(kāi)屏 功能代碼
- Android中判斷屏幕是否亮屏和是否解鎖功能
- Android 仿小米鎖屏實(shí)現(xiàn)九宮格解鎖功能(無(wú)需圖片資源)
- Android編程實(shí)現(xiàn)一鍵鎖屏的方法
- Android4.0開(kāi)發(fā)之Keyguard解鎖屏機(jī)制詳解
相關(guān)文章
Android監(jiān)聽(tīng)來(lái)電和去電的實(shí)現(xiàn)方法
這篇文章主要介紹了Android監(jiān)聽(tīng)來(lái)電和去電的實(shí)現(xiàn)方法,涉及Android中BroadcastReceiver組件的使用及AndroidManifest.xml權(quán)限操作的相關(guān)技巧,需要的朋友可以參考下2016-08-08Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案
這篇文章主要為大家詳細(xì)介紹了Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android開(kāi)發(fā)中使用WebView控件瀏覽網(wǎng)頁(yè)的方法詳解
這篇文章主要介紹了Android開(kāi)發(fā)中使用WebView控件瀏覽網(wǎng)頁(yè)的方法,結(jié)合實(shí)例形式較為詳細(xì)的總結(jié)分析了Android WebView控件的功能、布局、設(shè)置、常用方法及相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Android開(kāi)發(fā)之資源文件用法實(shí)例總結(jié)
這篇文章主要介紹了Android開(kāi)發(fā)之資源文件用法,結(jié)合實(shí)例形式總結(jié)分析了Android開(kāi)發(fā)過(guò)程中針對(duì)資源文件的常見(jiàn)操作技巧,需要的朋友可以參考下2016-02-02AndroidStudio 設(shè)置格式化斷行寬度教程
這篇文章主要介紹了AndroidStudio 設(shè)置格式化斷行寬度教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android 擴(kuò)大 View 的點(diǎn)擊區(qū)域的方法
這篇文章主要介紹了Android 擴(kuò)大 View 的點(diǎn)擊區(qū)域的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04基于flutter?sound插件實(shí)現(xiàn)錄音與播放功能
這篇文章主要介紹了基于flutter?sound插件實(shí)現(xiàn)錄音與播放功能,介紹了如何錄音,如何播放本地和遠(yuǎn)程音頻文件,以及如何實(shí)現(xiàn)動(dòng)畫(huà),在錄制完音頻文件后如何上傳,這些都是我們平常使用這個(gè)功能會(huì)遇到的問(wèn)題。在使用的過(guò)程中遇到的問(wèn)題也有列出,需要的朋友可以參考下2022-05-05