星期一, 11月 07, 2011

Fragment (六)

Android應用程式學習筆記

Adding a fragment without a UI

之前文章都是有關如何增加fragment到activity提供UI,然而,也可以使用fragment為activity提供後台行為,沒有額外的UI。

新增一個沒有UI的fragment,從activity利用add(fragment , String)方法(為fragment提供唯一的字串"Tag",-而不是view ID)增加fragment。這樣就新增了fragment,但是,因為不會牽涉到activity佈局中的View,就不用呼叫onCreateView(),所以你不需要實現此方法。

為fragment提供字串Tag並不限制在沒有UI的fragment-你也可以提供字串Tag給有UI的fragment,但是如果fragment沒有UI,那麼字串Tag是唯一能辨識他們的方式。如果你想從activity取得fragment,你需要利用findFragmentByTag()方法。


Fragment (五)

Android應用程式學習筆記

Adding a fragment to an activity

通常,fragment會貢獻一部份使用者介面在寄主activity,有兩種方式你可以將fragment增加到activity佈局上。
  • Declare the fragment inside the activity's layout file
    在此方法中,你可以設定fragment的屬性,以下舉例在activity的layout中增加兩個fragment。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <fragment android:name="com.example.news.ArticleListFragment"
                android:id="@+id/list"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
        <fragment android:name="com.example.news.ArticleReaderFragment"
                android:id="@+id/viewer"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
    </LinearLayout>

    在<fragment>標籤範圍中的android:name屬性具體指定了在layout中實體化的Fragment類。
    當系統建立此activity布局時,它實體化佈局中的每個fragment並且呼叫每個fragment的onCreateView()方法去取得每個fragment的布局,系統將onCreateView()回傳的View直接插入<fragment>標籤的位置。
  • Or programmatically add the fragment to an existing ViewGroup
    在activity運行的任何時間,你可以增加fragment到你的activity布局,你需要指定fragment該置於哪個ViewGroup。在activity布局處理fragment的相關工作(比如新增、移除或是取代fragment),你必須使用FragmentTransaction,你可以從你的activity取得FragmentTransaction實例,如下。
  • FragmentManager fragmentManager = getFragmentManager()
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    你接著可以利用add()方法增加fragment,指定新增的fragment和插入fragment的View。
    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
    第一個傳入add()方法是fragment要置入的ViewGroup,由ID來表示,第二個參數是要置入ViewGroup的fragment。一旦你已在FragmentTransaction做了你的改變就必須呼叫commit()方法。

Fragment (四)

Android應用程式學習筆記

Adding a user interface

Fragment通常做為activity的使用者介面的一部份,且貢獻fragment自己的布局給activity。

要提供布局給fragment,你必須實現onCreateView()方法,當該是fragment繪製它的布局的時候,系統呼叫onCreateView()方法。此方法必須回傳一個View物件,該View物件為fragment布局的根節點。

如果你的fragment是ListFragment的子類,預設從onCreateView()回傳為一個ListView。

從onCreateView()回傳一個布局,你可以透過XML布局資源擴張布局,onCreateView()提供了LayoutInflater物件來幫助你擴張布局。

舉例,以下是一個Fragment的子類,從example_fragment.xml文件取得一個布局。


public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

傳入onCreateView()的參數container是parent ViewGroup(來自activity的布局),你的fragment的布局將被放入ViewGroup中。參數savedInstanceState是一個Bundle物件,如果前一個fragment恢復,提供前一個fragment相關的數據。

inflate()方法有三個參數:
  • 你想擴張的布局的ID。
  • ViewGroup成為擴張布局的parent。
  • 布林值是要表示擴張的布局是否要在擴張時依附在ViewGroup上(在此例中,布林值為fales是因為系統正準備將擴張的布局插入container中-傳入true將在布局最後建立重複的view group)。

Fragment (三)

Android應用程式學習筆記

Creating a Fragment

