星期一, 10月 31, 2011

Fragment (一)

Android應用程式學習筆記

Fragments

Fragment用來描述在Activity上使用者介面的一部份或是行為,你可以結合多個fragments在單一activity上建立多框架的activity還有可以在多個activity重複利用單一fragment,你可以將fragment認為是activity的模件部分,fragment擁有自己的生命周期,接收自己的事件輸入,你可以在activity運行時,新增或移除fragment。

Fragment必須鑲嵌在activity中,且fragment的生命週期直接受到宿主activity的影響。例如,當activity暫停,所有嵌在activity上的fragments也會暫停;當activity被銷毀,所有嵌在activity上的fragments也會銷毀。然而當activity正在運行(在resume生命週期狀態),你可以獨立操作每個fragment,比如,增加或是移除它們。

當你增加fragment作為activity布局的一部份,它存在activity的視圖階層樹的ViewGroup中,且fragment定義自己的視圖布局,你可以通過在activity布局文件中宣告fragment標籤<fragment>,在你的activity的布局中插入fragment,或是從應用程式程式碼中插入到已存在ViewGroup。然而,fragment並不是一定要是activity布局的一部份,你也可以不為fragment設定使用者介面,讓它作為activity的隱形工作者。


小小總結:
Fragment中文來說應該就是框架,就像是網頁框架一樣,可以將網頁切割成幾個部分。所以在activity上就可以利用fragment產生畫面上的切割,並在不同部分設計達成不同需求。

星期日, 10月 30, 2011

Activity (八)

Android應用程式學習筆記

Coordinating activities

Activity A 啟動 Activity B的流程:
1.執行Activity A的onPause()方法。
2.依序執行Activity B的onCreate()、onStart()、onResume()方法。
3.然後,如果Activity A不再顯示在屏幕,執行onStop()方法。

了解流程可以幫助你管理從activity轉換到另一個activity的資訊。比如,你必須在第一個activity停止前將數據寫入資料庫,如此讓接下來的activtiy可以讀取,你應該在onPause()方法執行寫入資料庫的工作,而不是onStop()。

Activity (七)

Android應用程式學習筆記

接著Activity (六)的內容繼續學習。

然而,即使你沒做甚麼和沒有實現onSaveInstanceState()方法,Activity類預設實現onSaveInstanceState()方法還保存一些activity狀態。特別的是,預設會為布局的每個視圖呼叫onSaveInstanceState()方法,允許每個視圖提供應該被儲存的資訊,在Android架構下幾乎每個部件都會實現這個方法,這樣當你的activity再次建立時,在使用者介面上的任何可見的改變都可以自動保留儲存起來。比如,EditText部件會保存用戶輸入的文字,CheckBox部件保存用戶是否check。這些只會在當你提供你想保存狀態的部件的ID(android:id屬性)才有作用,如果部件沒有ID,就無法保存狀態。

雖然預設會執行onSaveInstanceState()方法來保存與activity的使用者介面有關的有用資訊,你仍可能需要複寫onSaveInstanceState()方法來保存額外資訊。比如,你可能需要保存在activity的生命中改變的變數值。

因為預設實現的onSaveIntanceState()方法幫助儲存使用者介面的狀態,如果你複寫此方法來保存額外資訊,你應該在執行任何工作之前呼叫superclass實現onSaveInstanceState()

測試你的應用程式有能力保存狀態的做簡單方法就是轉動裝置,讓屏幕方向改變。當屏幕方向改變,系統為了恢復資源銷毀又在建立activity可能利用為新的方向,單獨為此理由,你的activity能保存轉動屏幕的狀態是重要的,這樣用戶就不會經常在使用應用程式時還要轉動屏幕。

以下圖示,左邊顯示當activity被停止然後恢復,activity的狀態保持原封不動。右邊顯示當activity被銷毀然後再次建立,activity必須保存先前的activity狀態。


Activity (六)

Android應用程式學習筆記

Saving activity state

