Fragment 要點整理

參考自:

http://blog.sina.com.cn/s/blog_69a4fbd70100r5j4.html

http://developer.android.com/guide/components/fragments.html


基本要點如下

1. 必須放到 activity 中


2. android 3.0 api 11 之後支持


3. 大多數應用應當爲每一個fragment實現至少這3個方法,onCreate(), onCreateView(), onPause()


4. 開始第一個是 onAttach(),最後結束一個是 onDetach()


5. 幾個常用的 Fragment:DialogFragment, ListFragment, PreferenceFragment


6. 注意: 每一個fragment都需要一個唯一的標識, 如果activity重啓,系統可以用來恢復fragment(並且你也可以用來捕獲fragment來處理事務,例如移除它.) 
有3種方法來爲一個fragment提供一個標識:
android:id 屬性提供一個唯一ID.
android:tag 屬性提供一個唯一字符串.
如果以上2個你都沒有提供, 系統使用容器view的ID.

7. FragmentTransaction API.


8. 要添加一個無UI的fragment, 需要從activity使用 add(Fragment, String) 來添加 fragment (爲fragment提供一個唯一的字符串"tag", 而不是一個view ID).這麼做添加了fragment, 但因爲它沒有關聯到一個activity layout中的一個view, 所以不會接收到onCreateView()調用. 因此不必實現此方法。


9. 要在activity中管理fragment, 需要使用FragmentManager.。


10. 每一個事務都是同時要執行的一套變化.可以在一個給定的事務中設置你想執行的所有變化,使用諸如 add(), remove(), 和 replace().然後, 要給activity應用事務, 必須調用 commit()。


11. 添加變化到 FragmentTransaction 的順序不重要, 除以下例外:
11.1 必須最後調用 commit().
11.2 如果添加多個fragment到同一個容器, 那麼添加的順序決定了它們在view hierarchy中顯示的順序.

12. 當執行一個移除fragment的事務時, 如果沒有調用 addToBackStack(), 那麼當事務提交後, 那個fragment會被銷燬(銷燬哦,和 activity 不一樣,是不能在 back 回去的),並且用戶不能導航回到它. 有鑑於此, 當移除一個fragment時,如果調用了 addToBackStack(), 那麼fragment會被停止, 如果用戶導航回來,它將會被恢復.

13. 提示: 對於每一個fragment事務, 你可以應用一個事務動畫, 通過在提交事務之前調用 setTransition() 實現.

14. 調用 commit() 並不立即執行事務.恰恰相反, 它將事務安排排期, 一旦準備好, 就在activity的UI線程上運行(主線程).如果有必要, 無論如何, 你可以從你的UI線程調用 executePendingTransactions() 來立即執行由commit()提交的事務. 但這麼做通常不必要, 除非事務是其他線程中的job的一個從屬.

15. 你只能在activity保存它的狀態(當用戶離開activity)之前使用commit()提交事務. 如果你試圖在那個點之後提交, 會拋出一個異常.這是因爲如果activity需要被恢復, 提交之後的狀態可能會丟失.對於你覺得可以丟失提交的狀況, 使用 commitAllowingStateLoss().

16. fragment可以使用 getActivity() 訪問Activity實例

17. activity可以通過從FragmentManager獲得一個到Fragment的引用來調用fragment中的方法, 使用 findFragmentById()findFragmentByTag()

18. 在一些情況下, 你可能需要一個fragment與activity分享事件. 一個好的方法是在fragment中定義一個回調的interface, 並要求宿主activity實現它.當activity通過interface接收到一個回調, 必要時它可以和在layout中的其他fragment分享信息.爲了確保宿主activity實現這個接口, fragment A的 onAttach() 回調方法(當添加fragment到activity時由系統調用) 通過將作爲參數傳入onAttach()的Activity做類型轉換來實例化

19. Fragment可以通過實現 onCreateOptionMenu() 提供菜單項給activity的選項菜單. 注意: 儘管你的fragment會接收到它所添加的每一個菜單項被選擇後的回調,  但實際上當用戶選擇一個菜單項時,activity會首先接收到對應的回調。如果activity的on-item-selected回調函數實現並沒有處理被選中的項目, 然後事件纔會被傳遞到fragment的回調.

20. 處理 Fragment 生命週期
20.1 Resumed
在運行中的activity中fragment可見.
20.2 Paused
另一個activity處於前臺並擁有焦點, 但是這個fragment所在的activity仍然可見(前臺activity局部透明或者沒有覆蓋整個屏幕).
20.3 Stopped
要麼是宿主activity已經被停止, 要麼是fragment從activity被移除但被添加到後臺堆棧中.
停止狀態的fragment仍然活着(所有狀態和成員信息被系統保持着). 然而, 它對用戶不再可見, 並且如果activity被幹掉,他也會被幹掉.

21. 生命週期方面 activity 和 fragment 之間最重要的區別是各自如何在它的後臺堆棧中儲存。默認地, activity 在停止後, 它會被放到一個由系統管理的用於保存 activity 的後臺堆棧.(因此用戶可以使用 BACK 按鍵導航回退到它)。然而, 僅當你在一個事務期間移除 fragment 時,顯式調用addToBackStack()請求保存實例時,才被放到一個由宿主 activity 管理的後臺堆棧.

