fragment 理解

来源:互联网 发布:国产羽绒服 知乎 编辑:程序博客网 时间:2024/05/24 04:15
Fragment概述

Fragment是Activity中用户界面的一个行为或者说是一部分。主要是支持大屏幕上动态显示和更为灵活的去组合或是交换UI组件,通过将Activity的布局分割成若干个fragment,可以在运行时编辑activity的呈现,并且那些变化会被保存在由activity管理的后台栈里面。
Fragment必须总是被嵌入到一个activity之中并且fragment的生命周期直接接受其宿主activity的生命周期的影响。你可以认为fragment是activity的一个模块零件,它有自己的生命周期,接收它自己的输入的事件,并且可以在activity运行时添加或者删除。
应该将每一个fragment设计为模块化和可复用化的activity组件也就是说,你可以在多个activity中引用同一个fragment,因为fragment定义了它自己的布局,并且使用它本身生命周期回调的行为。

  • 先看fragment生命周期图:

    Fragment周期图
  • 在看Fragment依附于Activity的生命状态图:

    Fragment与宿主Activity的声明周期方法调用顺序

Fragment生命周期中的那么多方法,快来学习一下吧!go go go

  • new 一个Fragment,Fragment只是调用了自身的空参数构造方法,并没有其他操作。

Fragment生命周期方法含义:
  • public void onAttach(Context context)
    onAttach()方法会在Fragment与Activity窗口关联后立刻调用。从该方法开始,就可以通过Fragment.getActivity()方法获取与Fragment关联的Activtiy窗口对象,但因为Fragment的控件未初始化,所以不能够操作控件。
  • public void onCreate(Bundle savedInstanceState)
    在调用完onAttach()执行完之后,立即就会调用onCreate()方法,可以在Bundle对象中获取一些在Activity中传过来的数据。通常会在该方法中读取保存的转态,获取或初始化一些数据。在该方法中不要进行耗时操作,不然Activity窗口不会显示。
  • public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    该方法是Fragment很重要的一个生命周期方法,因为会在该方法中创建Fragment显示的View,其中inflater是用来装载布局文件的,container是<fragment>标签的父标签对应对象,saveInstanceState参数可以获取Fragment保存的转态,如果未保存那么就为null
  • public void onViewCreated(View view,Bundle savedInstanceState)
    Android在创建完Fragment中的View对象之后,会立刻回调该方法。其中view参数就是onCreateView中返回的view,而bundle对象用于一般用途。
  • public void onActivityCreated(Bundle savedInstanceState)
    在Activity的onCreate()方法执行完之后,Android系统会立刻调用该方法,表示Activity窗口已经初始化完成,从这一个时候开始,就可以在Fragment中使用getActivity().findViewById(R.id.xxx);来操作Activity中的view了
  • public void onStart()
    这个没啥可讲的,但是一个细节需要知道,当系统调用该方法的时候,fragment已经显示在UI上了,但还不能进行互动,因为onResume()方法还没有执行完。
  • public void onResume()
    该方法为fragment从创建到显示Android系统调用的最后一个生命周期方法,调用完该方法时候,fragment就可以与用户互动了。
  • public void onPause()
    fragment由活动状态变成非活跃执行的第一个回调方法,通常可以在这个方法中保存一些需要临时暂停的工作。例如:存音乐播放速度,然后在onResume()方法中恢复音乐播放进度。(注意不能执行太耗时的操作,不然会导致另外一个fragment的onResume要展示得等到这个fragment的onPause执行完才会执行
  • public void onStop()
    当onStop()返回的时候,fragment将从屏幕上消失。
  • public void onDestoryView()
    该方法的调用意味着在onCreateView()中创建的视图都将被移除。
  • public void onDestroy()
    Android在Fragment不再使用时会调用该方法,要注意的是~这是Fragment还和Activity是藕断丝连!并且可以获得Fragment对象,但无法对获得的Fragment进行任何操作。
  • public void onDetach()
    为Fragment生命周期中的最后一个方法,当该方法执行完后,Fragment与Activity不再有关联。
Fragment比Activity多了几个额外的生命周期回调方法:
  • onAttach(Activity):当Fragment和Activity发生关联时使用。
  • onCreateView(LayoutInflater,ViewGroup,Bundle):创建该Fragment的视图
  • onActivityCreate(Bundle):当Activity的onCreate()方法返回时调用
  • onDestoryView():与onCreateView相对应,当该Fragment的视图被移除时调用
  • onDetach():与onAttach()相对应,当Fragment与Activity关联被取消时调用
    注意:除了onCreateView()方法,其他的所有的方法如果你重写了,必须调用父类对于该方法的实现
管理fragment生命周期与管理activity生命周期很相像

像activity一样,fragment也有三种状态:

  • Resumed
    fragment在运行中的activity中可见
  • Paused
    另一个activity处于前台且得到焦点,但是这个fragment所在的activtiy仍然可见(前台activity部分透明,或者没有覆盖全屏)。
  • Stopped
    fragment不可见。要么宿主activity已经停止,要么fragment已经从activity上移除,但已被添加到后台栈中。一个停止的fragment仍然活着(所有的状态和成员信息仍然由系统保留着)。但是,它对于用户来讲已经不再可见,并且如果activity被杀掉,它也将被杀掉。
    ————————————————————————————————
    如果activity的进程被杀掉了,在activity被重新创建时,你恢复fragment状态。可
    以执行fragment的onSaveIntanceState()来保存状态(注意:fragment是在onCreate(),onCreateView()或者onActivityCreate()中进行恢复)
    在生命周期方面,activity和fragment之间一个很重要的不同就是在各自的后台栈中是如何存储的。当activity停止时,默认情况下activity被安置在由系统管理的activity后台栈中;fragment仅当在一个事务被移除时,通过显式调用addToBackStack()请求保存的实例,该fragment才被置于由宿主activity管理的后台栈。
    要创建一个fragment,必须创建一个fragment的子类,一般情况下,我们至少需要实现以下几个fragment生命周期的方法:onCreate(),onCreateView(),onPause()
    @Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,                                    Bundle savedInstanceState) {           // Inflate the layout for this fragment         return inflater.inflate(R.layout.example_fragment,                                                container, false);}
    inflate()函数需要以下三个参数:
    (1).要inflate的布局的资源Id
    (2).被inflate的布局的父ViewGroup
    (3).一个布尔值,表明在inflate期间被inflate的布局是否应该附上ViewGroup(第二个参数container)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到容器中(container)——传入true会在最终的布局里创建一个多余的ViewGroup)