介紹Managing the Activity Lifecycle簡單地提到在activity被暫停或是停止,狀態是可以被保留的。這是真的,因為在暫停或停止時,Activity物件仍保留在記憶體中-所有有關的資訊及目前狀態仍是活的。因此用戶在activity上產生的任何改變都會保留在記憶體中,以便當activity回到前台,這些改變仍能存在。

然而當系統為了恢復記憶體而銷毀activity,Activity物件就會被銷毀,所以無法輕易以它完整無缺的狀態恢復activity。代替的方法就是,如果用戶操作回到activity,系統必須再次建立Activity物件。到目前為止,用戶不會察覺系統銷毀activity並重新建立activity,因此期望activity能精確地回到之前的狀態。在此情況下,你能擔保有關activity狀態的重要資訊被保存下來,藉著實現額外的回調方法來保存有關你的activity狀態的資訊然後在系統重新建立activity時恢復。

保存與目前activity狀態有關的資訊的回調方法為onSaveInstanceState()方法,系統呼叫此方法之前,使activity容易銷毀,並傳入Bundle物件。Bundle物件是你保存與activity有關的狀態資訊的地方,比如鍵值對,利用一些方法將值放入Bundle物件,比如putString()。然後,如果系統殺掉你的activity後用戶操作回到你的activity,系統將Bundle物件傳到onCreate()方法,所以你可以恢復到在onInstanceState()方法中保存的activity狀態。如果沒有任何資訊需要恢復,Bundle傳入onCreate()方法為null。


星期六, 10月 29, 2011

Activity (五)

Android應用程式學習筆記

我們把生命周期的所有回調方法列在以下表中,對每個回調方法進行描述以及它是否可以被系統殺掉。


MethodDescriptionKillable after?Next
onCreate()Always followed by onStart().當activity第一次被建立時呼叫該方法,該方法用來完成靜態設置-建立視圖、為lists綁定數據等等。該方法傳入一個Bundle物件的參數,Bundle物件包含了activity前一次的狀態。該方法執行完後,接著一定執行onStart()方法。NoonStart()
    onRestart()
Activtiy已經被停止之後,再次啟動前呼叫onRestart()方法。接著執行的一定是onStart()方法。
NoonStart()
onStart()Activity在讓用戶看見之前呼叫該方法。如果activity到達前台,呼叫onResume()方法;如果activity隱藏起來,呼叫onStop()方法。NoonResume()
or
onStop()
    onResume()在activity開始與用戶互動之前呼叫該方法。Activity在此點是位在activity佇列的頂端,可以接收用戶的輸入。接著一定呼叫onPause()方法。NoonPause()
onPause()當系統開始恢復其他activity時呼叫此方法。該方法是典型用來將為儲存的變動交給永久數據、停止動畫及其他可能會消耗CPU的工作,onPause()裡的工作應該盡可能快速地完成,因為下一個activity將不會被恢復,直到onPause()裡的工作完成。
如果activity回到前台,接著就會呼叫onResume()方法;如果變成用戶看不見,接著就會呼叫onStop()方法。
YesonResume()
or
onStop()
onStop()當activity不再被用戶看見時呼叫onStop()方法,不被用戶看見有可能是因為activity即將被銷毀或是其他的activity已經恢復且覆蓋activity。
如果activity正回來與用戶互動,就會接著呼叫onRestart()方法;如果activity正要停止,接著就會呼叫onDestory()方法。
YesonRestart()
or
onDestroy()
onDestroy()Activity銷毀之前呼叫該方法,這是activity最後接收的呼叫。可能因為activity正在停止(呼叫finish()方法),或是因為系統臨時銷毀activity的使用空間。你可以利用isFinishing()方法分辨這兩個種可能。Yesnothing
注意"Killalbe after"是表示系統是否會在回調方法執行完後的任何時間殺掉activity。三個回調方法標記為"Yes",分別是onPause()、onStop()、onDestroy(),因為onPause()方法是三個方法的第一個,activity一旦被建立,onPause()方法肯定是在程序被殺掉之前最後呼叫的方法-如果系統必須緊急恢復記憶體空間,接著onStop()和onDestroy()可能不會被呼叫。因此你應該利用onPause()方法將決定性數據寫到記憶體。然而你應該選擇保留一些資訊,因為在方法中的任何阻礙程序會阻塞轉到下一個activity而降低用戶經驗。

