Fragment是一个很熟悉的朋友,几乎大部分的APP都会使用它。但是使用起来却不是很简单,在不求甚解的情况进行在使用它进行一些细节处理的时候会比较混乱。在一番尝试和查阅资料之后进行一番总结,可能会有不对的地方,还请发现的朋友们给于指证。
Fragment的生命周期:借鉴别人的一个图吧,一目了然。
一 fragment切换有两种方式 1 :用add hide show 2 :replace 他们都是通过FragmentManger生成的数据库事务进行管理的。
下面是常用方法:
FragmentManager fm =getSupportFragmentManager;
FragmentTransaction ft=fm.beginTransaction();
ft.add():向加入FragmengManager中加入一个Fragment. :
ft.hide():隐藏已经存在的Fragment
ft.show():显示FragmengManager中被隐藏的Fragment
ft.remove() //从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
ft.detach():Fragment的视图被销毁,但是它的状态没有被销毁,还是被fragmengManager管理。
ft.attach():Fragment的view重新加载到UI视图中,并显示,也就是执行
ft.replace()实际上就是remove + add的结合,并且使用replace的话,每次切换的话,会导致Fragment重新创建
注意:
1.hide,show 方法不会走Fragment的生命周期,他只是通过FragmentManager管理了这几个Fragment的显示和隐藏,所以会将数据保留在视图中。hide()某个Fragment会调用其onHiddenChanged方法。
2.replace=remove+add. remove掉旧的:在没有加入任务站的情况下是会被Destroy的,add进去一个新的是属于重新创建,
所以将不会保留之前的数据,每次都是最新的数据(涉及到请求之类的)。
3.add这种切换不会出现闪屏,replac可能会出现闪屏,不顺畅。
4.在APP“内存重启“的时候,add这种管理会出现重叠,因为Activity会从栈底到栈顶按顺序恢复并且因为没有保存Fragment的mHidden属性,默认为false,即show状态。而replace就不会出现这个现象。当然有大神已经解决了。
重叠问题解决https://www.jianshu.com/p/662c46cd3b5f
5.replace .如果在commit之前调用.addToBackStack(tag),则不会走onDes,只会走onDestroyView.
6..hide,show 生命周期的文章 https://blog.youkuaiyun.com/u014699958/article/details/52996143
大家可以根据具体情况结合使用,下面是一个例子,可以作为参考:
private void setFragment(int index) { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); String tag = index + ""; if (mCurrentTag != null && !tag.equals(mCurrentTag)) { Fragment fragmentByTag = fm.findFragmentByTag(tag); if (fragmentByTag != null) {//fragment栈中已经有该fragment实例 ft.hide(mCurrentFragment) .show(fragmentByTag) .commitAllowingStateLoss(); mCurrentFragment = fragmentByTag; mCurrentTag = tag; } else { Fragment chilFragment = findFragmentByIndex(index); if (chilFragment == null) return; ft.hide(mCurrentFragment) .add(contentFl.getId(), chilFragment, tag) .commitAllowingStateLoss(); mCurrentFragment = chilFragment; mCurrentTag = tag; } } else { if (mCurrentFragment == null) { Fragment childFragment = findFragmentByIndex(index);//创建一个新的Fragment if (childFragment == null) return; ft.replace(contentFl.getId(), childFragment, tag) .addToBackStack(tag) .commitAllowingStateLoss(); mCurrentFragment = childFragment; mCurrentTag = tag; } } }
二,懒加载Fragmnet:很多时候ViewPager和Fragmen的结合使用,由于ViewPager有预加载处理,默认情况下会加载展示页左右的Fragment,如果涉及到耗时的请求,会影响体验也会造成浪费。懒加载说的直白一点就是你可以预加载页面布局之类的,但是某些耗时的操作我可以让在即将展示的时候去做。这里设计到一个很重要的方法,setUserVisibleHint.
这块知识网上到处都是,就不做详细说明了,可以自行百度。但是几个注意事项需要看下的:
1.setUserVisibleHint:此方法是Viewpager等具有预加载功能的自定义的ViewGroup中手动调用的 并且在Fragment如果可见调用setUserVisibleHint(),并且是getUserVisibleHint()为true
2.setUserVisibleHint这个方法是手动调用的,Fragment的生命周期种不会自动调用此方法。ViewPager是通过它来管理展示与不展示的。
3.只要是Fragment即将要呈现getUserVisibleHint()就为true。 多加这句也是为了更好的理解 其实并不多余。
4.ViewPager种 setUserVisibleHint方法是在onCreate之后,onCreateView之前调用的。所以为了避免空指针最好加个判断。
5.https://blog.youkuaiyun.com/mr_immortalz/article/details/51015196
下面是我写的懒加载封装,仅供参考。 (具体的生命周期之类的,可以搜搜 听清楚的)
public abstract class BaseLazyFragment extends Fragment { Context mContext; View rootView; Unbinder bind; boolean isViewInit;//View记载完毕 @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = getActivity(); } /** * 此Fragment可见+布局View已经初始化完毕 * 进行数据请求加载 * * @param isVisibleToUser */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { lazyLoadData(); } } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (rootView != null) { ViewGroup parentView = (ViewGroup) rootView.getParent(); if (parentView != null) { parentView.removeView(rootView); } } else { if (getLayout() == 0) { throw new RuntimeException("getLayoutId need to set up res"); } else { rootView = inflater.inflate(getLayout(), container, false); } } return rootView; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); bind = ButterKnife.bind(this, rootView); isViewInit = true; lazyLoadData(); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initData(); initView(); } @Override public void onDestroyView() { super.onDestroyView(); isViewInit = false; } @Override public void onDestroy() { super.onDestroy(); if (bind != null) bind.unbind(); } /** * 懒加载请求的数据 * * @return */ public void lazyLoadData() { if (getUserVisibleHint() && isViewInit) { lazyLoad(); } } /** * 获取资源布局 * * @return */ public abstract int getLayout(); /** * 懒加载请求的数据 * * @return */ public abstract void lazyLoad(); /** * 初始化数据(跳转携带,或者缓存中) */ protected abstract void initData(); /** * 将初始化数据-->View上 */ protected abstract void initView(); }
三.FragmentPagerAdapter和FragmentStatePagerAdapter区别
1.FragmentpagerAdapter。该类内的每一个生成的Fragment都将保存在内存之中,因此适用于那些相对静态的页+数量也比较少的那种;
2FragmentStatePagerAdapter:只保留当前页面,当页面离开视线后,就会被消除,释放其资源。大量的页面时+不必在内存中占用大量的内存+资源实时刷新的。
3.FragmentpagerAdapter生命周期:第一次加载onAttach--->onResume,如果被移除会走到onDestroyView销毁视图,但是FragmentManager中会一直保留其存在,下次需要从中直接读取,而不会再斗getItem方法了。
4。FragmentStatePagerAdapter:如果不是当前使用,只要离开视线即走onDes 下次重新创建一个新的。
5.推荐连接:https://www.jianshu.com/p/25a02f5a15b3
就这些吧!