建立Fragment,必須建立Fragment的子類,Fragment類有很多和Activity類很像的方法,包括回調方法,比如,onCreate()、onStart()、onPause()和onStop()。事實上,如果你正在將已存在的Android應用程式轉換成使用Fragment,你可能能輕鬆地將程式碼從Activity的回調方法移到fragment對應的回調方法中。

通常,你至少應該執行以下的生命週期方法:
onCreate()
當建立fragment時系統呼叫此方法。在方法中,應該為重要的組件完成初始化的動作。
onCreateView()
當輪到fragment繪製自己的使用者介面的時候,系統呼叫此方法。為你的fragment繪製使用者介面,你必須從此方法回傳一個View物件,View物件是你的fragment布局的根結點,如果你的fragment沒有提供使用者介面,你可以回傳null。
onPause()
系統呼叫此方法作為用戶離開fragment的第一個提示。此方法通常是承擔任何改變的地方,應該保持超過目前用戶使用期間(因為用戶可能不會再回來)。
大部分的應用程式都應該至少為每個fragment實現這三個方法,但是也有許多其他回調方法可以使用來處理不同階段的生命週期。

也有一些子類你可以繼承,取代Fragment類:

DialogFragment
顯示一個浮動對話框,比起使用Activity類中的對話框輔助方法,使用此類建立對話框是一個好選擇。因為你可以將fragment對話框併入由activity管理的fragment的back stack。
ListFragment
顯示一列由適配器管理的項目列表,類似ListActivity,它提供許多方法管ListView,比如,onListItemClick()回調方法來處理click事件。
PreferenceFragment
類似PrefereneceActivity,顯示Preference物件的階層如列表。

星期五, 11月 04, 2011

實作並觀察Activity生命週期

Android應用程式學習筆記

實作說明: 試著觀察生命週期。我們運行兩個activity,並複寫所有生命週期中的回調方法,通過activity A啟動activity B和activity B啟動activity A觀察兩個activity的生命週期變化。

Activity A(HelloActivityActivity):


  1. package test.activity;

  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.Button;

  7. public class HelloActivityActivity extends Activity {
  8.     /** Called when the activity is first created. */
  9. private Button mBtn;
  10.     @Override
  11.     public void onCreate(Bundle savedInstanceState) {
  12.      //初始化所有成員變數
  13.         super.onCreate(savedInstanceState);
  14.         setContentView(R.layout.main);
  15.         System.out.println("Activity1---onCreate");
  16.         mBtn = (Button)findViewById(R.id.button1);
  17.     }

  18.     public void GoToActivity2(View view){
  19.      Intent intent = new Intent(HelloActivityActivity.this , activity2.class);
  20.      startActivity(intent);
  21.     }
  22. @Override
  23. protected void onDestroy() {
  24. // TODO Auto-generated method stub
  25. //釋放所有資源
  26. super.onDestroy();
  27. System.out.println("Activity1---onDestory");
  28. }

  29. @Override
  30. protected void onPause() {
  31. // TODO Auto-generated method stub
  32. //此方法執行後,activity失去用戶焦點,
  33. super.onPause();
  34. System.out.println("Activity1---onPause");
  35. }

  36. @Override
  37. protected void onRestart() {
  38. // TODO Auto-generated method stub
  39. super.onRestart();
  40. System.out.println("Activity1---onRestart");
  41. }

  42. @Override
  43. protected void onResume() {
  44. // TODO Auto-generated method stub
  45. //此方法執行後,activity取得用戶焦點,
  46. super.onResume();
  47. System.out.println("Activity1---onResume");
  48. }

  49. @Override
  50. protected void onStart() {
  51. // TODO Auto-generated method stub
  52. //此方法執行後,用戶看的見activity,置方法監控影響UI的事件,隨時隨環境變化UI
  53. super.onStart();
  54. System.out.println("Activity1---onStart");
  55. }

  56. @Override
  57. protected void onStop() {
  58. // TODO Auto-generated method stub
  59. //此方法執行後,用戶無法看見activity,釋放所有在onStart()所有監控影響UI的方法
  60. super.onStop();
  61. System.out.println("Activity1---onStop");
  62. }
  63.     
  64. }