表記為"No"的回調方法,保護程序不會被殺掉。因此當activity是可被殺的,從onPause()時段到onResume()時段,直到再次呼叫onPause()方法,activity才又可以被殺掉。

Activity (四)

Android應用程式學習筆記

Shutting Down an Activity

呼叫finish()方法就可以關閉activity,你也可以從另外一個activity呼叫finishActivity()方法關閉activity。


Managing the Activity Lifecycle

藉由回調方法管理activity的生命週期對於開發強健且靈活的應用程式來說是相當重要的,activity的生命週期直接受到與它相關的其他activity,它的task及back stack所影響。

activity有三個基本的狀態:
Resumed
Activity運行在前台且擁有用戶焦點。
Paused
其他Activity運行在前台且擁有用戶焦點,但是activity仍是可見的,那就是其他activities顯示在它的上面,其他activtiy部分是透明的或是不會完全覆蓋整個屏幕。被暫停的activity仍是活的,但是系統可能在極低記憶體的狀態下殺掉被暫停的activity。 
Stopped
Activity完全被其他activtiy遮掩(activity現在運行於後台),被停止的activity也仍是活的,然而被停止的activity不會被用戶看見且它可能被系統殺掉,當系統需要記憶體空間時。
如果activity處在暫停或是停止狀態,系統可以從記憶體停止這些activity,不論是要求它們結束或是殺掉它們的程序,當'它們再次被啟動,再全部重建。


Implementing the lifecycle callbacks

當activity轉換狀態,會接收許多回調方法通知。所有回調方法都可以複寫來完成在狀態改變時適合的工作,以下就是生命週期中所有回調方法:



public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}


這些方法定義了activtiy的整個生命週期,藉由實現這些方法,你可以注意到在生命週期中有三個巢狀迴圈:

  • Entire lifetime-Activity的entire lifetime發生在呼叫onCreate()方法與呼叫onDestory()方法之間,你的activity應該在onCreate()方法中設置"總體"狀態(比如定義布局),在onDestory()方法中釋放所有剩下的資源。例如,如果你的activity有一個執行緒在後台執行從網路下載資料的工作,activtiy在onCreate()方法中建立執行緒,而在onDestory()方法中停止執行緒。
  • Visible lifetime-Activity的visible lifetime發生在呼叫onStart()方法與onStop()方法之間,在此時段中,用戶可以在屏幕上看見activity並與它互動。例如,當新的activity啟動而activity不再可見時,onStop()方法被呼叫。在onStart()與onStop()之間,你可以保有顯示activity給用戶的資源。例如,你可以在onStart()方法註冊BroadcastReciever監視影響UI的改變,在onStop()方法撤銷BroadcastReciever。系統可能在activity的生命週期中多次呼叫onStart()方法和onStop()方法,當activity對於用戶為可見與隱藏之間交替時。
  • Foreground lifetime-Activity的foreground lifetime發生在呼叫onResume()方法與呼叫onPause()方法之間,在此時段中,activity在屏幕上是在所有其他activity前面且擁有用戶焦點。activity會頻繁地轉進與轉出前台,例如,當裝置進入睡眠狀態時呼叫onPause()方法,或是當一個對話框出現。因為經常在此狀態轉換,這兩個方法在程式碼中應該要公平,避免變慢造成用戶在此狀態轉換時需要等待。
下圖顯示這些loops及兩狀態之間的路徑。

在這裡做個小小總結:

1.關閉activity有兩個方法,呼叫finish()及finishActivity()。
2.Activtiy生命週期中有三個迴圈,Entire lifetime、Visible lifetime、Foreground lifetime。
3.Entire lifetime為onCreate()與onDestory()之間,為activity的整個生命週期,主要執行取得資源與釋放資源的工作。
4.Visible lifetime為onStart()與onStop()之間,為activity可以被看見的時間,主要執行與使用者介面相關的工作。
5.Foreground lifetime為onResume()與onPause()之間,為activity被用戶使用的時間。
6.當執行到onCreate()時,用戶還未看見activity,當執行到onStart()時,用戶才看見activtiy。

