上接《Android传感器编程入门(二)》

三、实例:窈窈录音器

通过上面的例子我们学会了如何获得某种类型的传感器,下面我通过一个实例来学会如何使用某一个类型的传感器。我们这里使用加速度传感器来实现这样一个功能:开启我们的录音程序放在你的口袋或者提包里,需要录音的时候把衣服整理一下,或者把提包挪动个位置,那么此时手机就会感受到变化从而开始录音。由此达到神不知鬼不觉的录音效果。说起来似乎有点神,其实做起来很简单,让我们开始吧。

简单的录音程序已经在第28讲的时候做过了,我们在28讲程序的基础上写本讲的代码。

1、新建一个项目 Lesson37_YYRecorder , 主文件叫 MainActivity.java ,具体信息都可以参见第二十八讲的“窈窈录音”的例子。

 

 
  

 

001 

  1. package basic.android.lesson37; 
  2. 002 
  3.   
  4. 003 
  5. import java.io.File; 
  6. 004 
  7. import java.io.IOException; 
  8. 005 
  9. import java.util.Calendar; 
  10. 006 
  11. import java.util.Locale; 
  12. 007 
  13.   
  14. 008 
  15. import android.app.Activity; 
  16. 009 
  17. import android.content.Context; 
  18. 010 
  19. import android.hardware.Sensor; 
  20. 011 
  21. import android.hardware.SensorEvent; 
  22. 012 
  23. import android.hardware.SensorEventListener; 
  24. 013 
  25. import android.hardware.SensorManager; 
  26. 014 
  27. import android.media.MediaRecorder; 
  28. 015 
  29. import android.os.Bundle; 
  30. 016 
  31. import android.text.format.DateFormat; 
  32. 017 
  33. import android.view.View; 
  34. 018 
  35. import android.widget.Button; 
  36. 019 
  37. import android.widget.TextView; 
  38. 020 
  39. import android.widget.Toast; 
  40. 021 
  41.   
  42. 022 
  43. public class MainActivity extends Activity { 
  44. 023 
  45.   
  46. 024 
  47.     //录音和停止按钮 
  48. 025 
  49.     private Button recordButton; 
  50. 026 
  51.     private Button stopButton; 
  52. 027 
  53.   
  54. 028 
  55.     //检测摇动相关变量 
  56. 029 
  57.     private long initTime = 0
  58. 030 
  59.     private long lastTime = 0
  60. 031 
  61.     private long curTime = 0
  62. 032 
  63.     private long duration = 0
  64. 033 
  65.   
  66. 034 
  67.     private float last_x = 0.0f; 
  68. 035 
  69.     private float last_y = 0.0f; 
  70. 036 
  71.     private float last_z = 0.0f; 
  72. 037 
  73.   
  74. 038 
  75.     private float shake = 0.0f; 
  76. 039 
  77.     private float totalShake = 0.0f; 
  78. 040 
  79.   
  80. 041 
  81.     //媒体录音器对象 
  82. 042 
  83.     private MediaRecorder mr; 
  84. 043 
  85.   
  86. 044 
  87.     //是否正在录音 
  88. 045 
  89.     private boolean isRecoding = false
  90. 046 
  91.   
  92. 047 
  93.     @Override 
  94. 048 
  95.     public void onCreate(Bundle savedInstanceState) { 
  96. 049 
  97.         super.onCreate(savedInstanceState); 
  98. 050 
  99.         setContentView(R.layout.main); 
  100. 051 
  101.   
  102. 052 
  103.         // UI组件 
  104. 053 
  105.         recordButton = (Button) this.findViewById(R.id.Button01); 
  106. 054 
  107.         stopButton = (Button) this.findViewById(R.id.Button02); 
  108. 055 
  109.         final TextView tx1 = (TextView) this.findViewById(R.id.TextView01); 
  110. 056 
  111.   
  112. 057 
  113.         // 录音按钮点击事件 
  114. 058 
  115.         recordButton.setOnClickListener(new View.OnClickListener() { 
  116. 059 
  117.   
  118. 060 
  119.             @Override 
  120. 061 
  121.             public void onClick(View v) { 
  122. 062 
  123.                 //如果没有在录音,那么点击按钮可以开始录音 
  124. 063 
  125.                 if(!isRecoding){ 
  126. 064 
  127.                     startRecord(); 
  128. 065 
  129.                 } 
  130. 066 
  131.             } 
  132. 067 
  133.         }); 
  134. 068 
  135.   
  136. 069 
  137.         // 停止按钮点击事件 
  138. 070 
  139.         stopButton.setOnClickListener(new View.OnClickListener() { 
  140. 071 
  141.   
  142. 072 
  143.             @Override 
  144. 073 
  145.             public void onClick(View v) { 
  146. 074 
  147.                 initShake(); 
  148. 075 
  149.                 //如果正在录音,那么可以停止录音 
  150. 076 
  151.                 if (mr != null) { 
  152. 077 
  153.                     mr.stop(); 
  154. 078 
  155.                     mr.release(); 
  156. 079 
  157.                     mr = null
  158. 080 
  159.                     recordButton.setText("录音"); 
  160. 081 
  161.                     Toast.makeText(getApplicationContext(), "录音完毕", Toast.LENGTH_LONG).show(); 
  162. 082 
  163.                     isRecoding = false
  164. 083 
  165.   
  166. 084 
  167.                 } 
  168. 085 
  169.             } 
  170. 086 
  171.         }); 
  172. 087 
  173.   
  174. 088 
  175.         // 获取传感器管理器 
  176. 089 
  177.         SensorManager sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE); 
  178. 090 
  179.         // 获取加速度传感器 
  180. 091 
  181.         Sensor acceleromererSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 
  182. 092 
  183.   
  184. 093 
  185.         // 定义传感器事件监听器 
  186. 094 
  187.         SensorEventListener acceleromererListener = new SensorEventListener() { 
  188. 095 
  189.   
  190. 096 
  191.             @Override 
  192. 097 
  193.             public void onAccuracyChanged(Sensor sensor, int accuracy) { 
  194. 098 
  195.                 //什么也不干 
  196. 099 
  197.             } 
  198. 100 
  199.   
  200. 101 
  201.             //传感器数据变动事件 
  202. 102 
  203.             @Override 
  204. 103 
  205.             public void onSensorChanged(SensorEvent event) {    
  206. 104 
  207.   
  208. 105 
  209.                 //如果没有开始录音的话可以监听是否有摇动事件,如果有摇动事件可以开始录音 
  210. 106 
  211.                 if(!isRecoding){ 
  212. 107 
  213.                     //获取加速度传感器的三个参数 
  214. 108 
  215.                     float x = event.values[SensorManager.DATA_X]; 
  216. 109 
  217.                     float y = event.values[SensorManager.DATA_Y]; 
  218. 110 
  219.                     float z = event.values[SensorManager.DATA_Z]; 
  220. 111 
  221.   
  222. 112 
  223.                     //获取当前时刻的毫秒数 
  224. 113 
  225.                     curTime = System.currentTimeMillis(); 
  226. 114 
  227.   
  228. 115 
  229.                     //100毫秒检测一次 
  230. 116 
  231.                     if ((curTime - lastTime) > 100) { 
  232. 117 
  233.   
  234. 118 
  235.                         duration = (curTime - lastTime); 
  236. 119 
  237.   
  238. 120 
  239.                         // 看是不是刚开始晃动 
  240. 121 
  241.                         if (last_x == 0.0f && last_y == 0.0f && last_z == 0.0f) { 
  242. 122 
  243.                             //last_x、last_y、last_z同时为0时,表示刚刚开始记录 
  244. 123 
  245.                             initTime = System.currentTimeMillis(); 
  246. 124 
  247.                         } else { 
  248. 125 
  249.                             // 单次晃动幅度 
  250. 126 
  251.                             shake = (Math.abs(x - last_x) + Math.abs(y - last_y) + Math.abs(z - last_z)) / duration * 100
  252. 127 
  253.                         } 
  254. 128 
  255.   
  256. 129 
  257.                         //把每次的晃动幅度相加,得到总体晃动幅度 
  258. 130 
  259.                         totalShake += shake; 
  260. 131 
  261.   
  262. 132 
  263.                         // 判断是否为摇动,这是我自己写的标准,不准确,只是用来做教学示例,别误会了^_^ 
  264. 133 
  265.                         if (totalShake > 10 && totalShake / (curTime - initTime) * 1000 > 10) { 
  266. 134 
  267.                             startRecord(); 
  268. 135 
  269.                             initShake(); 
  270. 136 
  271.                         } 
  272. 137 
  273.   
  274. 138 
  275.                         tx1.setText("总体晃动幅度="+totalShake+ "\n平均晃动幅度="+totalShake / (curTime - initTime) * 1000 ); 
  276. 139 
  277.                     } 
  278. 140 
  279.   
  280. 141 
  281.                     last_x = x; 
  282. 142 
  283.                     last_y = y; 
  284. 143 
  285.                     last_z = z; 
  286. 144 
  287.                     lastTime = curTime; 
  288. 145 
  289.                 } 
  290. 146 
  291.             } 
  292. 147 
  293.   
  294. 148 
  295.         }; 
  296. 149 
  297.   
  298. 150 
  299.         //在传感器管理器中注册监听器 
  300. 151 
  301.         sm.registerListener(acceleromererListener, acceleromererSensor, SensorManager.SENSOR_DELAY_NORMAL); 
  302. 152 
  303.   
  304. 153 
  305.     } 
  306. 154 
  307.   
  308. 155 
  309.     // 开始录音 
  310. 156 
  311.     public void startRecord() { 
  312. 157 
  313.         //把正在录音的标志设为真 
  314. 158 
  315.         isRecoding = true
  316. 159 
  317.         //存放文件 
  318. 160 
  319.         File file = new File("/sdcard/" + "YY" 
  320. 161 
  321.                 + new DateFormat().format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".amr"); 
  322. 162 
  323.   
  324. 163 
  325.         Toast.makeText(getApplicationContext(), "正在录音,录音文件在" + file.getAbsolutePath(), Toast.LENGTH_LONG).show(); 
  326. 164 
  327.   
  328. 165 
  329.         // 创建录音对象 
  330. 166 
  331.         mr = new MediaRecorder(); 
  332. 167 
  333.   
  334. 168 
  335.         // 从麦克风源进行录音 
  336. 169 
  337.         mr.setAudioSource(MediaRecorder.AudioSource.DEFAULT); 
  338. 170 
  339.   
  340. 171 
  341.         // 设置输出格式 
  342. 172 
  343.         mr.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); 
  344. 173 
  345.   
  346. 174 
  347.         // 设置编码格式 
  348. 175 
  349.         mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); 
  350. 176 
  351.   
  352. 177 
  353.         // 设置输出文件 
  354. 178 
  355.         mr.setOutputFile(file.getAbsolutePath()); 
  356. 179 
  357.   
  358. 180 
  359.         try { 
  360. 181 
  361.             // 创建文件 
  362. 182 
  363.             file.createNewFile(); 
  364. 183 
  365.             // 准备录制 
  366. 184 
  367.             mr.prepare(); 
  368. 185 
  369.         } catch (IllegalStateException e) { 
  370. 186 
  371.             e.printStackTrace(); 
  372. 187 
  373.         } catch (IOException e) { 
  374. 188 
  375.             e.printStackTrace(); 
  376. 189 
  377.         } 
  378. 190 
  379.         // 开始录制 
  380. 191 
  381.         mr.start(); 
  382. 192 
  383.         recordButton.setText("录音中……"); 
  384. 193 
  385.     } 
  386. 194 
  387.   
  388. 195 
  389.     //摇动初始化 
  390. 196 
  391.     public void initShake() { 
  392. 197 
  393.         lastTime = 0
  394. 198 
  395.         duration = 0
  396. 199 
  397.         curTime = 0
  398. 200 
  399.         initTime = 0
  400. 201 
  401.         last_x = 0.0f; 
  402. 202 
  403.         last_y = 0.0f; 
  404. 203 
  405.         last_z = 0.0f; 
  406. 204 
  407.         shake = 0.0f; 
  408. 205 
  409.         totalShake = 0.0f; 
  410. 206 
  411.     } 

3、连接真机Milestone,编译并运行程序:

device 

 

晃动机器,开始录音

device

查看录音文件,效果还可以:

device

4、我们小结一下:

到Android2.2版本为止,系统并没有给开发者提供多少可用的包装好的传感器信息,只是提供了传感器发出的原始数据,这些原始数据存放在  event.values 的数组里,开发人员需要从这些裸数据总自行发掘有用的信息,譬如从加速度传感器的3维裸数据中获得摇动的判断(我的摇动判断很弱智,有时间再改吧……)。

 

 

好了本讲就先到这里,关于传感器有机会我们展开再谈,下次再见吧。