getFragmentManager()和getSupportFragmentManager()和getChildFragmentManager()
前两个属于activity,Android的v4扩展包中的FragmentActivity中获取FragmentManager使用的就是getSupportFragmentManager(),后面属于fragment。
在android中,对fragment的事务操作都是通过FragmentTranSaction来执行。操作大致可以分为两类:
1:>显示:add(), replace(), show(), attach()
2:>隐藏:remove(), hide(), detach()

说明调用show() & hide()方法时,Fragment的生命周期方法并不会被执行,仅仅是Fragment的View被显示或者​隐藏执行replace()(相当于remove和add的合体)时(至少两个Fragment),会执行第二个Fragment的onAttach()方法、执行第一个Fragment的onPause()-onDetach()方法,同时containerView会detach第一个Fragment的View。add()方法执行onAttach()-onResume()的生命周期,相对的remove()就是执行完成剩下的onPause()-onDetach()周期。remove会销毁整个Fragment实例,而detach则只是销毁其视图结构

(因为detach后,只被调用到了onPauseonStoponDestroyView方法,onDestroyonDetach方法并未被调用到。

主要的操作都是FragmentTransaction的方法:

a、获取FragmentManage的方式:
getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操作都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

  • 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维护。
    //当fragment被加入到回退栈的时候,该方法与*remove()*的作用是相同的,  //反之,该方法只是将fragment从视图中移除,  //之后仍然可以通过*attach()*方法重新使用fragment,  //而调用了*remove()*方法之后,  //不仅将Fragment从视图中移除,fragment还将不再可用。
  • attach()
    重建view视图,附加到UI上并显示。Fragment只有在attach后,才会受到Activity生命周期的影响,调用到自己的生命周期。
  • transatcion.commit()
    提交一个事务

注意:当使用add(),show(),hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),不会回调onStop()等生命周期方法而新的Fragment在创建时是不会回调onHiddenChanged(),这点要切记。