星期三, 10月 26, 2011

Activity (三)

Amdroid應用程式學習筆記

Starting an Activity

你可以呼叫startActivity()啟動其他activity,傳入Intent物件來描述你想啟動的activity,Intent不是明確指定你想起動的activity就是描述你想執行的動作類型(系統會為你挑選適合的activity,可能是不同應用程式的activity),Intent物件也可以攜帶小量的數據到你想啟動的activity使用。

當你在自己的應用程式完成啟動其他activity,通常都是啟動已知的activity,你可以建立一個intent明確地用類別名稱定義你想啟動的activity是哪個,以下就是一個activity如何啟動另一個名稱為SignInActivity的activity的例子。


Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

然而,你的應用程式也想執行一些動作,例如發送電子郵件、編輯簡訊或是狀態更新,既然這樣,你的應用程式也許沒有自己的activities可以執行這些動作,所以你可以使用在裝置上其他應用程式所提供的activity,且這些activity可以執行動作。這是intent有價值的地方-你可以建立一個intent描述你想執行的動作,然後系統啟動來自其他應用程式適合的activity。如果有許多activity都可以完成intent描述的動作,用戶可以選擇其中一個使用。舉例,你想允許用戶寄發電子訊息,你可以如此建立intent:


Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

EXTRA_EMAIL額外加到intent的是電子郵件地址的字串陣列,當電子郵件應用程式回應這個intent,應用程式讀取intent中的字串陣列。在此狀況,電子郵件應用程式啟動,用戶用完,你的activity就會恢復。


Starting an activity for a result


有時候你可能想要從你啟動的activity接收結果,既然這樣,呼叫startActivityForResult()方法啟動activity取代startActivity()方法,從被啟動的activity接收結果,實現onActivityResult()回調方法,以下為如何建立intent與處理結果:


private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

在此例子中,演示了你使用onActivityResult()方法處理activity結果的基本邏輯,第一個條件式檢查請求是否成功-如果成功,resultCode為RESULT_OK-回應的請求是否已知,requestCode配對startActivytForResult()的第二個參數。

contentResolver執行查詢,回傳一個Cursor物件,通過cursor物件讀取結果。

星期二, 10月 25, 2011

Activity (二)

Android應用程式學習筆記

Creating an Activity


建立一個activity,你必須建立Activity類的子類,在子類,你需要實現回調方法,系統會在activity專換不同生命週期狀態時呼叫,比如,activity建立、停止、恢復、銷毀,兩個最重要的回調方法是:
onCreate()
你必定實現此方法,系統建立你的activity時呼叫此方法,在實現此方法中,你應該初始化你的activity中重要的變數。最重要的是,這是你必須呼叫setContentView()方法去顯示activity的使用者介面的地方。
onPause()
當用戶離開你的activity時,系統呼叫此方法, 這通常是你應該堅持保存任何改變超過目前用戶的使用期間。


Implementing a user interface

 Activity的使用者介面是由view的階層所提供,每個view控制activity的視窗中特定的矩形空間,並且可以回應用戶的互動,比如,view可以是一個按鈕,當用戶觸碰它時啟動動作。

Android提供許多現成的views,你可以使用來設計及組織你的布局,"Widget"是view,桌面的小工具,"Layouts"由ViewGroup衍生的View,比如,線性布局或是相關布局,你可以運用view及viewgroup來創造你的桌面小工具或是布局,然後應用在你的activity上。

使用view定義布局最普遍的方式是存在你的應用程式資源中的XML布局文件,這樣的方式,你可以維護從定義應用程式行為的程式碼中分離的使用者介面的設計。你可以利用setContentView()方法傳入布局文件的ID,將布局文件設置為使用者介面。


Declaring the activity in the manifest

你必須在manifest文件中宣告你的activity,這樣系統才可以存取。宣告activity,打開你的manifest文件,並在<application>標籤範圍內加入<activity>標籤。例如:


<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...</manifest >

