Chapter 14 感測器 作者 : 林致孙 手機和感測器的結合, 讓手機產生更多的應用, 除了應用於遊戲軟體, 感測器也讓手機上實作擴增實境變得更容易 本章將介紹應用程式如何讀取手機上的感測器, 同時也會提供範例, 讓讀者瞭解方位感測器 (Orientation Sensor) 與加速度感測器

Similar documents
Microsoft PowerPoint - Lab 2-2 Android Sensors.pptx

Microsoft PowerPoint - 12 特色开发.ppt [兼容模式]

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

多媒體應用 13 新增專案並完成版面配置 <ExMusic01> <activity_main.xml> ImageView ID imgplay ImageView ID imgstop ImageView ID imgfront TextView ID txtsong TextView ID t

Android Service

ContextMenu

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

新・解きながら学ぶJava

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

CHAPTER VC#

詞 彙 表 編 號 詞 彙 描 述 1 預 約 人 資 料 中 文 姓 名 英 文 姓 名 身 份 證 字 號 預 約 人 電 話 性 別 2 付 款 資 料 信 用 卡 別 信 用 卡 號 信 用 卡 有 效 日 期 3 住 房 條 件 入 住 日 期 退 房 日 期 人 數 房 間 數 量 入

Dynamic Layout in Android

建模与图形思考

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

ShareText

任務二 : 產生 20 個有炸彈的磚塊, 放在隨機的位置編輯 Block 類別的程式碼 import greenfoot.; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) Write a description of class

Microsoft Word - 01.DOC

untitled

Android Fragment

Database_001

實作SQLiteOpenHelper類別

建立Android新專案

運算子多載 Operator Overloading

Chapter 8 影音多媒體 作者 : 林致宇 在上一章中筆者有提到一個 Android 應用程式是由四個構成要素所組成的 : Activity, Broadcast Receiver, Service 與 Content Provider, 我們也已經詳細介紹了 Activity 與 Broad

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

建立Android新專案

