星期二, 11月 15, 2011

Fragment (十)

Adnrdoid應用程式學習筆記

Coordinating with the activity lifecycle

Fragment的寄主activity的生命週期直接影響fragment的生命週期,如此,activity的生命週期回呼方法與fragment的生命週期回呼方法類似,例如,當activity接收onPause()方法,在activity中的每個fragment也接收onPause()方法。

Fragment也有一些額外的回呼方法,然而,這些方法用來處理與activity的互動,完成一些動作比如建立或移除fragment的UI,這些額外的回呼方法有:

onAttach()
當fragment與activity聯合的時候呼叫此方法。
onCreateView()
呼叫此方法建立與fragment聯合的視圖階層。 
onActivityCreated()
當activity的onCreate()方法執行完呼叫此方法。
onDestoryView()
當與fragment有關的視圖階層移除時呼叫此方法。
onDetach()
當fragment不'要與activtiy聯合時呼叫此方法。 
fragment的生命週期流程,以及如何受寄主activity影響,都顯示在圖中。


在此圖中,你可以觀察activity的接替的狀態如何決定fragement接收哪一個回呼方法。舉例,當activity已經接收onCreate()方法的回呼,在activity的fragment只有接收onActivityCreated()方法回呼。

一旦activity處於resumed狀態,你可以自由地對activity增加或是移除fragment,因此,只有在activity是resumed狀態,fragment的生命週期才能獨立地改變。

然而,當activity離開resumed狀態,fragment再次由activity推動fragment的生命週期。
 

星期一, 11月 14, 2011

Fragment (九)

Android應用程式學習筆記

Handling the Fragment Lifecycle

管理fragment的生命週期非常像在管理activity的生命週期,如activity,fragment也存在三個狀態:

Resumed:
Fragment在運行中的activity是可見的。
Paused:
其他的activity在前台且擁有焦點,但是在activity的該fragment仍是可見的(前台activity部分是透明的或沒有完全覆蓋屏幕)。
Stopped:
Fragment已不可見,一方面可能是寄主activity已經停止或是fragment已經從activity被移除,但被加到back stack中。被停止的fragment仍是活的(所有的狀態和變數資訊都由系統保存),然而,fragment不再被用戶所見且如果activity被殺掉fragment也會被殺掉。
也像activity一樣,可以利用Bundle保留fragment狀態,假如activity的進程被殺掉了而你需要在activity重新建立時恢復fragment,你可以在fragment的onSaveInstanceState()回調方法中儲存狀態並且在onCreate()方法或是onCreateView()方法中恢復狀態。

activity的生命週期與fragment的生命週期之間最大的差異在於如何儲存在各自的back stack。預設,當activity停止會被置到由系統管理的back stack,然而,fragment只在你確切地要求在fragment移除時呼叫addToBackStack()方法儲存實例時,才會置入由寄主activity管理的back stack。

除此之外,管理fragment生命週期與管理activity生命週期非常類似,所以練習管理activity生命週期也可以用在fragment上,觀察activity生命週期如何影響fragment生命週期。


星期四, 11月 10, 2011

Fragment (八)

Android應用程式學習筆記

Communicating with the Activity

雖然fragment可以做為物件獨立於activity,也可以使用在多個activities,預設的fragment實體是直接與activity綁定。

特別是,fragment透過getActivity()方法可以存取Activity並輕鬆地執行工作比如取得activity布局的view。


View listView = getActivity().findViewById(R.id.list);

同樣地,你的activity也可以呼叫fragment中的方法,透過findFragmentById()方法或是findFragmentByTag()方法。


ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);


Creating event callbacks to the Activity

在一些情況中,你需要一個fragment與activity分享一個事件,在fragment定義一個回呼的介面並要求寄主activity執行是一個分享事件的好方法。當activity從界面接收到回呼,介面就會將資訊分享給在布局中的其他需要的fragment。