有許多屬性你可以包含在此標籤中去定義activity的屬性,比如activity的標題、activity的圖像或是activity的使用者介面主題風格。android:name屬性是唯一一定要有的屬性。一旦你發行了應用程式,你就不應該改變這個名稱,因為如果你改變了,可能會破壞一些功能,比如,應用程式的捷徑。


Using intent filters

<activity>標籤也可以利用<intent-filter>指定多個intent過濾器,為了就是讓其他應用程式也可以活化它。

當你使用Android SDK工具建立新的應用程式時,工具自動地為你建立activity,它包含一個intent過濾器用來回應"main"動作,intent過濾器看起來像:


<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<action>標籤指定了activity是應用程式的"main"的進入點,<category>標籤指定該activity應該被列表在系統上,讓用戶可以啟動它。

如果你不想允許你的應用程式受其他應用程式的activities活化,那你就不需要任何intent過濾器,只需要有"main"action和"Launcher"cagetory。

然而,如果你想你的activity回應從其他應用程式傳來的明確intent,那就必須為你的activity另外加上intent過濾器,每個過濾器必須有<intent-filter>標籤,<intent-filter>標籤必須包含<action>標籤,選擇性包含<category>標籤及<data>標籤,這些標籤決定你的activity回應那一種形式的intent。

星期二, 10月 18, 2011

MapView

Android應用程式學習筆記

MapView

顯示地圖的View,從Google Map Service取得數據。當它為焦點時,它可以抓取按鍵和觸碰手勢去搖鏡頭和變焦地圖。你也可以在程式碼中控制(getController())和可以繪製Overlay物件在地圖上(getOverlays())。

地圖可以顯示許多種模式:setSatellite(boolean)設定為衛星地圖、setTracffic(boolean)、setStreetView(boolean)。

首選的變焦機制就是內建的變焦機制,setBuildInZoomControls(boolean),當用戶搖鏡頭時,變焦控制會自動出現在MapView的底部。

MapView也是一個ViewGroup,LayoutParameters允許你附加其他View。

MapView只能藉由MapActivity建立,這是因為MapView必須依賴在後台存取網路及檔案系統的執行續。這些執行續的生命週期是由MapActivity看管,Tips是在應用程式目錄下緩衝的檔案系統,緩衝是自動管理,所以你不需要做任何事。

為了在MapView顯示Google Map,你必須註冊Google Map Service並獲得Map API 金鑰,可以參考如何利用Eclipse取得MD5 Fingerprint文章。

一旦你取得金鑰,你需要在XML布局文件中MapView的android:apiKey屬性上引用它。

Package com.google.android.maps

Android應用程式學習筆記

Package com.google.android.maps

此地圖套件允許應用程式顯示及控制Google Map介面,建立一個Google Map,你需要繼承MapActivity並且在布局上實現一個MapView。

為了在應用程式中使用MapView,你需要一個有"android:apiKey"屬性的MapView。

注意此套件並不是Android函式庫的標準套件,為了使用它,你必須在Androidmanifest文件中新增xml標籤在<application>標籤範圍中:

<uses-library android:name="com.google.android.maps" />


星期一, 10月 17, 2011

如何利用Eclipse取得MD5 Fingerprint

Android應用程式學習筆記

如何利用Eclipse取得MD5 Fingerprint

為何需要取得MD5 Fingerprint?
因為如果是想使用Google Map API就必須取得使用金鑰,那取得金鑰必須先取得MD5 fingerprint,再到官網產生金鑰。得到金鑰就能在應用程式中使用MapView等Google Map APIs。

取得方式有許多,可以使用指令完成,那對於指令不熟該怎麼辦?

以下就是不須透過指令,也一樣可以取得MD5 fingerprint的流程。

1.打開Eclipse。
2.Help > Install New Software。
3.在work with:中輸入http://keytool.sourceforge.net/update。
4.等到Name表中出現"Keytool",選取"Keytool"。
5.點擊Next。
6.當一切安裝完成時,在Eclipse中會出現"Keytool"菜單。



7.點擊Keytool > Open keystore。
8.點擊"Browse"然後在.android目錄下找到"debug.keystore"。
9.輸入android為密碼。
10.雙擊"androiddebugkey",顯示MD5 Fingerprint。



