什么是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>

效果图:

Android fragment 获取父fragment activity 获取fragment 里面控件_android fragment

这样我们就将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生命周期

Android fragment 获取父fragment activity 获取fragment 里面控件_android fragment_02

上图所示我们可以看到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中的时候我们观察日志:

Android fragment 获取父fragment activity 获取fragment 里面控件_android fragment_03

首先进入的时候ViewPager处于第一个Fragment上,此时由于ViewPager的预加载功能TwoFragment也被加载了,通过日志我们就能看到。当我们此时切换到了第四个Fragment中去的时候,我们就会发现OneFragment仅仅只是调用了onDestroyView方法而已,后面的onDestroy方法很onDetach方法都没被调用到。

Android fragment 获取父fragment activity 获取fragment 里面控件_android fragment_04

同样的操作再在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