Activity B(activity2):
  1. package test.activity;

  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.widget.Button;

  7. public class activity2 extends Activity {
  8. private Button mBtn; 

  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. // TODO Auto-generated method stub
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.layout2);
  14. System.out.println("activity2---onCreate");
  15. mBtn = (Button)findViewById(R.id.button1);
  16. }
  17. public void BackToActivity1(View view){
  18. Intent intent = new Intent(activity2.this , HelloActivityActivity.class);
  19. startActivity(intent);
  20. }

  21. @Override
  22. protected void onDestroy() {
  23. // TODO Auto-generated method stub
  24. super.onDestroy();
  25. System.out.println("activity2---onDestory");
  26. }

  27. @Override
  28. protected void onPause() {
  29. // TODO Auto-generated method stub
  30. super.onPause();
  31. System.out.println("activtiy2---onPause");
  32. }

  33. @Override
  34. protected void onRestart() {
  35. // TODO Auto-generated method stub
  36. super.onRestart();
  37. System.out.println("activity2---onRestart");
  38. }

  39. @Override
  40. protected void onResume() {
  41. // TODO Auto-generated method stub
  42. super.onResume();
  43. System.out.println("activity2---onResume");
  44. }

  45. @Override
  46. protected void onStart() {
  47. // TODO Auto-generated method stub
  48. super.onStart();
  49. System.out.println("activity2---onStart");
  50. }

  51. @Override
  52. protected void onStop() {
  53. // TODO Auto-generated method stub
  54. super.onStop();
  55. System.out.println("activity2---onStop");
  56. }


  57. }

實驗一:執行activity A後關閉,觀察生命週期變化。
變化如下圖。

發現一個activity開始運行到出現在屏幕上會經過生命週期為onCreate()->onStart()->onResume(),關閉activity會經過生命週期為onPause()->onStop()->onDestory()。
實驗二:執行activity A後,A啟動activity  B,B再啟動A,接著按"BACK"直到結束整個activity,觀察生命週期變化。
變化如下圖。
觀察activity A啟動時會經過生命週期onCreate()->onStart()->onResume(),A啟動activity B會經過生命週期就比較複雜,首先要啟動B,A會先onPause(),接著換B要運行了,B會執行onCreate()->onStart()->onResume(),B取得用戶焦點,因此,A執行onStop()。執行到此我們來想想task狀態,task目前總共放有兩個activity,下層是最先執行的A,頂層是從A啟動的B,而從B啟動的A正在執行。現在接著按"BACK"按鈕,觀察A先執行onPause()讓在task頂層的B恢復,B執行onRestart()->onStart()->onResume(),A執行onStop()->onDedtory(),A結束,再按一次"BACK"按鈕,觀察B一樣先執行onPause()讓task中剩下的A恢復,A執行onRestart()->onStart()->onResume(),B執行onStop()->onDestory(),B結束,再按一次"BACK"按鈕,應用程式關閉,A執行onPause()->onStop()->onDestory(),A結束。


星期四, 11月 03, 2011

SQLiteOpenHelper

Android應用程式學習筆記

SQLiteOpenHelper

主要功能用於管理資料庫創建與版本的helper類。

此類實現了onCreate(SQLiteDatabase)、onUpgrate(SQLiteDatabase , int , int ),並可以選擇性地實現onOpen(SQliteDatabase),如果資料庫不存在,回呼onCreate(SQLiteDatabase)方法建立資料庫;如果版本不同或需要更新,回呼onUpgrade(SQLiteDatabase , int , int )方法資料庫升級;如果資料庫存在,回呼onOpen(SQLiteDatabase)打開資料庫。