取得MD5 Fingerprint到Maps API Key Signup產生金鑰。

星期日, 10月 16, 2011

LocationManager LocationProvider LocationListener

Android應用程式學習筆記

LocationManager
此類提供存取系統定位服務,這些服務允許應用程式定期獲得裝置地理位置的更新,或是啟動應用程式指定的Intent,當裝置接近預設的地理位置時。 
你無須直接實現此類,透過context.getSystemService(Context.LOCATIO_SERVICE)取得LocationManager。
以下介紹幾個此類中的方法。
 public LocationProvider getProvider(String name)-取得名稱為name的LocationProvider。如果沒有此LocationProvider,回傳值為null。
public List<String> getProviders(boolean EnableOnly)-取得一組LocationProviders。參數值為true表示只回傳目前可用的LocationProvider。
public Location getLastKnownLocation(String provider)-回傳provider最後得知的位置。 
public void requestLocationUpdates(String provider , long minTime , float minDistance , LocationListener listener)-註冊目前的activity定期接受provider的通知,隨著當前位置不同或是狀態更新定期地呼叫LocationLListener。它也許要花一些時間接收最近期的位置,如果需要立即接收位置,應用程式可以利用getLastKnownLocation(String)方法。用戶關閉provider的情況下,更新將停止,並且呼叫onProviderDisable(String)方法。當provider再次啟動,onProviderEnable(String)將被呼叫,且位置更新將再次啟動。利用minTime和minDistance參數控制通知的頻率。如果minTime參數值大於0,LocationManager有機會在位置更新之間休息minTime毫秒來保存電力;如果minDistance參數值大於0,裝置移動超過minDistance距離,廣播一個位置。想盡可能的獲得通知,就將兩個參數設為0。後台服務應該留意設定充分的minTime,如此裝置不會消耗太多電力在保持GPS或是無線電台,特別是,不建議minTime數值小於6000毫秒。
LocationProvider
Location provider提供有關裝置的地理位置的定期報告,每個提供器有一組標準,在標準之下也許可以使用。舉例,一些提供器需要GPS硬體設備及許多衛星的能見度;其他需要蜂巢式無線電,或是存取特定載體的網路,或是網路。它們也有不同的電池消耗特性或是成本。
LocationListener
利用於接收當位置改變時LocationManager的通知,利用reuqestLocationUpdates(String , long , float , LocationListener)方法將監聽器註冊到位置管理服務(LocationManager)。
public void onLocationChanged(Location location)-當位置改變時呼叫此方法。
public void onProviderEnable(String provider)-當提供器啟用時呼叫此方法。
public void onProviderDisable(String provider)-當提供器關閉時呼叫此方法。
public void onStatusChanged(String provider , int status , Bundle extras)-當provider狀態改變時呼叫此方法。
 

實作Location的取得與LocationListener的設置

實作Location的取得與LocationListener的設置


1.在main.xml新增一個TextView

  1. <TextView 
  2. android:text="TextView" 
  3. android:id="@+id/location" 
  4. android:layout_width="wrap_content" 
  5. android:layout_height="wrap_content">
  6. </TextView>