在调用commit()之前,可以将事务添加到fragment事务后台栈中(通过调用addToBackStatck())。这个后台栈由activity管理,并且允许用户通过按BACK键回退到前一个fragment状态。

下面的代码中一个fragment代替另一个fragment,并且将之前的fragment状态保留在后台栈中:

Fragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();transaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);transaction.commit();

注意
如果添加多个变更事务(例如另一个add()或者remove())并调用addToBackStack(),那么在调用commit()之前的所有应用的变更被作为一个单独的事务添加到后台栈中,并且BACK键可以将它们一起回退。
当移除一个fragment时,如果调用了addToBackStack(),那么之后fragment会被停止,如果用户回退,它将被恢复过来。
调用commit()并不立刻执行事务,相反,而是采取预约方式,一旦activity的界面线程(主线程)准备好便可运行起来。然而,如果有必要的话,你可以从界面线程调用executePendingTransations()立即执行由commit()提交的事务。
只能在activity保存状态(当用户离开activity时)之前用commit()提交事务。如果你尝试在那时之后提交,会抛出一个异常。这是因为如果activity需要被恢复,提交后的状态会被丢失。对于这类丢失提交的情况,可使用commitAllowingStateLoss()

管理Fragment回退栈
  • 跟踪回退栈的状态
    我们通过实现OnBackStackChangedListener接口来实现回退栈状态跟踪,具体代码如下:
    //implements接口public class XXX implements FragmentManager.OnBackStackChangedListener //实现接口所要实现的方法@Overridepublic void onBackStackChanged() {  //do whatevery you want}//设置回退栈监听接口getSupportFragmentManager().addOnBackStackChangedListener(this);
  • 管理回退栈
    (1).FragmentTransaction.addToBackStack(String)
    将一个刚刚添加的Fragment加入到回退栈中
    (2).getSupportFragmentManager().getBackStackEntryCount()
    获取回退栈中的实体数量
    (3).getSupportFragmentManager().popBackStack(String name, int flags)
    根据name立刻弹出栈顶的fragment
    (4).getSupportFragmentManager().popBackStack(int id, int flags)
    根据id立刻弹出栈顶的fragment
2.ViewPager管理
使用ViewPager等容器去装载Fragment列表并通过他们自己的页面切换能力去切换Fragment。

【关于ViewPager适配器相关类:FragmentPagerAdapter与FragmentStatePagerAdapter】

  • FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。
  • FragmentStatePagerAdapter:会调用remove方法,销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragment从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。
    • 总结使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。

因为Adapter destoryItem 要吗调用detach要吗调用remove,所以不在缓存的队列中则会走非fragment的一些生命周期,不像show()和Hide不调用什么周期。具体参考文章:http://www.jianshu.com/p/25a02f5a15b3

FragmentTabHost中的fragment的生命周期

FragmentTabHost不保存状态是因为切换fragment的时候是使用detach和attach来Fragment的隐藏和显示的,这样的话每次切换肯定要重新加载布局,与fragmentpageradapter相似。

