轉載於:http://android.yaohuiji.com/archives/2503
本講內容:Android傳感器編程入門,分別包括加速度傳感器(accelerometer),陀螺儀(gyroscope),環境光照傳感器
(light),磁力傳感器(magnetic
field),方向傳感器(orientation),壓力傳感器(pressure),距離傳感器(proximity)和溫度傳感器
(temperature)
一、前言
我很喜歡電腦,可是筆記本還是太大,筆記本電腦再小還是要弄個小包背起來的,智能手機則不同,它完全就是一個手機,可以隨意裝在一個口袋裏隨身攜
帶。因此我在2002年左右時最喜歡玩裝備是Dell的PDA,2007年的時候最喜歡玩的是N73,而在2010年最喜歡玩的則是Milestone。
眼見着手機的功能越來越強,時至今日智能手機甚至在某些方面已經強過了臺式機和筆記本。本節課講的就是智能手機強過臺式機和筆記本的地方:傳感器。
2008年的時候我很喜歡我的小白筆記本Macbook,喜歡玩它的一個小軟件,一拍桌子,筆記本感受到了震動,它就轉換了一個桌面出來,這讓我像個小孩子一樣沒事就拍拍桌子。這一功能這得益於蘋果筆記本內置有傳感器。
我不知道iPhone手機是不是第一個把各種各樣的傳感器運用在手機上的,不過我知道iPhone是把傳感器運用在手機上最成功的第一個。隨後的
Android系統也內置了大量的傳感器,這讓Android系統手機和普通的諾基亞智能機和Windows
CE智能機相比牛氣了許多,在擁有了Milestone之後,我的N73就被仍在抽屜的角落裏了。
從Android1.5開始,系統內置了對多達八種傳感器的支持,他們分別是:加速度傳感器(accelerometer),陀螺儀
(gyroscope),環境光照傳感器(light),磁力傳感器(magnetic
field),方向傳感器(orientation),壓力傳感器(pressure),距離傳感器(proximity)和溫度傳感器
(temperature)。
利用這些傳感器我們可以製作出各種有趣的應用程序和遊戲。譬如在口袋裏晃一晃手機,手機就開始神不知鬼不覺的錄音,不要着急這個很容易做,我們在本文的結尾就一起製作這個小應用。
本講的學習方式還是在實戰中學習,需要提醒的是模擬器中無法模擬傳感器,因此你需要準備一款Android真機才能運行本講的例子。
二、實例:手機傳感器清單
我們還是先看程序後解釋,
1、創建一個項目 Lesson37_HelloSensor , 主Activity名字叫 mainActivity.java
2、UI佈局文件main.xml的內容如下:
1
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
|
3
|
<textview android:layout_height=
"wrap_content"
android:layout_width=
"fill_parent"
android:text=
""
android:id=
"@+id/TextView01"
>
|
4
|
</textview></linearlayout>
|
3、mainActivity.java的內容如下:
01
|
package
basic.android.lesson37;
|
03
|
import
java.util.List;
|
05
|
import
android.app.Activity;
|
06
|
import
android.content.Context;
|
07
|
import
android.hardware.Sensor;
|
08
|
import
android.hardware.SensorManager;
|
09
|
import
android.os.Bundle;
|
10
|
import
android.widget.TextView;
|
12
|
public
class
MainActivity
extends
Activity {
|
14
|
/** Called when the activity is first created. */
|
16
|
public
void
onCreate(Bundle savedInstanceState) {
|
17
|
super
.onCreate(savedInstanceState);
|
18
|
setContentView(R.layout.main);
|
21
|
final
TextView tx1 = (TextView) findViewById(R.id.TextView01);
|
24
|
SensorManager sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
27
|
List<sensor> allSensors = sm.getSensorList(Sensor.TYPE_ALL);
|
30
|
tx1.setText(
"經檢測該手機有"
+ allSensors.size() +
"個傳感器,他們分別是:/n"
);
|
33
|
for
(Sensor s : allSensors) {
|
35
|
String tempString =
"/n"
+
" 設備名稱:"
+ s.getName() +
"/n"
+
" 設備版本:"
+ s.getVersion() +
"/n"
+
" 供應商:"
|
36
|
+ s.getVendor() +
"/n"
;
|
38
|
switch
(s.getType()) {
|
39
|
case
Sensor.TYPE_ACCELEROMETER:
|
40
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 加速度傳感器accelerometer"
+ tempString);
|
42
|
case
Sensor.TYPE_GYROSCOPE:
|
43
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 陀螺儀傳感器gyroscope"
+ tempString);
|
45
|
case
Sensor.TYPE_LIGHT:
|
46
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 環境光線傳感器light"
+ tempString);
|
48
|
case
Sensor.TYPE_MAGNETIC_FIELD:
|
49
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 電磁場傳感器magnetic field"
+ tempString);
|
51
|
case
Sensor.TYPE_ORIENTATION:
|
52
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 方向傳感器orientation"
+ tempString);
|
54
|
case
Sensor.TYPE_PRESSURE:
|
55
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 壓力傳感器pressure"
+ tempString);
|
57
|
case
Sensor.TYPE_PROXIMITY:
|
58
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 距離傳感器proximity"
+ tempString);
|
60
|
case
Sensor.TYPE_TEMPERATURE:
|
61
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 溫度傳感器temperature"
+ tempString);
|
64
|
tx1.setText(tx1.getText().toString() + s.getType() +
" 未知傳感器"
+ tempString);
|
4、連接真機Milestone,編譯並運行程序,顯示結果如下:
5、結合上面的程序我們做一些解釋。
- Android所有的傳感器都歸傳感器管理器 SensorManager 管理,獲取傳感器管理器的方法很簡單:
String service_name = Context.SENSOR_SERVICE;
SensorManager sensorManager = (SensorManager)getSystemService(service_name);
- 現階段Android支持的傳感器有8種,它們分別是:
傳感器類型常量 |
內部整數值 |
中文名稱 |
Sensor.TYPE_ACCELEROMETER |
1 |
加速度傳感器 |
Sensor.TYPE_MAGNETIC_FIELD |
2 |
磁力傳感器 |
Sensor.TYPE_ORIENTATION |
3 |
方向傳感器 |
Sensor.TYPE_GYROSCOPE |
4 |
陀螺儀傳感器 |
Sensor.TYPE_LIGHT |
5 |
環境光照傳感器 |
Sensor.TYPE_PRESSURE |
6 |
壓力傳感器 |
Sensor.TYPE_TEMPERATURE |
7 |
溫度傳感器 |
Sensor.TYPE_PROXIMITY |
8 |
距離傳感器 |
- 從傳感器管理器中獲取其中某個或者某些傳感器的方法有如下三種:
第一種:獲取某種傳感器的默認傳感器
Sensor defaultGyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
第二種:獲取某種傳感器的列表
List<Sensor> pressureSensors = sensorManager.getSensorList(Sensor.TYPE_PRESSURE);
第三種:獲取所有傳感器的列表,我們這個例子就用的第三種
List<Sensor> allSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
- 對於某一個傳感器,它的一些具體信息的獲取方法可以見下表:
方法 |
描述 |
getMaximumRange()
|
最大取值範圍 |
getName()
|
設備名稱 |
getPower()
|
功率 |
getResolution()
|
精度 |
getType()
|
傳感器類型 |
getVentor()
|
設備供應商 |
getVersion()
|
設備版本號 |
三、實例:窈窈錄音器
通過上面的例子我們學會了如何獲得某種類型的傳感器,下面我通過一個實例來學會如何使用某一個類型的傳感器。我們這裏使用加速度傳感器來實現這樣一
個功能:開啓我們的錄音程序放在你的口袋或者提包裏,需要錄音的時候把衣服整理一下,或者把提包挪動個位置,那麼此時手機就會感受到變化從而開始錄音。由
此達到神不知鬼不覺的錄音效果。說起來似乎有點神,其實做起來很簡單,讓我們開始吧。
簡單的錄音程序已經在第28講的時候做過了,我們在28講程序的基礎上寫本講的代碼。
1、新建一個項目 Lesson37_YYRecorder , 主文件叫 MainActivity.java ,具體信息都可以參見第二十八講的“窈窈錄音”的例子。
2、這裏只貼出於28講不同的 MainActivity.java 的代碼,請注意看註釋:
001
|
package
basic.android.lesson37;
|
004
|
import
java.io.IOException;
|
005
|
import
java.util.Calendar;
|
006
|
import
java.util.Locale;
|
008
|
import
android.app.Activity;
|
009
|
import
android.content.Context;
|
010
|
import
android.hardware.Sensor;
|
011
|
import
android.hardware.SensorEvent;
|
012
|
import
android.hardware.SensorEventListener;
|
013
|
import
android.hardware.SensorManager;
|
014
|
import
android.media.MediaRecorder;
|
015
|
import
android.os.Bundle;
|
016
|
import
android.text.format.DateFormat;
|
017
|
import
android.view.View;
|
018
|
import
android.widget.Button;
|
019
|
import
android.widget.TextView;
|
020
|
import
android.widget.Toast;
|
022
|
public
class
MainActivity
extends
Activity {
|
025
|
private
Button recordButton;
|
026
|
private
Button stopButton;
|
029
|
private
long
initTime =
0
;
|
030
|
private
long
lastTime =
0
;
|
031
|
private
long
curTime =
0
;
|
032
|
private
long
duration =
0
;
|
034
|
private
float
last_x =
0
.0f;
|
035
|
private
float
last_y =
0
.0f;
|
036
|
private
float
last_z =
0
.0f;
|
038
|
private
float
shake =
0
.0f;
|
039
|
private
float
totalShake =
0
.0f;
|
042
|
private
MediaRecorder mr;
|
045
|
private
boolean
isRecoding =
false
;
|
048
|
public
void
onCreate(Bundle savedInstanceState) {
|
049
|
super
.onCreate(savedInstanceState);
|
050
|
setContentView(R.layout.main);
|
053
|
recordButton = (Button)
this
.findViewById(R.id.Button01);
|
054
|
stopButton = (Button)
this
.findViewById(R.id.Button02);
|
055
|
final
TextView tx1 = (TextView)
this
.findViewById(R.id.TextView01);
|
058
|
recordButton.setOnClickListener(
new
View.OnClickListener() {
|
061
|
public
void
onClick(View v) {
|
070
|
stopButton.setOnClickListener(
new
View.OnClickListener() {
|
073
|
public
void
onClick(View v) {
|
080
|
recordButton.setText(
"錄音"
);
|
081
|
Toast.makeText(getApplicationContext(),
"錄音完畢"
, Toast.LENGTH_LONG).show();
|
089
|
SensorManager sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
091
|
Sensor acceleromererSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
094
|
SensorEventListener acceleromererListener =
new
SensorEventListener() {
|
097
|
public
void
onAccuracyChanged(Sensor sensor,
int
accuracy) {
|
103
|
public
void
onSensorChanged(SensorEvent event) {
|
108
|
float
x = event.values[SensorManager.DATA_X];
|
109
|
float
y = event.values[SensorManager.DATA_Y];
|
110
|
float
z = event.values[SensorManager.DATA_Z];
|
113
|
curTime = System.currentTimeMillis();
|
116
|
if
((curTime - lastTime) >
100
) {
|
118
|
duration = (curTime - lastTime);
|
121
|
if
(last_x ==
0
.0f && last_y ==
0
.0f && last_z ==
0
.0f) {
|
123
|
initTime = System.currentTimeMillis();
|
126
|
shake = (Math.abs(x - last_x) + Math.abs(y - last_y) + Math.abs(z - last_z)) / duration *
100
;
|
133
|
if
(totalShake >
10
&& totalShake / (curTime - initTime) *
1000
>
10
) {
|
138
|
tx1.setText(
"總體晃動幅度="
+totalShake+
"/n平均晃動幅度="
+totalShake / (curTime - initTime) *
1000
);
|
151
|
sm.registerListener(acceleromererListener, acceleromererSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
156
|
public
void
startRecord() {
|
160
|
File file =
new
File(
"/sdcard/"
+
"YY"
|
161
|
+
new
DateFormat().format(
"yyyyMMdd_hhmmss"
, Calendar.getInstance(Locale.CHINA)) +
".amr"
);
|
163
|
Toast.makeText(getApplicationContext(),
"正在錄音,錄音文件在"
+ file.getAbsolutePath(), Toast.LENGTH_LONG).show();
|
166
|
mr =
new
MediaRecorder();
|
169
|
mr.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
|
172
|
mr.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
|
175
|
mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
|
178
|
mr.setOutputFile(file.getAbsolutePath());
|
182
|
file.createNewFile();
|
185
|
}
catch
(IllegalStateException e) {
|
187
|
}
catch
(IOException e) {
|
192
|
recordButton.setText(
"錄音中……"
);
|
196
|
public
void
initShake() {
|
3、連接真機Milestone,編譯並運行程序:
晃動機器,開始錄音
查看錄音文件,效果還可以:
4、我們小結一下:
到Android2.2版本爲止,系統並沒有給開發者提供多少可用的包裝好的傳感器信息,只是提供了傳感器發出的原始數據,這些原始數據存放在
event.values
的數組裏,開發人員需要從這些裸數據總自行發掘有用的信息,譬如從加速度傳感器的3維裸數據中獲得搖動的判斷(我的搖動判斷很弱智,有時間再改吧……)。
好了本講就先到這裏,關於傳感器有機會我們展開再談,下次再見吧。