什么是Fragment?
简单来说,Fragment其实可以理解为一个具有自己生命周期的控件,只不过这个控件又有点特殊,它有自己的处理输入事件的能力,有自己的生命周期,又必须依赖于Activity,能互相通信和托管 。
说白了:Fragment也是用来显示的;就是可以把“按钮(Button)”、“文本(TextView)”等组件放在上面,再通过“onCreateView”方法返回的“view”,在“Activity”中显示出来。但是,Fragment的显示依赖于“Activity”,需要通过“Activity”将其展示出来,它本身不具有显示的功能。
Fragment,也简称为:碎片,是Android 3.0(API 11)提出的,为了兼容低版本,support-v4库中也开发了一套Fragment API,最低兼容Android 1.6。
过去support-v4库是一个jar包,24.2.0版本开始,将support-v4库模块化为多个jar包,包含:support-fragment, support-ui, support-media-compat等,这么做是为了减少APK包大小,你需要用哪个模块就引入哪个模块。
因为support库是不断更新的,因此建议使用support库中的android.support.v4.app.Fragment,而不要用系统自带的android.app.Fragment。而如果要使用support库的Fragment,Activity必须要继承FragmentActivity(AppCompatActivity是FragmentActivity的子类)。
基本使用
(1)将Fragment当作控件使用
这种方法是使用Fragment的最简单的一种方式了,我们只需要声明一个类继承自Fragment实现其onCreateView方法,并将fragment声明在Activity的xml里即可。我们来看代码:
AFragment.java:
@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.afragment, container, false);}
afragment.xml:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="AFragment" />RelativeLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.xn.myproject.MainActivity"> <fragment android:id="@+id/fragmenta" android:name="com.xn.myproject.fragment.AFragment" android:layout_width="match_parent" android:layout_height="match_parent" />LinearLayout>
效果图:
这样我们就将AFragment作为一个控件显示出来了,十分简单,只是需要注意fragment控件一定要加id属性即可,否则会崩溃。。但是将Fragment作为一个控件来使用就不推荐了。我们下面看看其它的使用方法:
(2)FragmentManager动态加载Fragment
在代码中通过FragmentManager获取FragmentTransaction来进行Fragment的动态添加才是我们最常用的使用方式。先来看代码:
MainActivity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAFragment = new AFragment(); getFragmentManager().beginTransaction() .replace(R.id.main_container, mAFragment).commit(); getFragmentManager().beginTransaction().show(mAFragment); }
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.xn.myproject.MainActivity"> <FrameLayout android:id="@+id/main_container" android:layout_width="match_parent" android:layout_height="match_parent" />LinearLayout>
这里我们在Activity中获取FragmentManager然后再进一步获取到FragmentTransaction对象将我们new出来的AFragment add到FrameLayout中。
这种动态加载Fragment的方式十分灵活,可以让我们在代码当中动态的决定加载哪些Fragment显示出来。这里我们需要重点关注的是FragmentTransaction对象。除了例子当中使用的add操作以外,它还有replace,hide,show,remove等操作。
1)add(int containerViewId, Fragment fragment)
这个方法是将fragmen添加到我们指定id的layout中.
2)hide(Fragment fragment)和show(Fragment fragment)
隐藏或者显示指定的fragment,类似于我们在View中经常使用的setVisibly方法,需要注意的是,这里的hide和show仅仅只是让fragment显示和隐藏,不会对fragment进行销毁,甚至我们在hide的时候fragment的onPause方法都没有被调用。
3)remove(Fragment fragment)
会将fragment移除,如果被移除的Fragment没有添加到回退栈,该Fragment会同时被销毁。
4)replace(int containerViewId, Fragment fragment)
replace方法是用来进行替换的,实际上也就是对指定的layout id先remove掉其fragment,然后再add上去我们指定的fragment的一种组合操作。
5)detach()
会将view从UI中移除,和remove()不同,此时fragment并没有与Activity断绝关系,所以生命周期的onDestroy方法和onDetach方法并没有被调用
6)attach()
重建view视图,附加到UI上并显示,如果调用完detach方法后再来调用该方法的话不会去走onAttach和onCreate方法。
需要注意的是,我们在进行了上述的各种操作以后一定要调用commit方法提交事务才能生效。
Fragment生命周期
上图所示我们可以看到Fragment生命周期对应Activity的各个生命周期方法。
部分方法说明:
1).onAttach
当该Fragment与Activity发生关联的时候调用,注意的是这个方法里会给我们传入一个Context上下文参数,此时我们可以将其存入成员变量中进行使用,避免在代码中调用getActivity()出现的空指针异常。
2).onCreate
当创建Fragment的时候调用与onAttach方法是一起调用的,如果没有调用到onAttach方法就不会调用该方法。
3).onCreateView
每次创建、绘制Fragment的View时调用,并且返回一个view对象。
4).onActivityCreated
当Fragment所在的Activity被onCreate完成时调用。
5)onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用。
6)onDestroy()方法
与onCreate想对应当Fragment的状态被销毁的时候进行调用。
6)onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用,需要注意的是我们调用detach方法的时候并不会调用到该生命周期方法。
Fragment的典型应用场景
Fragment的应用场景最多的便是ViewPager+Fragment的实现,现在主流的APP几乎都能看到它们的身影,那么这一部分我就主要针对该应用场景进行分析。
ViewPager+Fragment结构
相信绝大多数的人都用ViewPager+Fragment的形式实现过界面,同时目前市面上主流的APP也都是采用这种结构来进行UI架构的,所以我们有必要单独对这种情况拿出来做一下分析。
首先我们来看一段代码:
public class MainActivity extends FragmentActivity implements View.OnClickListener { private ViewPager mViewPager; private List mList; private Fragment mOne; private Fragment mTwo; private Fragment mThree; private Fragment mFour; private Button mOneButton; private Button mTwoButton; private Button mThreeButton; private Button mFourButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mViewPager = (ViewPager) findViewById(R.id.content_pager); //加载Fragment mList = new ArrayList<>(); mOne = new OneFragment(); mTwo = new TwoFragment(); mThree = new ThreeFragment(); mFour = new FourFragment(); mList.add(mOne); mList.add(mTwo); mList.add(mThree); mList.add(mFour); mOneButton = (Button) findViewById(R.id.one); mTwoButton = (Button) findViewById(R.id.two); mThreeButton = (Button) findViewById(R.id.three); mFourButton = (Button) findViewById(R.id.four); mOneButton.setOnClickListener(this); mTwoButton.setOnClickListener(this); mThreeButton.setOnClickListener(this); mFourButton.setOnClickListener(this); //设置到ViewPager中 mViewPager.setAdapter(new ContentsPagerAdapter( getSupportFragmentManager())); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.one : mViewPager.setCurrentItem(0); break; case R.id.two : mViewPager.setCurrentItem(1); break; case R.id.three : mViewPager.setCurrentItem(2); break; case R.id.four : mViewPager.setCurrentItem(3); break; } } class ContentsPagerAdapter extends FragmentStatePagerAdapter { public ContentsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return mList.get(position); } @Override public int getCount() { return mList.size(); } }}
在这里我们加载了4个Fragment到ViewPager中,同时我在这里使用的是
FragmentStatePagerAdapter。这里需要提到的是FragmentStatePagerAdapter与FragmentPagerAdapter的区别。FragmentPagerAdapter:对于不再需要的fragment,仅仅只会调用到onDestroyView方法,也就是仅仅销毁视图而并没有完全销毁Fragment。FragmentStatePagerAdapter:会销毁不再需要的fragment,一直调用到onDetach方法失去与Activity的绑定。销毁时,会调用onSaveInstanceState(Bundle outState)方法通过bundle将信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,我们可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。下面我们来看一下日志就清楚了:
首先在使用FragmentPagerAdapter中的时候我们观察日志:
首先进入的时候ViewPager处于第一个Fragment上,此时由于ViewPager的预加载功能TwoFragment也被加载了,通过日志我们就能看到。当我们此时切换到了第四个Fragment中去的时候,我们就会发现OneFragment仅仅只是调用了onDestroyView方法而已,后面的onDestroy方法很onDetach方法都没被调用到。
同样的操作再在FragmentStatePagerAdapter里来一遍,我们会发现当我们切换的时候One和Two的Fragment的onDestroyView,onDestroy,onDetach全部都调用到了。同时我在OnewFragment通过onSaveInstanceState方法存起来的值在下一次的onCreate的时候也能读取到。
通过上面的代码和例子我们基本搞清楚了ViewPager与Fragment如何结合起来使用,以及他们的生命周期调用我们也弄清楚了。那么我们到底什么时候用FragmentStatePagerAdapter,什么时候用FragmentPagerAdapter呢?根据我的经验来看,当页面较少的情况下可以考虑使用FragmentPagerAdapter,通过空间来换取时间上的效率。但当页面多了的时候我们就更需要使用FragmentStatePagerAdapter来做了,因为没有哪个用户希望某个应用会占爆它内存。
以上就是fragment的基本用法,如果有需要可以去官方文档详细了解:
https://developer.android.com/guide/topics/providers/content-provider-basics?hl=zh-cn