问题:使用FragmentTabHost时,Fragment之间切换时每次都会调用 onCreateView方法,导致每次 Fragment的布局都重绘,无法保持Fragment原有状态。

解决办法:在Fragment  onCreateView方法中缓存View

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {if (view == null) {view = inflater.inflate(R.layout.fragment_tableindex, container, false);}// 缓存的viewiew需要判断是否已经被加过parent,// 如果有parent需要从parent删除,要不然会发生这个view已经有parent的错误。ViewGroup parent = (ViewGroup) view.getParent();if (parent != null) {parent.removeView(view);}return view;}
或者修改fragmenttabHost源码,具体可以参考:http://www.jianshu.com/p/4d4a83945193

Fragment 实现懒加载:

主要方法:

setUserVisibleHint(一般用在Viewpager)表示此fragment是否可见

这个方法仅仅工作在FragmentPagerAdapter中,不能被使用在一个普通的activity中。

因此我这个单个fragment 就是不被调用的 ,可以重写 onHiddenChanged() 方法来得知当前 fragment 的状态。

setUserVisibleHint此方法可能在frgament生命周期以外调用所以,必须判控防止控件未初始化。
所以想要直接在setUserVisibleHint中进行判断,然后直接进行数据的加载是行不通的。我们需要进行双重的判断
可以这样:
public abstract class BaseLazyOnlyOnceFragment extends Fragment {    protected View mRootView;    public Context mContext;    protected boolean isVisible;    private boolean isPrepared;    private boolean isFirst = true;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        EventBus.getDefault().register(this);        mContext = getActivity();    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        if (mRootView == null) {            mRootView = inflater.inflate(initViewLayoutId(), null);            ButterKnife.bind(this, mRootView);            initView(mRootView);        }        ViewGroup parent = (ViewGroup) mRootView.getParent();        if (parent != null) {            parent.removeView(mRootView);        }//        Log.d("TAG", "fragment->onCreateView");        return mRootView;    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);//        Log.d("TAG", "fragment->onActivityCreated");        isPrepared = true;        lazyLoad();    }    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);//        Log.d("TAG", "fragment->setUserVisibleHint");        if (getUserVisibleHint()) {            isVisible = true;            lazyLoad();        } else {            isVisible = false;            onInvisible();        }    }    //懒加载    protected void lazyLoad() {        if (!isPrepared || !isVisible || !isFirst) {            return;        }        Log.d("TAG", getClass().getName() + "->initData()");        initData();        isFirst = false;    }    //do something    protected void onInvisible() {    }    // 传入布局id    public abstract int initViewLayoutId();    //根据View 初始化操作    public abstract void initView(View view);    //加载数据    public abstract void initData();    @Override    public void onDestroy() {        super.onDestroy();        EventBus.getDefault().unregister(this);    }    @Subscribe(threadMode = ThreadMode.MainThread)    public void noneGoEvent(EmptyEvent emptyEvent) {    }}

/** * Created by lqh on 2017/7/13. * 这里我们做了三个判断,判断isPrepared和isVisible和isFirst只有全为true,才去执行initData()方法加载网络(或本地)数据。 ①isPrepared参数在系统调用onActivityCreated时设置为true,这时onCreateView方法已调用完毕(一般我们在这方法里执行findviewbyid等方法),确保 initData()方法不会报空指针异常。 ②isVisible参数在fragment可见时通过系统回调setUserVisibileHint方法设置为true,不可见时为false,这是fragment实现懒加载的关键。 ③isFirst确保ViewPager来回切换时BaseFragment的initData方法不会被重复调用,initData在该Fragment的整个生命周期只调用一次,第一次调用initData()方法后马上执行 isFirst = false。 */
这是实现只加载一次,而且缓存了View。如果要实现每次fragment可见,就实现加载数据,可以去除isFirst这个判断。

参考:http://www.jianshu.com/p/94bede7d6f46

http://www.jianshu.com/p/38f7994faa6b

原创粉丝点击