Fragment深入解析和学习
Fragment的产生
在多屏展示的时候,为了维护各个展示界面独立的生命周期和数据加载效果。
概述
对于Fragment的使用,一方面Activity需要在布局中为Fragment安排位置,另一方面需要管理好Fragment的生命周期。
Fragment中涉及的对象和方法:
FragmentManager:
Activity中的FragmentManager,其内部维护着fragment队列,以及fragment事务的回退栈。
-
getFragments()
获取加入到fragment中的fragment的list列表
-
findFragmentById(int id)
通过id找到加入的fragment
-
findFragmentByTag(String tag)
通过tag找到对应的fragment
-
popBackStack()
顶层fragment出栈
扫描二维码关注公众号,回复: 4734192 查看本文章
FragmentTransaction
保证一些列Fragment操作的原子性。主要用来操作fragment的展示和隐藏等相关操作。
-
transaction.add() 往Activity中添加一个Fragment
-
transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
-
transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体
-
transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁,
-
transaction.show()
显示之前隐藏的Fragment
注意:当使用add(),show(),hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),不会回调onStop()等生命周期方法,而新的Fragment在创建时是不会回调onHiddenChanged(),这点要切记。
-
detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
-
attach() 重建view视图,附加到UI上并显示。
-
transatcion.commit() 提交一个事务
Arguments
对Fragment传递数据使用setArguments(Bundle args),而后在onCreate中使用getArguments()取出,在 “内存重启”前,系统会帮你保存数据,不会造成数据的丢失。和Activity的Intent原理一致。
使用newInstance(参数)创建Fragment对象,优点是调用者只需要关系传递的哪些数据,而无需关心传递数据的Key是什么。
public class ContentFragment extends Fragment { private String mArgument; public static final String ARGUMENT = "argument"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // mArgument = getActivity().getIntent().getStringExtra(ARGUMENT); Bundle bundle = getArguments(); if (bundle != null) mArgument = bundle.getString(ARGUMENT); } /** * 传入需要的参数,设置给arguments * @param argument * @return */ public static ContentFragment newInstance(String argument) { Bundle bundle = new Bundle(); bundle.putString(ARGUMENT, argument); ContentFragment contentFragment = new ContentFragment(); contentFragment.setArguments(bundle); return contentFragment; }
注意:
replace 和 add/hide的区别
用replace()方法来替换Fragment,每次切换的时候,Fragment都会重新实例化,重新加载一边数据。replace()这个方法只是在上一个Fragment不再需要时采用的简便方法。
一般的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个。这样就能做到多个Fragment切换不重新实例化:
public void switchContent(Fragment from, Fragment to) { if (mContent != to) { mContent = to; FragmentTransaction transaction = mFragmentMan.beginTransaction().setCustomAnimations( android.R.anim.fade_in, R.anim.slide_out); if (!to.isAdded()) { // 先判断是否被add过 transaction.hide(from).add(R.id.content_frame, to).commit(); // 隐藏当前的fragment,add下一个到Activity中 } else { transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个 } }
内存重启所带来的fragment的问题
内存重启:系统资源紧张的时候或者应用出现bug奔溃的时候导致把app的资源全部回收(杀死app的进程),或是屏幕旋转等配置变化也会造成当前Activity重启。
系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate方法调用后紧接着恢复(从onAttach生命周期开始)。
getActivity()空指针
在调用了getActivity()时,当前的Fragment已经onDetach() 了宿主Activity。在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。
这里最好在onAttach(Activity activity)里赋值,使用mActivity代替getActivity()。