2.編輯activity
  1. package test.location;

  2. import java.util.List;

  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.location.Location;
  6. import android.location.LocationListener;
  7. import android.location.LocationManager;
  8. import android.os.Bundle;
  9. import android.widget.TextView;

  10. public class HelloLocationActivity extends Activity {
  11.     /** Called when the activity is first created. */
  12. //創建新專案,注意創建時,target build選擇Google APIs,這是為了添加jar文件maps.jar。
  13. //引入LocationManager實例。
  14. //在main文件中宣告TextView。
  15. //在Androidmanifest文件中宣告權限。
  16. private LocationManager locationManager;
  17.     @Override
  18.     public void onCreate(Bundle savedInstanceState) {
  19.         super.onCreate(savedInstanceState);
  20.         setContentView(R.layout.main);
  21.         //呼叫getSystemService()取得LocationManager物件。
  22.         locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
  23.         locate();
  24.     }
  25.     
  26.     private void locate(){
  27.      TextView tv = (TextView)findViewById(R.id.location);
  28.      StringBuilder builder = new StringBuilder("可利用的provider");
  29.      //呼叫getProviders(boolean EnableOnly)取得一組可用的location provider名稱,true表示為可用的;false表示停用的。
  30.      List<String> providers = locationManager.getProviders(true);
  31.      //定義位置監聽器,接收來自LocationManager的通知。
  32.      LocationListener locationlistener = new LocationListener(){
  33.      //位置管理服務利用reuqestLocationUpdates(String , long , float , LocationListener)方法註冊位置監聽器後,這些方法就會被呼叫。
  34.      //Provider狀態改變時呼叫此方法。
  35.      public void onStatusChanged(String provider , int status , Bundle extras){
  36.     
  37.      }
  38.      //位置改變時呼叫此方法。
  39. @Override
  40. public void onLocationChanged(Location location) {
  41. // TODO Auto-generated method stub
  42. System.out.println("onLcationChanged");
  43. }

  44. //用戶關閉provider時呼叫此方法。
  45. @Override
  46. public void onProviderDisabled(String provider) {
  47. // TODO Auto-generated method stub
  48. System.out.println("onProviderDisabled");
  49. }

  50. //用戶啟動provider時呼叫此方法。
  51. @Override
  52. public void onProviderEnabled(String provider) {
  53. // TODO Auto-generated method stub
  54. System.out.println("onProviderEnabled");
  55. }
  56.      };
  57.     
  58.      for (String provider:providers){
  59.      //註冊目前的activity定期由provider通知位置更新。
  60.      locationManager.requestLocationUpdates(provider, 0, 1000, locationlistener);
  61.     
  62.      builder.append("\n").append(provider).append(":");
  63.      //回傳從provider最後所知的位置。
  64.      Location location = locationManager.getLastKnownLocation(provider);
  65.      if(location != null){
  66.      double lat = location.getLatitude();
  67.      double lng = location.getLongitude();
  68.      builder.append("(");
  69.      builder.append(lat);
  70.      builder.append(",");
  71.      builder.append(lng);
  72.      builder.append(")");
  73.     
  74.      }else{
  75.      builder.append("沒有訊息");
  76.      }
  77.      }
  78.      tv.setText(builder);
  79.     }
  80. }