22. fragment所生存的activity的生命週期,直接影響fragment的生命週期,每一個activity的生命週期的回調行爲都會引起每一個fragment中類似的回調.

23. 僅當 activity 處於 resumed 狀態時, fragment 的生命週期纔可以獨立變化。

開發中遇到的問題

1. ListFragment 因爲默認是返回 list view 所以不需要重載 onCreateView(),其他直接繼承自 fragment 則必須返回一個view,否則會報異常

2. 如果有 options menu,需要在 onCreate() 或者 onActivityCreate() 中設置 setHasOptionsMenu(true)
如果是使用 support v4 的庫,那麼在 menu 的 xml 中寫 “app:showAsAction="always” 是沒用的,必須要通過代碼來設置,否則不是 always 的效果,代碼是 

MenuItemCompat.setShowAsAction(菜單, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);

3. 一個 activity 如果要在 land 和 portrait mode 下顯示不同個數的 fragment,例如:portrait 下顯示一個,land 下顯示兩個該怎麼做?
按照 sdk 的說明,可以通過指定不同的 layout 文件來達到,例如:portrait 下的 layout 設置一個 fragment,land 下的 layout 設置兩個 fragment。


4. Fragment 爲什麼要用 newInstance() 的方式來創建
http://stackoverflow.com/questions/6677136/member-variables-vs-setarguments-in-fragments

5. 如果有一個 asynctask,是放到 activity 中,還是 fragment 中?
http://www.michenux.net/android-asynctask-in-fragment-best-pratices-725.html

6. 如何傳遞 argument 去一個 Fragment
首先這個 fragment 不能寫死在 layout 中,可以在 layout 中先寫一個 frameLayout 佔個位置,然後在代碼中動態替換成 fragment,詳細可以參考 support 4 中的FragmentArgumentsSupport.java  

7. 如果使用 custom list view 在 ListFragment 中,那麼有些地方要注意
7.1 重載 onCreateView

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.my_layout, container, false);
}

7.2 my_layout 的寫法
參考 http://developer.android.com/reference/android/app/ListFragment.html 中那個包含 list 的 linear layout 的寫法

7.3 如果使用 loader 的話則在 onLoadFinished 中要去掉部分代碼,否則會報錯
@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);


    // 下面的代碼要刪除掉
    if (isResumed()) {
        setListShown(true);
    } else {
        setListShownNoAnimation(true);
    }
}

錯誤日誌
11-01 15:38:37.489: E/ACRA(10903): java.lang.IllegalStateException: Can't be used with a custom content view
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.app.ListFragment.setListShown(ListFragment.java:282)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.app.ListFragment.setListShown(ListFragment.java:258)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:395)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.Loader.deliverResult(Loader.java:104)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.CursorLoader.deliverResult(CursorLoader.java:73)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.CursorLoader.deliverResult(CursorLoader.java:35)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.AsyncTaskLoader.dispatchOnLoadComplete(AsyncTaskLoader.java:223)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.AsyncTaskLoader$LoadTask.onPostExecute(AsyncTaskLoader.java:61)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.ModernAsyncTask.finish(ModernAsyncTask.java:461)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.ModernAsyncTask.access$500(ModernAsyncTask.java:47)
11-01 15:38:37.489: E/ACRA(10903):      at android.support.v4.content.ModernAsyncTask$InternalHandler.handleMessage(ModernAsyncTask.java:474)
11-01 15:38:37.489: E/ACRA(10903):      at android.os.Handler.dispatchMessage(Handler.java:99)
11-01 15:38:37.489: E/ACRA(10903):      at android.os.Looper.loop(Looper.java:137)
11-01 15:38:37.489: E/ACRA(10903):      at android.app.ActivityThread.main(ActivityThread.java:5227)
11-01 15:38:37.489: E/ACRA(10903):      at java.lang.reflect.Method.invokeNative(Native Method)
11-01 15:38:37.489: E/ACRA(10903):      at java.lang.reflect.Method.invoke(Method.java:511)
11-01 15:38:37.489: E/ACRA(10903):      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
11-01 15:38:37.489: E/ACRA(10903):      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:562)
11-01 15:38:37.489: E/ACRA(10903):      at dalvik.system.NativeStart.main(Native Method) 


8. 使用 fragment 導致的重影(overlay)的問題
主要是因爲使用了 add 的方法,然後在屏幕旋轉的時候又 add 了一次,現在應該改爲 replace,並且每次先獲取一次,如果沒有在創建 fragment
public void onCreate(Bundle savedInstanceState) {
    if (DEBUG) Log.v(TAG, "onCreate()");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.123);

    // check first
    mMyFragment = (MyFragment) getSupportFragmentManager().findFragmentById(R.id.my_item);
    if (null==mMyFragment) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        mMyFragment = MyFragment.newInstance(itemId);
        ft.replace(R.id.my_item, mMyFragment);   // not add
        ft.commit();
    } else {
        if (DEBUG) Log.i(TAG, "onCreate() : find my fragment");sf  
         }
}


9. fragment 之間如何通信
9.1 通過 local broadcast 來通訊
9.2 通過 activity 來中轉
參考:http://wuyexiong.github.io/blog/2013/04/30/andr







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章