Fragment简单介绍
来源:互联网 发布:js post 跨域解决方案 编辑:程序博客网 时间:2024/06/11 17:11
一、Fragment概念以及设计原理
Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment,我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响。比如,当Activity暂停时,其中的所有片段也会暂停;当Activity被销毁时,所有片段也会被销毁。 不过,当Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除它们。 当您执行此类片段事务时,您也可以将其添加到由Activity管理的返回栈,Activity中的每个返回栈条目都是一条已发生片段事务的记录。返回栈让用户可以通过按返回按钮撤消片段事务(后退)。
二、android.support.v4.app.Fragment和android.app.Fragment
- android.app.Fragment 兼容的最低版本是android:minSdkVersion=”11” 即3.0版。
- android.support.v4.app.Fragment 兼容的最低版本是android:minSdkVersion=”4” 即1.6版。
推荐用android.support.v4.app.Fragment。
请注意:一个应用中,千万不要混合使用给自己增加不必要的麻烦。最好一个应用里面都用v4包或者app包的Fragment。
三、Fragment生命周期函数
>
除了上面列出的标准周期函数之外,还有几个函数也要特别注意:
- onViewCreated(): 是不是和onCreateView()很像,onViewCreated()是在onCreateView()函数之后执行,我们通常在onViewCreated()函数里面findViewById。
- setUserVisibleHint():当前页面是否可见(一般ViewPager+Fragemnt配合使用会用到,懒(延时)加载的时候这个函数有大用处),因为ViewPager+Fragemnt的时候是会同时去加载前后多个Fragment的,这个时候就有些Fragment是可见的一些Fragment是不可见的。有一点要注意setUserVisibleHint()只在ViewPager+Fragment这情况下才会回调,其他静态加载和动态加载Fragment不会被调用到。。
- onHiddenChanged():hide()、show()来回切换Fragment显示的时候,Fragment只会回调onHiddenChanged()。Fragment在add()的时候不会回调onHiddenChanged()函数,这点要切记。还有,在ViewPager+Fragment使用的时候Fragment也不会回调onHiddenChanged()函数的。
四、Fragment的使用
在Fragment使用之前,有三几个特别重要的类要先来了解下:FragmentManager、FragmentTransaction、FragmentManager.BackStackEntry。
- FragmentManager:FragmentManager是负责管理Fragment并将它们的视图添加到Activity视图层级结构中的一个管理类。
FragmentManage可以做那些事情:
- 通过 findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段。
- 通过 popBackStack()(模拟用户发出的返回命令)将片段从返回栈中弹出。
- 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
- 开启一个事务。
FragmentManager相关API函数解释如下:
/** * 开启一个事务,用于对Fragment操作的一系列处理 */ public abstract FragmentTransaction beginTransaction(); /** * 立即执行挂起的事物FragmentTransaction里面的,commit()、popBackStack()都不是立即执行的, * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行. * 但是有时候你希望你的操作是立即执行的,在commit()只会调用该函数。 */ public abstract boolean executePendingTransactions(); /** * 通过Fragment所在的布局id,查找Fragment.查找规则: * 1. 先在add列表里面查找。记住一定是拿最顶上的那个Fragment, (add A、add B. 这个时候就算你把B隐藏了,拿到的还是B). * 2. 第一步没找到的情况下,接着就去回退栈里面查找。 */ public abstract Fragment findFragmentById(@IdRes int id); /** * 通过Fragment的Tag找到Fragment(添加Fragment的时候会保证tag唯一) */ public abstract Fragment findFragmentByTag(String tag); /** * 弹出堆栈中顶部的Fragment并且显示,类似按下返回键的操作(不是立即执行的, * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行) */ public abstract void popBackStack(); /** * 弹出堆栈中顶部的Fragment并且显示(立即执行) */ public abstract boolean popBackStackImmediate(); /** * name可以为null或者相对应的BackStackEntry 的名字(在FragmentTransaction的addToBackStack()可以设置该名字),flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况 * 1. 如果name为null,flags为0时,弹出回退栈中最上层的那个fragment。 * 2. 如果name为null ,flags为1时,弹出回退栈中所有fragment。 * 3. 如果name不为null,flags为0时,那就会找到这个tag所对应的fragment,弹出该fragment以上的Fragment, * 4. 如果name不为null,flag为是1,弹出该fragment(包括该fragment)以上的fragment。 */ public abstract void popBackStack(String name, int flags); /** * 同上(唯一的区别就是会立即执行) */ public abstract boolean popBackStackImmediate(String name, int flags); /** * 与popBackStack(String name, int flags)类似,id是BackStackEntry对应的id */ public abstract void popBackStack(int id, int flags); /** * 同上 */ public abstract boolean popBackStackImmediate(int id, int flags); /** * 得到回退栈中BackStackEntry的数量 */ public abstract int getBackStackEntryCount(); /** * 根据序号返回后台堆栈中的BackStackEntry对象(按照添加回退栈的顺序) */ public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int index); /** * 为添加回退堆栈添加一个监听器,用于监听堆栈的改变情况 */ public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener); /** * 移除监听堆栈的监听器 */ public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener); /** * 把一个Fragment对象放入到bundle中, 和getFragment函数对应。比如你可以在onSaveInstanceState()函数中调用该函数 * 保存一个Fragment */ public abstract void putFragment(Bundle bundle, String key, Fragment fragment); /** * 根据key从bundle中取出之前putFragment()的Fragment。比如你可以在onRestoreInstanceState()函数中调用该函数 * 恢复之前保存的Fragment */ public abstract Fragment getFragment(Bundle bundle, String key); /** * 得到加入FragmentManager中所有的Fragment(这些Fragment都是通过FragmentTransaction加入)。 * 不包括回退栈中,以及已经detached或者removed掉的。 */ public abstract List<Fragment> getFragments(); /** * 保存给定Fragment的当前实例状态,返回值得到的状态可以用Fragment的 * setInitialSavedState()方法设置给新的Fragment实例, 作为初始状态. * 当如这个函数的使用也是有限制的: * 1. 保持状态的Fragment必须attach到FragmentManager中。 * 2. 新创建的Fragment必须和状态对应的Fragment相同的class类型。 * 3. 保存状态的Fragment不能依赖其他的Fragment,并且不能使用 putFragment(Bundle, String, Fragment)函数 */ public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f); /** * Activity的onDestroy()是否调用 */ public abstract boolean isDestroyed(); /** * 注册监听FragmentManager中所有Fragment的生命周期 */ public abstract void registerFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks cb, boolean recursive); /** * 注销监听FragmentManager中所有Fragment的生命周期 */ public abstract void unregisterFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks cb); /** * 返回FragmentManager里面当前活动的主导航Fragment。 */ public abstract Fragment getPrimaryNavigationFragment();
- FragmentTransaction:所有对Fragment的动态操作都是通过FragmentTransaction事务来提交执行。FragmentTransaction是一个事物(事务是在同一时刻执行的一组动作,很像数据库中的事务)。你可以用add(),remove(),replace()等方法构成事务,最后使用commit()方法提交事务。在调用commint()之前,你可以用addToBackStack()把事务添加到一个后退栈中,这个后退栈属于所在的Activity。有了它,就可以在用户按下返回键时,返回到Fragment们执行事务之前的状态。
FragmentTransaction中对Fragment的操作大致可以分为三类:
- 显示操作:add()、 replace()、 show()、 attach()。
- 隐藏操作:remove() 、hide() 、detach()。
- 添加回退栈:addToBackStack()。
只能在Activity处于可保存状态的状态时,比如running中,onPause()方法和onStop()方法中提交事务,否则会引发异常。这是因为Fragment的状态会丢失。如果要在可能丢失状态的情况下提交事务,请使用commitAllowingStateLoss()。
FragmentTransaction相关API函数解释如下:
/** * 添加一个Fragment到FragmentManager中(注意这里没写Fragment要显示在那个id上,所以这个Fragment要显示的时候是看不到的) * 同一个Fragment(或者相同的tag)不能多次添加否则会报IllegalStateException */ public abstract FragmentTransaction add(Fragment fragment, String tag); /** * 添加一个Fragment到FragmentManager中(tag = null) */ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment); /** * 添加一个Fragment到FragmentManager中(containerViewId 表示Fragment要放置在在哪个位置) */ public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment, @Nullable String tag); /** * 替换掉指定位置(containerViewId)上所有的Fragment(记住是containerViewId上所有的) * containerViewId 上加入了两个Fragment A、B。如果用C来replace掉containerViewId上的Fragment。 * 那么A,B都会被相当于调用了FragmentTransaction里面的remove()函数。 */ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment); /** * 替换掉指定位置(containerViewId)上所有的Fragment */ public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment, @Nullable String tag); /** * 移除掉指定的Fragment */ public abstract FragmentTransaction remove(Fragment fragment); /** * 隐藏指定的Fragment */ public abstract FragmentTransaction hide(Fragment fragment); /** * 显示指定的Fragment(配合hide使用) */ public abstract FragmentTransaction show(Fragment fragment); /** * 会将view与fragment分离,将此将view从view tree中删除(onPause、onStop、onDestroyView)!而且将fragment * 从Activity的ADD队列中移除!所以在使用detach()后,使用fragment::isAdded() * 返回的值是false;但此fragment实例并不会删除,此fragment的状态依然保持着使用, * 所以在fragmentManager中仍然可以找到,即通过FragmentManager::findViewByTag()仍然是会有值的 */ public abstract FragmentTransaction detach(Fragment fragment); /** * 显然这个方法与detach()所做的工作相反,它一方面利用fragment的onCreateView() * 来重建视图(onCreateView、onActivityCreate、onStart、onResume),一方面将此fragment添加到ADD队列中;这里最值得注意的地方在这里: * 由于是将fragment添加到ADD队列,所以只能添加到列队头部,所以attach()操作的结果是, * 最新操作的页面始终显示在最前面! */ public abstract FragmentTransaction attach(Fragment fragment); /** * 设置一个当前活动的主导航Fragment。(还没有搞清楚这个东西的作用) */ public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment); /** * 当前事务是否有操作 */ public abstract boolean isEmpty(); /** * 设置进入/退出的动画效果(资源文件)。这个必须位于replace、add、remove之前,否则效果不起作用。 */ public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter, @AnimatorRes @AnimRes int exit); /** * 设置进入/退出的动画效果(资源文件)。这个必须位于replace、add、remove之前,否则效果不起作用。 * 四个参数分别表示:添加、移除、从BackStack中pop出来、进入的动画效果。 */ public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter, @AnimatorRes @AnimRes int exit, @AnimatorRes @AnimRes int popEnter, @AnimatorRes @AnimRes int popExit); /** * Fragment切换时, 有一些元素(element)会保持不变, 使用该函数使这些元素切换时, 赋予动画效果。 * 关于这部分的内容可以去搜素下Android的共享元素动画,很有意思的一个东西。 */ public abstract FragmentTransaction addSharedElement(View sharedElement, String name); /** * 设置切换效果。目前API提供:TRANSIT_NONE、 TRANSIT_FRAGMENT_OPEN、TRANSIT_FRAGMENT_CLOSE三种。 */ public abstract FragmentTransaction setTransition(/*@FragmentTransaction.Transit*/ int transit); /** * 设置切换的风格 */ public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes); /** * 添加commit执行之前的操作到后台堆栈中(对应会生成一个FragmentManager.BackStackEntry对象) */ public abstract FragmentTransaction addToBackStack(@Nullable String name); /** * 是否允许添加到后台堆栈,如果是不允许的状态addToBackStack()会抛异IllegalStateException常 */ public abstract boolean isAddToBackStackAllowed(); /** * 设置不允许添加后台堆栈 */ public abstract FragmentTransaction disallowAddToBackStack(); /** * 设置面包屑导航栏的长标题 * (你可以认为就是保存了一个标题,然后可以通过FragmentManager.BackStackEntry 的getBreadCrumbTitle() * 获取到该设置的标题) */ public abstract FragmentTransaction setBreadCrumbTitle(@StringRes int res); /** * 设置面包屑导航栏的长标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数) */ public abstract FragmentTransaction setBreadCrumbTitle(CharSequence text); /** * 设置面包屑导航栏的短标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数) */ public abstract FragmentTransaction setBreadCrumbShortTitle(@StringRes int res); /** * 设置面包屑导航栏的短标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数) */ public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text); /** * 设置是否优化事务操作的执行,去掉一些冗余的操作,比如这么个情况,两个事务同时执行,一个事务是添加A Fragment, * 另一个事务是用B 去 替换掉A。如果做优化,会跳过A直接添加B. */ public abstract FragmentTransaction setReorderingAllowed(boolean reorderingAllowed); /** * 当事务commit的时候,执行指定的Runnable */ public abstract FragmentTransaction runOnCommit(Runnable runnable); /** * 提交事务(commit并不会立即执行的,系统会在适当的时候执行) */ public abstract int commit(); /** * 允许保存在存储状态之后 提交事务(Activity 的onSaveInstanceState()只会还可以提交事务) */ public abstract int commitAllowingStateLoss(); /** * 立即提交事务(用这个函数提交事务,不能添加到回退栈) */ public abstract void commitNow(); /** * 允许保存在存储状态之后 ,立即提交事务(用这个函数提交事务,不能添加到回退栈) */ public abstract void commitNowAllowingStateLoss();
- FragmentManager.BackStackEntry:可以认为是Fragment back stack中堆栈的一个记录,在我们自己去操作Fragment回退栈的时候会经常用到。
FragmentManager.BackStackEntry相关API函数解释如下:
/** * 回退栈对应的id(自动分配) */ public int getId(); /** * 回退栈对应名字 FragmentTransaction addToBackStack(String)的时候设置 */ public String getName(); /** * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbTitle(@StringRes int res) 对应 * FragmentTransaction addToBackStack(String) 之前调用FragmentTransaction setBreadCrumbTitle(@StringRes int res) */ @StringRes public int getBreadCrumbTitleRes(); /** * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbShortTitle(@StringRes int res) 对应 */ @StringRes public int getBreadCrumbShortTitleRes(); /** * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbTitle(CharSequence text) 对应 */ public CharSequence getBreadCrumbTitle(); /** * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbShortTitle(CharSequence text) 对应 */ public CharSequence getBreadCrumbShortTitle();
4.1 静态添加Fragment
静态Fragment使用,这个是最简单的了,就布局文件里面fragment标签,android:name就指定Fragment的路径。<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <fragment android:id="@+id/fragment_a" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:name="com.tuacy.example.statics.StaticFragmentA"/> <fragment android:id="@+id/fragment_b" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:name="com.tuacy.example.statics.StaticFragmentB"/></LinearLayout>
4.2 动态添加Fragment
通过FragmentManager和FragmentTransaction里面的一些函数来动态增删改Fragment。动态Fragment使用的时候,要有一个概念,Fragment要显示肯定是要依托于指定的id(容器)。可以简单的理解为每个id容器都对应一个数组,这个数组里面放的就是对这个id容器上所有的Fragment(FragmentTransaction类的add、show、hide、replace、remove、attach、detach就是对这个数组做相应的增删操作)。决定这个id容器展示给用户的是哪个Fragment就看这个数组最从后往前哪个Fragment是显示(show)的状态。 通过一个实例来瞧一瞧动态Fragment使用(FragmentTransaction类里面的add、show、hide、replace、remove、attach、detach函数的使用)。add():就相当于往id容器里面添加Fragment,注意一点就够add进去的默认都是shw的状态。比如我们用如下的代码依次添加三个Fragmetn,A、B、C FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.layout_content_fragment, mFragmentA); transaction.add(R.id.layout_content_fragment, mFragmentB); transaction.add(R.id.layout_content_fragment, mFragmentC); transaction.commit();
这个时候id容器展示给用户的是C,最上面第一个显示状态的是C。如图所示:
hide()、show():就是去改变对应Fragment的显示状态(show or hide)。
remove():相当于从数组里面把指定的Fragment给移除掉了。
replace():就是把指定id容器响应的数组清空掉,然后在把replace要放入的Fragment放入进去。
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.layout_content_fragment, mFragmentD); transaction.commitNow();
attach()、detach():detach会从容器数组中把Fragment删除,但是还会记录这个Fragment的一些状态,比如你先把这个Fragment hide掉,然后detach掉了,然后你又attach进来 这个Fragment的状态还是hide的状态。
FragmentTransaction中每个操作调用对应Fragment的生命周期里面的哪些函数:
- add():onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()。
- remove():onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()。
- show(): onHiddenChanged() - >false。
- hide(): onHiddenChanged() - >true。
- attach(): 已经add的前提下detach掉在attach进来的情况 onCreateView()->onActivityCreated()->onStart()->onResume()。
- detach(): onPause()->onStop()->onDestroyView()。
- replace():相当于把之前所有的remove掉,在add进来。对应remove和add对Fragment的生命周期的影响。
动态Fragment在使用过程中还要几个要注意的点:
- 同一个Fragment的实例对象只能add一次。就算是add到同一个activity里面的不同的id容器里面也只能一次。
- detach掉的Fragment会保存它detach之前的状态,下次attach进来还是之前的状态。比如你detach之前把Fragment hide隐藏掉了,attach进来还是隐藏的状态。
- 没有add过的Fragment直接调用attach,和add产生的效果一致。
4.3 ViewPager + Fragment
ViewPager + Fragment指的是每个pager都是一个Fraagment。 关于ViewPager + Fragment使用过程中Adapter:FragmentPagerAdapter、FragmentStatePagerAdapter的区别:- FragmentPagerAdapter:会将每个生成的Fragment对象一直存留在内存中,所以当有大量的pager显示页时不太适用。
- FragmentStatePagerAdapter:会销毁setOffscreenPageLimit()函数设置的limit之外的Fragment对象。
FragmentPagerAdapter、FragmentStatePagerAdapter的选择要根据实际的情况来定,如果pager只有三四页那咱们完全可以使用FragmentPagerAdapter避免每次重新加载的问题。如果pager比较多(比如实现无限加载)咱就选用FragmentStatePagerAdapter。
关于ViewPager + Fragment使用过程中的Fragment,涉及到Fragment懒加载的问题。懒加载说白了就是延时加载,把加载延时到Fragment对用户可见的时候。增强用户的体验。举一个很直白的例子,有三个pager对应三个Fragment,并且这三个Fragment都要做网络请求。如果不做懒加载的时候三个Fragement就要同时去请求数据。体验就不是那么的好了。懒加载就很好的规避了这个问题,当切换到哪一页的是那个Fragment才去做网络的请求加载数据。
Fragment懒加载:对懒加载的封装,就是对ragment周期函数的灵活使用。最主要的是围绕setUserVisibleHint()函数做各种变通。
网上很多大神都给出了对Fragment加载的封装:这里列出一个经常用的封装,在里面给加了一些注释,方便大家的。
/** * 懒加载Fragment */public abstract class LazyFragment extends Fragment { protected Context mContext = null; /** * 判断是不是第一次resume */ private boolean isFirstResume = true; /** * 判断是不是第一次可见(只会在setUserVisibleHint中判断和改变) */ private boolean isFirstVisible = true; /** * 判断是不是第一次不可见(只会在setUserVisibleHint中判断和改变) */ private boolean isFirstInvisible = true; /** * 标记是否准备加载数据,因为我们不能在setUserVisibleHint马上去加载数据 * setUserVisibleHint调用的只会,可能视图都还没有加载出来。 */ private boolean isPrepared = false; @Override public void onAttach(Context context) { super.onAttach(context); mContext = context; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initPrepare(); } @Override public void onResume() { super.onResume(); if (isFirstResume) { isFirstResume = false; return; } /** * 这里的情况是为了避免,比如你锁屏之后再解锁,这个时候也是用户可见的情况 * 并且这种情况是不会调用setUserVisibleHint()函数的 */ if (getUserVisibleHint()) { onUserVisible(); } } @Override public void onPause() { super.onPause(); /** * 这里的情况是为了避免,比如你锁屏之后载解锁 */ if (getUserVisibleHint()) { onUserInvisible(); } } /** * setUserVisibleHint 函数第一次调用肯定给的是false,第二次才是true */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { if (isFirstVisible) { isFirstVisible = false; initPrepare(); } else { onUserVisible(); } } else { if (isFirstInvisible) { isFirstInvisible = false; onFirstUserInvisible(); } else { onUserInvisible(); } } } private synchronized void initPrepare() { if (isPrepared) { onFirstUserVisible(); } else { isPrepared = true; } } /** * 第一次对用户可见的时候调用,在这里懒加载数据 */ protected abstract void onFirstUserVisible(); /** * 第二次包括第二次对用户可见的时候调用 */ protected void onUserVisible() { } /** * 第一次对用户不可见的时候调用 */ protected void onFirstUserInvisible() { } /** * 第二次包括第二次对用户不可见的时候调用 */ protected void onUserInvisible() { }}
4.4 Fragment回退栈的使用
Fragment的回退栈,类似与Android系统为Activity维护一个任务栈。我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
- 添加进回退栈:FragmentTransaction.addToBackStack(String)
- 从回退栈中退出:FragmentManager中的popBackStack()、popBackStackImmediate()、getBackStackEntryCount()、getBackStackEntryAt()函数使用。
/** * 弹出堆栈中顶部的Fragment并且显示,类似按下返回键的操作(不是立即执行的, * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行) */ public abstract void popBackStack(); /** * 弹出堆栈中顶部的Fragment并且显示(立即执行) */ public abstract boolean popBackStackImmediate(); /** * name可以为null或者相对应的BackStackEntry 的名字(在FragmentTransaction的addToBackStack()可以设置该名字),flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况 * 1. 如果name为null,flags为0时,弹出回退栈中最上层的那个fragment。 * 2. 如果name为null ,flags为1时,弹出回退栈中所有fragment。 * 3. 如果name不为null,flags为0时,那就会找到这个tag所对应的fragment,弹出该fragment以上的Fragment, * 4. 如果name不为null,flag为是1,弹出该fragment(包括该fragment)以上的fragment。 */ public abstract void popBackStack(String name, int flags); /** * 同上(唯一的区别就是会立即执行) */ public abstract boolean popBackStackImmediate(String name, int flags); /** * 与popBackStack(String name, int flags)类似,id是BackStackEntry对应的id */ public abstract void popBackStack(int id, int flags); /** * 同上 */ public abstract boolean popBackStackImmediate(int id, int flags); /** * 得到回退栈中BackStackEntry的数量 */ public abstract int getBackStackEntryCount(); /** * 根据序号返回后台堆栈中的BackStackEntry对象(按照添加回退栈的顺序) */ public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int index); /** * 为添加回退堆栈添加一个监听器,用于监听堆栈的改变情况 */ public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener); /** * 移除监听堆栈的监听器 */ public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener);
为了方便大家的理解Fragment的回退栈管理,我们给出一个全面的例子。参考下文给的实例中的,回退栈管理。
五、Fragment参数传递
在开发过程中经常会向Fragment传递参数,在最初的时候我是通过构造函数传递参数,或者通过放出一个set方法出来传递参数,其实这两种方式是不对的,因为,当一个Fragment一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建的时候,系统会把之前通过set方法设置的参数全部清掉,并且再次调用的是 Fragment中的默认构造函数(是默认构造函数,是没有参数的那个构造函数)根本就不会走到带参数的构造函数。 最正确的方式是通过 setArguments()、getArguments()来进行参数的传递和获取。
六、Fragment状态保存和恢复
讲到Fragment的状态保存和恢复,咱就得简单的来理一理Android里面的状态保存和恢复了。
Android中为什么会存在状态的保存和恢复一说。因为,Android系统中每个应用的内存都比较紧张的,Android展示都是通过Activity(里面有Fragment、View),当Activity退到后台的时候(onStop状态),且长时间不用或前台Activity需要更多资源导致系统必须杀死后台进程回收内存时,系统会销毁你的Activity。这个时候虽然Activity的实例是消失了,但系统会记录它的存在,如果用户下次返回到这个Activity,系统会使用该Activity销毁时保存的状态数据重建一个新的Activity实例。这个系统保存的用来恢复到之前状态的数据叫做“实例状态”,它以键值对的形式保存在Bundle对象中。
Android中的销毁分两种情况:
- 正常销毁。正常销毁是指我们用户主动销毁Activity比如back返回,finish的调用,这时该实例(Activity、Fragment、View)将永久的消失,因为这些行为表示该实例不再被需要。
- 非正常销毁。非正常销毁是指当Activity处于stopped状态且长时间不用时或前台Activity需要更多资源导致系统必须杀死后台进程回收内存时,系统会销毁你的Activity。
只有非正常销毁的时候才会用到状态的恢复,非正常销毁实际上Activity的实例是消失了,但系统会记录它的存在,这是如果用户回到它,系统会使用该Activity销毁时保存的状态数据重建一个新的Activity实例。这个系统保存的用来恢复到之前状态的数据叫做“实例状态”,它以键值对的形式保存在Bundle对象中。
Android中的状态的保存和恢复。我们分为两个部分:一个是状态的保存、一个是状态的恢复:
- 状态保存:状态的保存在退到后台的时候就会触发(不管是正常的退到后台还是非正常的退到后台)。
- 状态的恢复:状态的恢复一定是在非正常退出的在回来的时候会触发。所以状态保存的次数一定是大于等于状态恢复的次数。
Android中的状态保存和恢复我们分为三大块:Activity状态保存和恢复、Fragment状态保存和恢复、View状态保存和恢复。其实Activity的状态保存会触发Fragment和View去调用状态保存和恢复。
注:屏幕旋转被经常用来测试状态的保存和恢复
6.1 Activity状态保存恢复
Activity中onSaveInstanceState()用于保存Activity的状态、onRestoreInstanceState()用于恢复Activity保存的状态。当然也可以通过onCreate()函数来恢复保存的状态,onCreate()里面状态恢复的时候必须判断Bundle是否为null,如果为null,则是创建新的Activity,否则重建恢复Activity。如果使用onRestoreInstanceState()方法恢复状态,则不再需要对其中的Bundle进行判null,因为仅当有状态需要被恢复时,该方法才会被回调,且该方法在onStart()方法之后调用。
Activity状态的保存和恢复着重点在我们自定义的一些数据上。哪些是要保存和恢复心里有个底,比如,你的Activity有一个播放的功能,那视频播放的进度就是要保存和恢复的了。特别,特别要注意。在重写onSaveInstanceState()、onRestoreInstanceState()的时候一定要记得调用super方法。因为,在Android中当Activity的onSaveInstanceState调用的时候,因为在super中Activity会自动收集View层级中每个View的状态。请注意只有在内部实现了View的保存/恢复状态方法的View才会被收集到。一旦onRestoreInstanceState方法被调用,Activity会把收集的数据发送回给View结构树中具有相同android:id配置的View。同时也会触发Activity关联的Fragment的状态的保存和恢复。
onSaveInstanceState()会在Activity的onPause()方法之后调用、onRestoreInstanceState()方法会在onStart()之后调用。
Bundle中保存的数据一定要经过序列化,只能保存1M以下的数据。
6.2 Fragment状态保存和恢复
Fragment的状态保存和恢复和Activity产生的时机是一样的,Activity的状态保存和恢复会触发该Activity关联的Fragment的状态保存和恢复。Fragment中在onSaveInstanceState()中保存状态,在onCreateView()、onActivityCreated()、onViewCreated()任何一个函数中恢复数据都可以。
Fragment的使用稍稍复杂一点,Fragment要依托容器来显示,可能这个容器里面放置了好几个Fragment,这些Fragment的状态保存和恢复都是一样的。在Activity有状态保存和恢复的时候,都是会触发这个容器里面所有的Fragment的状态保存和恢复。
当Fragment的使用过程中使用了addToBackStack()的时候,在状态保存和恢复的时候这个也是会完全恢复的。屏幕旋转之后返回的时候还是能返回上一次保存的Fragment状态的。
setRetainInstance()函数的作用,将该Fragment转变为“持久化Fragment”,这意味着只有该Fragment的GUI(从onViewCreated()方法返回的View)会被销毁重建(就是Frament第一次创建之后,当由于内存不足要重复创建的时候不会调用onCreate()和onDestroy()函数。其他的函数还是会调用的)而所有其他的引用变量仍然保持。
Dialog弹窗的时候也是推荐使用DialogFragment来替换,你想下比如有这么一种情况,当你弹出了Dialog的时候这个时候进行屏幕的切换,我肯定希望Dialog还存在吧而且Dialog中的输入框里面之前输入的字符还存在。DialogFragment就能完美的解决这一问题。
6.3 View状态保存和恢复
在开发过程中自定义View是我们经常干的事情。在自定义View的时候 就要时刻注意View状态保存恢复,Android中原生的View已经帮我们处理好了,我们不用管。我们就就管我们自定义的View。
注意:为了让Activity在状态保存和恢复的过程中能触发调用View的状态保存和恢复,我们一定要在Activity的布局文件中给这个View id android:id,否则View的状态保存和恢复函数不会调用
下面给出一个通用自定义View状态保存恢复的模板。
@Nullable @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(BUNDLE_SUPER, super.onSaveInstanceState()); /** * 这里通过bundle放置自定义的要保存的一些状态 */ return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { Bundle bundle = (Bundle) state; super.onRestoreInstanceState(bundle.getParcelable(BUNDLE_SUPER)); /** * 这里通过bundle得到我们要恢复的一些状态 */ }
本文涉及到的所有相关实例(可能参考价值不是特别的大):实例DEMO
如发现文章的错误欢迎大家指正提出
- fragment的简单介绍
- Fragment的简单介绍
- Fragment简单介绍
- Fragment简单介绍
- Fragment 介绍
- Fragment介绍
- Fragment介绍
- Fragment介绍
- Fragment介绍
- Fragment介绍:
- Fragment介绍
- Fragment介绍
- 底部菜单栏(一)Fragment介绍和简单实现
- 底部菜单栏(一)Fragment介绍和简单实现
- Android技术成长之路(Fragment的简单介绍)
- 【fragment】Android Fragment 基本介绍
- Android Fragment---概要介绍
- Fragment应用的介绍
- 【Scikit-Learn 中文文档】20 流形学习
- PAT
- react中的事件
- python双版本共存
- 不只是阿里的操作系统,AliOS正式开源!
- Fragment简单介绍
- LeetCode Regular Expression Matching
- C++学习之路(25)---vs2015动态链接库.dll的生成方法
- MySQL的server_uuid获取之uuid()函数和uuid_short()函数
- Retrofit总结
- 河南高性能计算研发力量调查
- react入坑之高阶组件
- Codeforces893E
- 应用Maven-Assembly-Plugin插件构建可执行软件包