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







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