首先讓我們來看 SimpleGoogleSearch.java, 程式碼如下所示 : 1 public class SimpleGoogleSearch extends Activity { 2 3 EditText et_searchstr; 4 Button btn_search; 5

投影片 1

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

Android + NFC

0 0 = 1 0 = 0 1 = = 1 1 = 0 0 = 1


RecyclerView and CardVew

( 含 要 ) 1-2 用 或 雇 用, 抑 或 有 無 俸 給 文 職 或 武 職, 政 官 或 事 官 均 屬 之, 其 不 以 具 備 人 資 格 為 限, 因 此 屬 於 最 廣 義 之 念 四 廣 義 念 之 依 服 24 條 之 規 定 : 本 於 受 有 俸 給 之 文 武 職, 及

The golden pins of the PCI card can be oxidized after months or years


Java

(Microsoft Word - wes _\246p\246\363\250\317\245\316LED\277O\305\343\245\334\252\254\272A.doc)

新版 明解C++入門編

Microsoft PowerPoint - VB14.ppt

Microsoft PowerPoint - App與微控器整合.pptx

Microsoft PowerPoint - C_Structure.ppt

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

javaexample-02.pdf

《大话设计模式》第一章

untitled

The Embedded computing platform

预览图 : (2) 在 SelectCity.java 中增加控件, 用于绑定 select_city 文件的 ListView, TextView,EditTest 等控件 代码和注释如下 :

幻灯片 1

3.1 num = 3 ch = 'C' 2

untitled

EJB-Programming-4-cn.doc

雲端 Cloud Computing 技術指南 運算 應用 平台與架構 10/04/15 11:55:46 INFO 10/04/15 11:55:53 INFO 10/04/15 11:55:56 INFO 10/04/15 11:56:05 INFO 10/04/15 11:56:07 INFO

840 提示 Excel - Excel -- Excel (=) Excel ch0.xlsx H5 =D5+E5+F5+G5 (=) = - Excel 00

Python a p p l e b e a r c Fruit Animal a p p l e b e a r c 2-2

投影片 1

7 TextView tv = (TextView)findViewById(R.id.tv_birthday); 8 9 Context otherapp = null; try { 12 otherapp = createpackagecontext("lincyu.horoscop

( )... 5 ( ) ( )

Microsoft Word - 第3章.doc

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii

TextView: 用來顯示 月 EditText: 讓使用者輸入日期 Button: 使用者按下 得知星座 的按鈕後, 可以查詢自己的星座, 程式會切換到另一個頁陎, 顯示星座的圖示及相關資訊 規畫好使用者介陎後, 我們就可以開始撰寫 XML 檔, 請讀者自行參考光碟內的檔案 \ 範例程式 \Ch


CC213

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Microsoft PowerPoint - 遊戲企劃

Microsoft Word - 投影片ch11

FY.DOC

Microsoft Word - ch04三校.doc

2 WF 1 T I P WF WF WF WF WF WF WF WF 2.1 WF WF WF WF WF WF

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

C/C++ - 字符输入输出和字符确认

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

ebook39-6

KillTest 质量更高 服务更好 学习资料 半年免费更新服务

06 01 action JavaScript action jquery jquery AJAX CSS jquery CSS jquery HTML CSS jquery.css() getter setter.css('backgroundcolor') jquery CSS b

JBuilder Weblogic

Microsoft Word - ACI chapter00-1ed.docx

PowerPoint 簡報

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点


Transcription:

Chapter 14 感測器 作者 : 林致孙 手機和感測器的結合, 讓手機產生更多的應用, 除了應用於遊戲軟體, 感測器也讓手機上實作擴增實境變得更容易 本章將介紹應用程式如何讀取手機上的感測器, 同時也會提供範例, 讓讀者瞭解方位感測器 (Orientation Sensor) 與加速度感測器 (Accelerometer Sensor) 的應用 14.1 讀取感測資料 首先我們先學習感測器相關類別的使用方法, 請讀者引進光碟中 \ 範例程式 \Chapter14\SensorList 這個專案, 這個專案有兩個 Activity: SensorList: 繼承了 ListActivity, 會列出手機上所的感測器, 點選個別的感測器後, 會跳至 SensorReader 讀取所選取的感測器的讀值 SensorReader: 讀取特定感測器的數值 讓我們先來看 SensorList 這個 Activity, 其內容如下所示 : 1 public class SensorList extends ListActivity { 2 3 private SensorManager smgr; 4 List<Sensor> slist; 5 6 @Override 7 public void oncreate(bundle savedinstancestate) { 8 super.oncreate(savedinstancestate); 9 setcontentview(r.layout.main); 10 11 smgr = (SensorManager)getSystemService( 12 Context.SENSOR_SERVICE); 13 slist = smgr.getsensorlist(sensor.type_all); 14 ArrayList<String> snlist = new ArrayList<String>(); 15 16 for (int i = 0; i < slist.size(); i++) 17 snlist.add(slist.get(i).getname()); 18 19 ListAdapter adapter = new ArrayAdapter<String>(this, 20 R.layout.list_item, snlist);

21 setlistadapter(adapter); 22 } 23 24 @Override 25 protected void onlistitemclick(listview l, View v, 26 int position, long id) { 27 super.onlistitemclick(l, v, position, id); 28 29 Intent intent = new Intent(this, SensorReader.class); 30 intent.putextra("key_type", slist.get(position).gettype()); 31 startactivity(intent); 32 } 33 } 首先為了存取手機上的感測器資訊, 程式於第 11 行先呼叫 getsystemservice 取得一個 SensorManager 物件 [1], 第 13 行呼叫 SensorManager 物件的 getsensorlist 方法取得 Sensor 物件 [2], 參數 Sensor. TYPE_ALL 代表所有種類的感測器都要取得, 如果是 Sensor. TYPE_ACCELEROMETER 代表只取得加速度感測器的 Sensor 物件 ( 一個手機有可能擁有兩個以上的同種類測器 ) 第 17 行呼叫 Sensor 物件的 getname 方法取得感測器的名稱, 這名稱即要呈現於列表介面元件 (ListView) 的 資料 而當使用者點選某個感測器後, 會將感測器的型別 ( 可想成種類 ) 夾帶在 Intent 裡傳送給 SensorReader 下圖是筆者截取實機所得到的執行畫面: 接著討論 SensorReader 這個 Activity, 其內容如下所示 : 1 public class SensorReader extends Activity {

2 3 private SensorManager smgr; 4 private TextView tv; 5 private Sensor sensor; 6 7 @Override 8 public void oncreate(bundle savedinstancestate) { 9 super.oncreate(savedinstancestate); 10 setcontentview(r.layout.reader); 11 12 tv = (TextView)findViewById(R.id.tv_sresult); 13 14 smgr = (SensorManager)getSystemService( 15 Context.SENSOR_SERVICE); 16 Intent intent = getintent(); 17 int stype = intent.getintextra("key_type", -1); 18 19 sensor = smgr.getdefaultsensor(stype); 20 } 21 22 @Override 23 protected void onresume() { 24 smgr.registerlistener(slistener, sensor, 25 SensorManager.SENSOR_DELAY_UI); 26 super.onresume(); 27 } 28 29 @Override 30 protected void onpause() { 31 smgr.unregisterlistener(slistener, sensor); 32 super.onpause(); 33 } 34 35 private final SensorEventListener slistener = 36 new SensorEventListener() { 37 public void onsensorchanged (SensorEvent event) { 38 if (event.sensor!= sensor) return; 39

40 String str = ""; 41 42 switch (sensor.gettype()) { 43 case Sensor.TYPE_ACCELEROMETER: 44 str = "Accelerometer Sensor\n"; 45 break; 46 case Sensor.TYPE_GYROSCOPE: 47 str = "Gyroscope Sensor\n"; 48 break; 49 case Sensor.TYPE_LIGHT: 50 str = "Light Sensor\n"; 51 break; 52 case Sensor.TYPE_MAGNETIC_FIELD: 53 str = "Magnetic Field Sensor\n"; 54 break; 55 case Sensor.TYPE_ORIENTATION: 56 str = "Orientation Sensor\n"; 57 break; 58 case Sensor.TYPE_PRESSURE: 59 str = "Pressure Sensor\n"; 60 break; 61 case Sensor.TYPE_PROXIMITY: 62 str = "Proximity Sensor\n"; 63 break; 64 case Sensor.TYPE_TEMPERATURE: 65 str = "Temperature Sensor\n"; 66 break; 67 } 68 69 for (int i = 0; i < event.values.length; i++) 70 str = str + "values[" + i + "]: " + 71 event.values[i] + "\n"; 72 73 str = str + "Accuracy: " + event.accuracy; 74 75 tv.settext(str); 76 } 77 }

78 public void onaccuracychanged (Sensor sensor, int accuracy) { 79 } 80 }; 81 } 首先在第 14 行, 同樣使用 getsystemservice 方法取得 SensorManger 物件, 接著利用 getdefaultsensor 取得該種類的 Sensor 物件, 若同種類的感測器數目有兩個以上,Intent 需另外攜帶感測器的名稱, 同時應該使用 getsensorlist 取代 getdefaultsensor 為了讀取該感測器的資料, 我們必須設計一個傾聽者給感測器, 傾聽者類別需實作 SensorEventListener 介面 [3], 程式碼是位於 35~80 行, 一樣使用了匿名類別的技巧, 有兩個抽象方法需要實作 : onaccuracychanged: 當量測值的精準度改變時, 這個方法會被呼叫 onsensorchanged: 當量測值改變時, 這個方法會被呼叫 我們把焦點放在 onsensorchanged 方法,onSensorchanged 會傳入一個 SensorEvent 物件 [4], 感測的值可從這個 SensorEvent 物件中抓取, 首先在 38 行, 先判斷發生事件的感測器是否是我們所要量測的感測器, 接著 42~67 行, 我們將感測器的種類抓出來, 並設定之後要用來顯示於 TextView 上的字串, 字串是告訴使用者讀取到的感測器是哪一種感測器 讀取感測器值的程式是寫在 69~71 行, 感測值會儲存在一個浮點數陣列 (float [] values), 這個浮點數陣列是 SensorEvent 類別的成員變數, 不同種類的感測器有不同的意義 [4], 以方位感測器 (Orientation Sensor) 為例, 當手機是正著直著拿在手上時, 如下圖所示,values[0] 的值意義為 :0 代表北方 90 代表東方 180 代表南方 270 代表西方,values[1] 是縱向旋轉角, 現在一樣正著直著拿著手機, 向外旋轉, 垂直向上時值為 90 面朝上平置時值為 0 垂直向下時值為 90 面朝下平置時值為 180,values[2] 是橫向旋轉角, 現在一樣正著直著拿著手機, 左或向右旋轉, 朝前時值為 0 往右橫放是-90 往左橫放是 90, 詳細的定義可參閱說明文件 [4], 若對說明文件 [4] 不是十分瞭解, 筆者建議讀者可利用本程式自行做一些測詴, 便能體會出值是如何變化的

而以加速度感測器為例的話,values[0] 代表手機的 X 軸所承受的加速度減去重力於 X 軸所帶來的加速度, 以上圖為例, 手機的 X 軸是指比較窄的那一個邊所型成的軸, 往右較大,values[1] 代表手機的 Y 軸所承受的加速度減去重力於 Y 軸所帶來的加速度, 以上圖為例, 手機的 Y 軸是指比較長的那一個邊所型成的軸, 往上較大,values[2] 代表手機的 Z 軸所承受的加速度減去重力於 Z 軸所帶來的加速度, 手機的 Z 軸代表著向外或向內, 往外較大 當正著直著拿著手機時, 可發現,values[0] 與 values[2] 會接近於 0, 而 Y 軸受到了向下的重力加速度 (-9.81 公尺 / 秒平方 ), 因為手機穩穩的拿在手上, 因此 Y 軸事實上並沒有任何加速度 (0 公尺 / 秒平方 ), 因此 0-(-9.81)=9.81, 讀者可發現 values[1] 的值會接近 9.81 其它感測器的讀值就請讀者自行閱讀說明文件 SensorEvent 物件還有一個成員變數為 accuracy(73 行 ), SENSOR_STATUS_ACCURACY_HIGH( 值為 3) 代表這個回報的值是很精準的, SENSOR_STATUS_UNRELIABLE( 值為 0) 則代表回報的值的精準度是最低的, 請讀者參考說明文件 [1] 此外, 若在精準度發生變化時, 做些處理動做的話, 就要去確實實作 78 行的 onaccuracychanged 方法 現在傾聽者已經設計完成, 我們要幫感測器跟其傾聽者連結在一起, 當感測器註冊了傾聽者後, 程式就會一直去讀取感測器的資料, 因此我們希望只有當程式在前景執行時才讀取感測器的資料, 因此於 onresume 方法 ( 第 24 行 ) 呼叫 SensorManager 類別的 registerlistener 方法去做註冊,registerListener 方法需要三

個參數, 第一個參數傾聽者物件, 第二個參數是感測器物件, 第三個參數是設定回報速率, 亦即多久回報一次, 共有四種速率可選擇 : SENSOR_DELAY_FASTEST SENSOR_DELAY_GAME SENSOR_DELAY_UI 與 SENSOR_DELAY_NORMAL 程式於 onpause 方法 ( 第 31 行 ) 呼叫 unregisterlistener 方法取消註冊, 如此當程式 進入背景或結束時就不會讀取感測器了 下圖是筆者於實機讀取方位感測器的一 個執行畫面 : 14.2 方位感測器的應用 在這一節筆者將示範一個方位感測器的應用, 這個應用程式的功能相當簡單, 就是出現一個箭頭指向亞洲大學行政大樓 請讀者引進光碟中 \ 範例程式 \Chapter14\OrientationApp 這個專案, 裡面有兩個 Java 原始碼檔案 : OrientationApp.java: 這是一個 Activity, 會讀取 GPS 以及方位感測器的資料, 藉以判斷箭頭應該呈現的方向 ArrowView.java: 其繼承了 View 類別, 是一個客製化的 View, 主要是用來顯示箭頭 先來討論 OrientationApp.java, 其內容如下 : 1 public class OrientationApp extends Activity { 2 3 private ArrowView av; 4 private MyLocationListener mll; 5 private MySensorListener msl;

6 private LocationManager lmgr; 7 private SensorManager smgr; 8 private List<Sensor> slist; 9 private float orientation, target; 10 11 @Override 12 public void oncreate(bundle savedinstancestate) { 13 super.oncreate(savedinstancestate); 14 15 setrequestedorientation( 16 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 17 18 av = new ArrowView(this); 19 setcontentview(av); 20 21 lmgr = (LocationManager)getSystemService(LOCATION_SERVICE); 22 mll = new MyLocationListener(); 23 24 smgr = (SensorManager)getSystemService( 25 Context.SENSOR_SERVICE); 26 msl = new MySensorListener(); 27 28 slist = smgr.getsensorlist(sensor.type_orientation); 29 if (slist.size() == 0) { 30 Toast.makeText(this, "No orientation sensor", 31 Toast.LENGTH_SHORT).show(); 32 finish(); 33 } 34 orientation = (float)0.0; 35 target = (float)0.0; 36 } 37 38 @Override 39 protected void onresume() { 40 super.onresume(); 41 lmgr.requestlocationupdates( 42 LocationManager.GPS_PROVIDER, 0, 0, mll); 43 smgr.registerlistener(msl, slist.get(0),

44 SensorManager.SENSOR_DELAY_UI); 45 } 46 47 @Override 48 protected void onpause() { 49 super.onpause(); 50 lmgr.removeupdates(mll); 51 smgr.unregisterlistener(msl, slist.get(0)); 52 } 53 54 private void adjustarrow() { 55 float degree = target-orientation; 56 if (degree < 0) degree = degree + 360.0f; 57 av.setdegree(degree); 58 setcontentview(av); 59 } 60 61 class MySensorListener implements SensorEventListener { 62 public void onsensorchanged (SensorEvent event) { 63 if (event.sensor == slist.get(0)) { 64 orientation = event.values[0]; 65 adjustarrow(); 66 } 67 } 68 public void onaccuracychanged (Sensor sensor, int accuracy) { 69 } 70 } 71 72 class MyLocationListener implements LocationListener { 73 @Override 74 public void onlocationchanged(location location) { 75 if (location == null) 76 return; 77 78 Location dest = new Location(location); 79 dest.setlatitude(24.045857); 80 dest.setlongitude(120.686681); 81 target = location.bearingto(dest);

82 adjustarrow(); 83 } 84 @Override 85 public void onproviderdisabled(string provider) { 86 } 87 @Override 88 public void onproviderenabled(string provider) { 89 } 90 @Override 91 public void onstatuschanged(string provider, int status, 92 Bundle extras) { 93 } 94 } 95 } 這個程式使用到的大部份類別與方法都學過了, 因此筆者只告訴讀者這個程式做了哪幾件事, 讓讀者自行去閱讀細節即可, 這個程式主要做了幾件事 : 設計感測器的傾聽者 (61~70 行 ): 當發現感測值發生變化後, 程式會設定變數 orientation 的值 (values[0]), 藉此來判斷使用者面對的方位, 並呼叫我們自行定義的 adjustarrow 方法來設定箭頭的方向 設計 GPS 的傾聽者 (72~94 行 ): 當發現使用者所在位置改變時, 程式會去計算目標物 ( 亞洲大學行政大樓 ) 是在使用者的哪個方位, 要知道目標物是在使用者的哪個方位可利用 Location 類別 bearingto 方法 [5], 如果目標物在使用者的正北方, 變數 target 的值為 0, 如果目標物在使用者的正東方, 變數 target 的值為 90, 以此類推 接著一樣呼叫自行定義的 adjustarrow 方法來設定箭頭的方向 此處要提醒一下,120.686681 及 24.045857 是亞洲大學行政大樓的經緯度值, 讀者可換成在自己附近的目標物, 以方便程式的測詴 設定箭頭方向 : 寫於 adjustarrow 方法 (54~59 行 ), 我們假設手機正上方為 0 度, 右側為 90 度, 則箭頭應該偏轉的角度只要將 target 減去 orientation 即可算出 算出偏轉角度後呼叫 ArrowView 類別 ( 程式自行定義的, 稍後會解說 ) 的 setdegree 方法將角度傳送給 ArrowView 物件, 並將 ArrowView 顯示於畫面上 於 onresume 方法啟動定位與感測服務 (39~45 行 ) 於 onpause 方法停止定位與感測服務 (48~52 行 ) 在這個 OrientationApp.java 的程式中, 我們沒學過的應該就只有第 15 行 Activity 類別的 setrequestedorientation 方法, 其可用來設定畫面的呈現方式, 是要直著 看 (SCREEN_ORIENTATION_PORTRAIT) 或是橫著看

(SCREEN_ORIENTATION_LANDSCAPE), 如下圖所示, 其它的呈現方式可參考 ActivityInfo 類別的說明 [6] 接下來可以開始來討論 ArrowView.java 了, 其內容如下 : 1 public class ArrowView extends View { 2 3 private final float alength = (float)100.0; 4 private final float arrowd = (float)10.0; 5 private final float arroww = (float)5.0; 6 7 float startx, starty, stopx, stopy, degree; 8 9 public ArrowView(Context context) { 10 super(context); 11 startx = (float)160.0; 12 starty = (float)240.0; 13 degree = (float)0.0; 14 } 15 16 protected void setdegree(float degree) { 17 this.degree = degree; 18 } 19 20 @Override 21 protected void ondraw(canvas canvas) {

22 23 Paint paint = new Paint(); 24 25 float radian = (float)(degree*math.pi/180.0); 26 stopx = startx + (float)(alength*math.sin(radian)); 27 stopy = starty - (float)(alength*math.cos(radian)); 28 canvas.drawcolor(color.white); 29 canvas.drawline(startx, starty, stopx, stopy, paint); 30 31 float v3x, v3y, diffx, diffy, leftax, leftay, rightax, rightay; 32 33 v3x = stopx + ((startx-stopx)*arrowd)/alength; 34 v3y = stopy + ((starty-stopy)*arrowd)/alength; 35 36 diffx = (float)math.abs((arroww*(stopy-starty))/alength); 37 diffy = (float)math.abs((arroww*(stopx-startx))/alength); 38 39 if ((startx-stopx) < 0.0) { 40 leftay = v3y - diffy; 41 rightay = v3y + diffy; 42 } else { 43 leftay = v3y + diffy; 44 rightay = v3y - diffy; 45 } 46 47 if ((starty-stopy) < 0.0) { 48 leftax = v3x + diffx; 49 rightax = v3x - diffx; 50 } else { 51 leftax = v3x - diffx; 52 rightax = v3x + diffx; 53 } 54 canvas.drawline(leftax, leftay, stopx, stopy, paint); 55 canvas.drawline(rightax, rightay, stopx, stopy, paint); 56 } 57 } ArrowView 繼承了 View 類別, 是一個客製化的 View, 我們只要覆寫 ondraw 方

法, 便可呈現我們所設計的 View,onDraw 會傳進一個畫布 (Canvas) 物件, 我們只要在畫布上畫出想要呈現的線條 圖形等即可設計出自己的 View, 這部份請讀者自行閱讀相關類別的說明文件 [7][8], 這裡不做太多的說明, 然而筆者還是附上一張圖, 讓讀者能夠更容易瞭解箭頭是如何畫出來的 最後一件要提醒讀者的事是, 別忘了於 AndroidManifest.xml 中聲明這個程式需 要得到精確位置資訊, 亦即要讀取 GPS 資訊 下面是執行畫面, 然而要測詴這 個程式是否正確, 還是要走出戶外 : 14.3 加速度感測器的應用 在這一節筆者將示範一個加速度感測器的應用, 這個應用程式的功能用來偵測手機是否有劇烈晃動 請讀者引進光碟中 \ 範例程式 \Chapter14\AccelerometerApp 這個專案, 裡面只有 AccelerometerApp 這個 Activity, 其內容如下 : 1 public class AccelerometerApp extends Activity { 2 3 float max;

4 TextView tv; 5 private SensorManager smgr; 6 private List<Sensor> slist; 7 boolean isstarted; 8 9 @Override 10 public void oncreate(bundle savedinstancestate) { 11 super.oncreate(savedinstancestate); 12 13 setrequestedorientation( 14 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 15 16 setcontentview(r.layout.main); 17 tv = (TextView)findViewById(R.id.tv_max); 18 Button btn_start = (Button)findViewById(R.id.btn_start); 19 btn_start.setonclicklistener(start_l); 20 Button btn_stop = (Button)findViewById(R.id.btn_stop); 21 btn_stop.setonclicklistener(stop_l); 22 23 smgr = (SensorManager)getSystemService( 24 Context.SENSOR_SERVICE); 25 26 slist = smgr.getsensorlist(sensor.type_accelerometer); 27 if (slist.size() == 0) { 28 Toast.makeText(this, "No accelerometer sensor", 29 Toast.LENGTH_SHORT).show(); 30 finish(); 31 } 32 isstarted = false; 33 } 34 35 private final SensorEventListener mlistener = new 36 SensorEventListener() { 37 public void onsensorchanged (SensorEvent event) { 38 if (event.sensor == slist.get(0)) { 39 if (isstarted == false) return; 40 41 float totalforce = (float)0.0;

42 43 totalforce += (float)math.pow( 44 event.values[0]/sensormanager.gravity_earth, 2.0); 45 totalforce += (float)math.pow( 46 event.values[1]/sensormanager.gravity_earth, 2.0); 47 totalforce += (float)math.pow( 48 event.values[2]/sensormanager.gravity_earth, 2.0); 49 50 totalforce = (float)math.sqrt(totalforce); 51 52 if (totalforce > max) 53 max = totalforce; 54 } 55 } 56 public void onaccuracychanged (Sensor sensor, int accuracy) { 57 } 58 }; 59 60 OnClickListener start_l = new OnClickListener() { 61 public void onclick(view v) { 62 if (isstarted == true) return; 63 isstarted = true; 64 max = (float)0.0; 65 smgr.registerlistener(mlistener, slist.get(0), 66 SensorManager.SENSOR_DELAY_UI); 67 tv.settext(""); 68 } 69 }; 70 71 OnClickListener stop_l = new OnClickListener() { 72 public void onclick(view v) { 73 if (isstarted == false) return; 74 isstarted = false; 75 smgr.unregisterlistener(mlistener, slist.get(0)); 76 if (max < 2.5) { 77 tv.settext("fail! Try again!"); 78 } else { 79 tv.settext("max: " + max);

80 } 81 } 82 }; 83 } 這個程式有兩個按鈕 : Start 按鈕: 其傾聽者是實作於 60~69 行, 其主要功能是啟動感測器的讀取, 並將最大值 (max) 的值做初始化 Stop 按鈕: 其傾聽者是實作於 71~82 行, 其主要功能是停止讀取感測器, 並檢查所測得的最大值 (max) 是否有大於 2.5, 若小於 2.5, 會告訴使用者其沒有盡全力晃動手機, 若大於 2.5, 則將測得的最大值告訴使用者 晃動值的計算是寫於感測器的傾聽者內 (35~58 行 ), 其主要是運用畢氏定理算出 總值 ( a 2 + b 2 + c 2 ), 這裡順便跟讀者點出一件事, 當手機歪斜著拿著不動時, 若把三軸的加速度值利用畢氏定理算出會得出接近 9.81( 重力加速度 ) 的數才對 讀者按下 Start 按鈕就可開始進行測詴, 而按下 Stop 按鈕就可觀看結果, 唯一要注意的是不要不小心把手機甩出去, 甚至砸到電視 下面分別是晃動成功 與失敗的執行結果畫面 : 14.4 摘要 本章將介紹了感測器的相關應用, 首先我們學會了如何獲得手機上的感測器清單, 並瞭解如何讀取感測器的感測值 接著我們分別學習了一個方位感測器 (Orientation Sensor) 與一個加速度感測器 (Accelerometer Sensor) 的應用 手機和感測器的結合, 讓手機產生更多的應用, 除了應用於遊戲軟體, 感測器也讓手機上

實作擴增實境變得更容易 14.5 作業 1. 寫一個程式來判斷手機的放置方式 : 平放 水平立放或垂直立放, 分別如下 圖所示 請使用加速度感測器 2. 寫一個程式來判斷手機的放置方式 : 平放 水平立放或垂直立放 這次請改 用方位感測器 3. 改寫 14.2 的程式, 選定兩個不同的目標物, 程式會出現兩個箭頭分別指向 兩個目標物 14.6 參考資料 [1] SensorManager Android Developers, http://developer.android.com/reference/android/hardware/sensormanager.html [2] Sensor Android Developers, http://developer.android.com/reference/android/hardware/sensor.html [3] SensorEventListener Android Developers, http://developer.android.com/reference/android/hardware/sensoreventlistener.html [4] SensorEvent Android Developers, http://developer.android.com/reference/android/hardware/sensorevent.html [5] Location Android Developers, http://developer.android.com/reference/android/location/location.html [6] ActivityInfo Android Developers,

http://developer.android.com/reference/android/content/pm/activityinfo.html [7] Canvas Android Developers, http://developer.android.com/reference/android/graphics/canvas.html [8] Paint Android Developers, http://developer.android.com/reference/android/graphics/paint.html