此類別可以使ContentProvider容易地實現,避免阻塞應用程式開啟時資料庫要長時間更新。

它有兩種建構式:

public SQLiteOpenHelper(Context context , String name , SQLiteDatabase.CursorFactory factory , int version)
建立Helper物件來建立、打開、管理資料庫,此方法回傳很快,資料庫直到呼叫getWritableDatabase()或是getReadableDatabase()才會建立或是打開。  
參數: context-使用打開或是建立資料庫;name-資料庫名稱;factory-用來建立cursor物件,null表示為使用預設;version-資料庫版本,通常從1開始。如果版本太舊,呼叫onUpgrade(SQLiteDataBase , int , int )更新資料庫,如果版本太新,呼叫onDwongrade(SQLiteDatabase ,int ,int)更新資料庫。


此類實現的方法:
public void close()
關閉任何開啟的資料庫。
public SQLiteDatabase getReadableDatabase()
建立且/或開啟資料庫,回傳唯讀的資料庫。 
如getWritableDatabase()方法,此方法可能花較長的時間才會回傳,所以你不應該在應用程式的主執行續中呼叫,應該從ContentProvider.onCreate()。
returns: 回傳一個資料庫物件,直到getWritableDatabase()或是close()呼叫時才變成無效。
throws: 如果無法開啟時丟出SQLiteException例外。 
public SQLiteReadableDatabase()
建立且/或讀及寫的資料庫,第一次呼叫此方法,資料庫被開啟並且呼叫onCreate(SQLiteDatabase)、onUpgrade(SQLiteDatabase , int , int )方法,選擇性地呼叫onOpen(SQLiteDatabase)方法。
更新資料庫可能要花較長時間,你不應該在應用程式主執行續中呼叫此方法,應該從ContentProvider.onCreate()。
returns: 回傳資料庫物件 ,直到close()方法呼叫時才無效。
throws: 如果無法寫入資料庫,丟出例外SQLiteException。 
 public void onCreate(SQLiteDatabase db)
當資料庫第一次被建立時呼叫此方法,建立資料表的地方。
參數: db資料庫。 
public void onUpgrade(SQLiteDatabase db , int oldVersion , int newVersion)
資料庫升級時呼叫此方法,應該利用此方法實現摘除資料表、新增資料表或任何升級需要的工作。 

星期二, 11月 01, 2011

Fragment (二)

Android應用程式學習筆記

Design Philosophy

Android在Android 3.0介紹fragment,主要在更大的屏幕支援更動態及彈性的使用者介面設計,比如平板電腦,因為平板電腦的屏幕比手持的更大,有更多空間綁定及交換使用者介面組件,像fragment的設計允許你不需要管理複雜的視圖階層樹,藉著切割activity的布局成fragments,你變成可以在activity運行時修改activity的外觀,並且將變動保存在由activity管理的back stack中。

例如,有一個新的應用程式利用一個fragment在左邊顯示文章的列表,其他fragment在右邊顯示文章內容-兩個 fragment肩並肩地出現在同一個activity,且每個fragment都擁有自己一組生命週期回調方法來處理它們用戶輸入自己的事件。因此,取代一個activity用來選擇文章,另一個activtiy顯示文章內容,用戶可以在同一個activtiy中選擇文章及閱讀文章,見圖1。


你應該設計fragment為一個模件且可以在activity組件中重複使用,那是因為每個fragment在自己的生命周期回調方法定義了自己的布局及自己的行為,你可以在多個activity中包含同一fragment,所以你應該涉及能重複使用且避免直接從其他fragment操作fragment,這特別重要,因為模件的fragment允許你在不同大小的屏幕中改變fragment的組合。當你設計應用程式支援平板電腦及手持的裝置,你可以基於可用的屏幕空間中在不同布局配置中重複使用fragment來最佳化用戶使用經驗。例如,當無法將fragment塞入手持裝置屏幕,就必須分割fragment。