舉例,如果新聞應用程式有兩個fragment在activity中-一個顯示文章列表(Fragment A)-其他顯示文章內容(Fragment B),當一個列表選項被選到時,Fragment A必須告訴activity以便activity可以呼叫Fragment B去顯示文章內容,在此例中,OnArticleSelectedListener介面被宣告在Fragment A中。


public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

然後fragment的寄主activity執行OnArticleSelectedListener介面並複寫OnArticleSelected()方法,去通知Fragment B來自Fragment A的事件,為了確認寄主activity實現此介面,fragment A的onAttach()回呼方法將實體化OnArticleSelectedListener的Activity傳入onAttach()方法。


public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

如果activity沒有執行介面,fragment就會丟出ClassCastException例外,如果成功,mListener持有activity對OnArticleSelectedListener實現的引用,所以Fragment A可以通過呼叫由OnArticleSelectedListener介面定義的方法與Activity分享事件。例如,如果fragment A繼承ListFragment,每次用戶點擊列表項目,系統呼叫fragment中的onListItemClick()方法,接著在呼叫onArticleSelected()方法與Activity分享事件。


public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}

傳入onListItemClick()方法中的id參數是被選的項目行ID,id用來從activity的ContentProvider取文章。

星期三, 11月 09, 2011

Fragment (七)

Android應用程式學習筆記

Managing Fragments

管理在你的activity的fragments,需要使用FragmentManager,從activtiy呼叫getFragmentManager()方法取得FragmentManager。

FragmentManager可以完成以下工作:

  • 取得存在於activity的fragment,findFragmentById()方法(有在activity布局中提供UI的fragment)或是findFragmentTag()方法(沒有在activity佈局中提供UI的fragment)。
  • 將fragment從back stack中拿出,popBackStack()方法。
  • 向back stack註冊一個監聽器,監聽改變,addBackStackChangedListener()方法。

Performing Fragment Transactions

在activity中與使用fragment有關的最重要議題就是增加、移除、替換及運用fragment執行其他工作,回應用戶的互動。你託付給activity的每一組fragment變化稱為一個transaction且你可以使用FragmentTransaction的APIs組織一個transaction。你可以將transaction存在back stack由activity管理,允許用戶反轉fragment變化。

你可以從FragmentManager獲得FragmentTransaction實例,像是這樣:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每個transaction都是你想在同意時間執行的變化組合,你可以使用比如add()、remove()、replace()方法組織所有你想要執行的變化為一個transaction,然後將transaction應用在activity上,最後你必須呼叫commit()方法。

在呼叫commit()方法之前,你可能想要呼叫addToBackStack()方法,為了增加transaction到fragment transaction的back stack。Back stack由activity管理且允許用戶按"BACK"按鈕回傳前一個fragment的狀態。

舉例,你如何將fragment由另一個fragment替換,並且將先前的狀態保存在back stack。

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
此例中,newFragment取代了目前在布局容器中ID為R.id.Fragment_container的fragment,藉由呼叫addToBackStack()方法,替換的transaction被存到back stack中,所以用戶可以反轉transaction並且透過"BACK"按鈕返回先前的fragment。

如果增加更多變化到transaction(比如其他add()、remove())並呼叫addToBackStack()方法,在呼叫commit()之前所有變化都會增加到back stack作為一個transaction且"BACK"可以將transaction一起反轉。

將變化加到FragmentTransaction的順序並不重要,除了:
  • 必須在最後時呼叫commit()方法。
  • 如果要增加多個fragment到同一個容器,加入的順序決定了出現在view階層的順序。
如果在執行transaction移除fragment時,沒有呼叫addToBackStack()方法,然後,transaction被commit,fragment被銷毀,用戶就無法在回去,反之,如果有呼叫addToBackStack()方法,fragment被停止,當用戶想回去時,fragment就會再次恢復。

呼叫commit()方法後並不會立即執行transaction,由activity的UI執行續安排。如果需要,可以從UI執行續呼叫executingPandingTransaction()方法立即執行transaction,並不是常常會如此需要,除非transaction牽涉到其他的執行續。



星期一, 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。

星期一, 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才又可以被殺掉。