3.記得在Androidmanifest文件中</application>後宣告權限,此宣告為採用GPS_PROVIDER。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 4.在真實的設備當中要獲得定位服務需要有GPS硬體支援,但在開發或測試,使用模擬器是最方便的。將Eclipse切換到DDMS模式,在Emulator control中可以找到定位服務選項,選項可以手動發送經緯度、GPX、KML格式數據來測試定位服務。如果發現選項無法調整及發送(呈現灰色),原因可能是尚未選擇裝置,在Devices中選擇裝置。

          星期六, 10月 15, 2011

          Location and Maps (三)

          Android應用程式學習筆記

          Defining a Model for the Best Performance

          以定位為基礎的應用程式現在已司空見慣,但是由於不到理想準確度、用戶移動、眾多獲得位置的方法、保存電力,因此取得用戶位置變得複雜。為克服取得一個好的用戶位置又兼顧維護電力的障礙,你必須定義兼容的模型,模型指定應用程式如何取得用戶位置。模型包括何時開始及停止監聽更新,何時使用緩存的位置數據。


          Flow for obtaining user location

          以下是獲得用戶位置的典型流程:

          1. 開始應用程式。
          2. 一段時間後,開始監聽預期位置提供器的更新。
          3. 維護篩選出新的"目前最佳估計"位置,但缺乏固定準確度。
          4. 停止監聽更新。
          5. 利用最後最佳位置估計的優勢。
          圖一演示在時間軸上的模型,視覺化應用程式何時監聽位置更新及事件發生的時間。



          Deciding when to start listening for updates

          你可能會想在應用程式啟動時就開始監聽更新,或只在用戶激活某功能時。小心較長的監聽位置窗口消耗較多的電力,但較短又無法達到足夠的準確度。

          如上述,你可以呼叫requestLocationUpdates()方法監聽位置更新。


          LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
          // Or, use GPS location data:
          // LocationProvider locationProvider = LocationManager.GPS_PROVIDER;
          
          locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);

          Getting a fast fix with the last known location

          你的位置監聽器鳩收第一個位置所花的時間通常為用戶等待時間過長,直到更加正確的位置提供給你的位置監聽器,你應該呼叫getLastKnownLocation()方法使用暫存的位置。


          LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
          // Or use LocationManager.GPS_PROVIDER
          Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);

          Deciding when to stop listening for updates

           決定新的修復的邏輯不再需要根據你的應用程式從簡單到複雜的,何時獲得定位和何時需要使用定位之間最短的距離,改善估計的準確度,永遠都要注意長時間的監聽會消耗許多電力,所以當你取得你需要的資訊,就應該呼叫removeUpdates(PendingIntent)立即停止監聽更新。


          // Remove the listener you previously added
          locationManager.removeUpdates(locationListener);



          Maintaining a current best estimate

          你也許會期望大部分的位置修復是準確的,然而,因為位置修復的準確度多變,大部分的修復都不是最佳的,你必須有邏輯地從許多標準選擇位置修復,標準也依應用程式的使用與測試領域變化。

          以下有幾個步驟可以驗證位置修復的準確度:

          • 檢查檢索的位置是否明顯的比前一次估計的新。
          • 檢查定位聲明的準確度是否比前一次估計的來的更好或更糟。
          • 檢查新的位置是哪一個提供器及決定更相信哪一個提供器。
          以下是邏輯的闡述例子:


          private static final int TWO_MINUTES = 1000 * 60 * 2;
          /** Determines whether one Location reading is better than the current Location fix
            * @param location  The new Location that you want to evaluate
            * @param currentBestLocation  The current Location fix, to which you want to compare the new one
            */
          protected boolean isBetterLocation(Location location, Location currentBestLocation) {
              if (currentBestLocation == null) {
                  // A new location is always better than no location
                  return true;
              }
          
              // Check whether the new location fix is newer or older
              long timeDelta = location.getTime() - currentBestLocation.getTime();
              boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
              boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
              boolean isNewer = timeDelta > 0;
          
              // If it's been more than two minutes since the current location, use the new location
              // because the user has likely moved
              if (isSignificantlyNewer) {
                  return true;
              // If the new location is more than two minutes older, it must be worse
              } else if (isSignificantlyOlder) {
                  return false;
              }
          
              // Check whether the new location fix is more or less accurate
              int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
              boolean isLessAccurate = accuracyDelta > 0;
              boolean isMoreAccurate = accuracyDelta < 0;
              boolean isSignificantlyLessAccurate = accuracyDelta > 200;
          
              // Check if the old and new location are from the same provider
              boolean isFromSameProvider = isSameProvider(location.getProvider(),
                      currentBestLocation.getProvider());
          
              // Determine location quality using a combination of timeliness and accuracy
              if (isMoreAccurate) {
                  return true;
              } else if (isNewer && !isLessAccurate) {
                  return true;
              } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
                  return true;
              }
              return false;
          }
          /** Checks whether two providers are the same */
          private boolean isSameProvider(String provider1, String provider2) {
              if (provider1 == null) {
                return provider2 == null;
              }
              return provider1.equals(provider2);
          }


          Adjusting the model to save battery and data exchange

          當你測試你的應用程式,你可能發現你的模型要提供優異的定位及優異的表現需要一些調整,這裡也一些事你可能需要改變找出兩著之間的平衡。

          Reduce the size of  the window

          較小的監聽位置更新視窗意味著與GPS和Network location services較少的互動,因此保住電池的壽命,但是它也提供較少的位置選擇最佳的估計。

          Set the location providers to return updates less frequently

          減少新的更新出現在視窗的頻率也可以改善電池的壽命,但是準確度較低,這兩者之間的權衡依賴你的應用程式如何使用,你可以減少更新頻率,透過呼叫requestLocationUpdates()方法中的參數。

          Restrict a set of providers

          依據你的應用程式使用或是準確度的需求的環境,選擇只採用Network location provider或是只採用GPS,取代兩者皆採用,