Fragment

  碎片(Fragment)是一种可以嵌入在活动(Activity)中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间。Fragment拥有自己的生命周期,可以接受处理用户的事件,并且可以在一个Activity中动态的添加、替换、移除不同的Fragment,对于信息的展示具有很大的便利性。

Fragment的生命周期

  Fragment是依附于Activity存在的,因此它的生命周期收到Activity的生命周期影响。

  Fragment比Activity多了几个生命周期的回调方法:
1 . onAttach(Activity):当Fragment与Activity发生关联的时候调用;
2 . onCreateView(LayoutInflater,ViewGroup,Bundle):创建该Fragment的视图;
3 . onActivityCreated(Bundle):当Activity的onCreate()方法返回时调用;
4 . onDestroyView():与onCreateView(LayoutInflater,ViewGroup,Bundle)方法对应,当该Fragment的视图被移除时调用;
5 . onDetach():与onAttach(Activity)方法对应,当Fragment与Activity取消关联时调用;

  注意:除了onCreateView()方法,其他的所有方法如果重写了,必须调用父类对于该方法的实现。

Fragment基础用法

  有两个不同包下面的Fragment都可以使用,一个是系统内置的android.app.Fragment,一个是support-v4库中的android.support.v4.app.Fragment。建议使用support-v4库中的Fragment,因为可以让碎片在所有Android系统版本中保持功能一致性。比如说在Fragment中嵌套使用Fragment,这个功能是在Android4.2系统中才开始支持的,如果你使用的是系统内置的Fragment,那么在4.2系统之前的设备运行你的程序就会崩溃,而使用support-v4库中的Fragment就不会出现这个问题。
  另外,我们不需要在build.gradle文件中添加support-v4库的依赖,因为build.gradle文件中已经添加了appcompat-v7库的依赖,而这个库会将support-v4库也一起引入进来。
  使用Android Studio的高版本,会默认使用androidx下面的Fragment,不提供使用support-v4包下面的Fragment。

静态添加Fragment

  静态添加Fragment比较简单,创建要显示的Fragment,然后在Activity的xml布局中添加<fragment>标签,需要使用属性name来添加要展示的Fragment,name属性值为要显示的Fragment的路径。然后就可以在Activity中显示了。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".fragment.FragmentActivity">

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.kang.fragment.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />
    
    <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.kang.fragment.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />
</LinearLayout>

动态添加Fragment

  有时候,我们的业务逻辑会需要替换Fragment来进行动态展示,所以就需要用到动态添加Fragment。动态添加Fragment要稍微复杂一些,步骤如下:
1 . 创建要显示的Fragment,也就是进行实例化;
2 . 在v4包中,获取FragmentManager是直接通过调用getSupportFragmentManager(),使用FragmentManager对Fragment进行管理;
3 . 通过调用FragmentManager对象的beginTransaction()方法创建一个FragmentTransaction事务对象;
4 . 向布局容器内添加或替换Fragment,一般使用replace()方法实现,需要传入布局容器的id和待添加的Fragment;
5 . 提交事务,调用commit()方法来完成;

  FragmentTransaction主要有以下几种方法:
1 . add():向Activity中添加一个Fragment;
2 . remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁;
3 . replace():使用另一个Fragment替换当前的,实际上就是remove()和add()的合体;
4 . hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁;
5 . show():显示之前隐藏的Fragment;
6 . detach():会将view从UI中移除,和remove()不同,此时Fragment的状态依然由FragmentManager维护;
7 . attach():重建view视图,附加到UI上并显示;
8 . commit():提交事务;

  注意:在add()、replace()、hide()、show()以后都要使用commit()方法,效果才会在屏幕上显示出来;

//在Activity的xml布局中;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context=".fragment.FragmentActivity">

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.kang.fragment.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />
    <FrameLayout
        android:id="@+id/fragment_fl"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />

</LinearLayout>
//在Activity中调用;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment);

    replaceFragment(new RightFragment());
}

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    //R.id.fragment_fl就是要显示的Fragment的布局容器的id;
    fragmentTransaction.replace(R.id.fragment_fl,fragment);
    fragmentTransaction.commit();
}

在Fragment中模拟返回栈

  当我们动态添加Fragment之后,这时按下手机的返回键,Activity就会直接退出。如果我们想要模仿Activity的返回栈的效果,按下手机的返回键回到上一个动态添加的Fragment,只需要使用FragmentTransaction的一个addToBackStack()方法。
  我们在提交事务之前调用FragmentTransaction的addToBackStack()方法,这个方法接收一个名字用于描述返回栈的状态,一般传入null即可。

private void replaceFragment(Fragment fragment) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    //R.id.fragment_fl就是要显示的Fragment的布局容器的id;
    fragmentTransaction.replace(R.id.fragment_fl,fragment);
    //实现返回栈模式;
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

Fragment和Activity以及Fragment和Fragment之间的通信

  • Fragment和Activity之间的通信
    1 . 虽然Fragment是嵌入Activity之中显示的,但是Fragment和Activity都是各自存在于一个独立的类当中的,它们之间没有明显的方式来直接进行通信。
      为了方便Fragment和Activity之间进行通信,FragmentManager提供了一个类似于findViewById()的方法,也就是findFragmentById()方法,还有一个findFragmentByTag()方法,都可以用于获取Fragment的实例。这样就可以在Activity中获取相应的Fragment的实例,就能调用Fragment中的方法了;
RightFragment rightFragment = (RightFragment)getSupportFragmentManager().findFragmentById(R.id.right_fragment);

2 . 在每个Fragment中都可以通过调用getActivity()方法来得到和当前Fragment相关联的Activity实例。有了Activity实例以后,就可以调用Activity之中的方法了;

FragmentActivity fragmentActivity = (FragmentActivity)getActivity();

3 . 如果Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法;

  • Fragment和Fragment之间的通信
      在一个Fragment之中可以获取到与它相关联的Activity,然后再通过这个Activity去获取另一个Fragment的实例,这样就哭可以实现不同Fragment之间的通信了。

  • Fragment和Activity之间的通信的优化
      虽然Fragment和Activity可以通过getActivity()与findFragmentByTag()、findFragmentById()方法,进行通信。但是不提倡使用这样的操作。因为Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外,虽然Fragment不能响应Intent操作,但是Activity可以响应Intent操作,然后根据Intent的数据参数来判断显示哪个Fragment。

此处要看看使用接口来实现Activity与Fragment通信的方式

处理运行时配置发生变化

  当屏幕旋转时,Activity会对屏幕上的视图进行重新绘制,因为Activity发生了重新启动,默认的Activity中的Fragment也会跟着Activity重新创建。不断的旋转自然就会不断地绘制,这是一种很耗费内存资源的操作。当旋转发生的时候,Activity会执行onCreate()方法,就会再次实例化一个新的Fragment。
  我们通过检查onCreate()方法中的参数savedInstanceState可以判断,当前是否发生了Activity的重新创建。所以,我们只需要简单地判断在savedInstanceState == null的时候,才进行创建Fragment实例就可以了。
  当需要重新绘制时,在onSaveInstanceState()方法中对数据进行保存,然后在onCreate()或者onCreateView()以及onActivityCreated()方法中都可以进行恢复。

Fragment的动态加载布局技巧

Fragment懒加载

-
-
-
-
-
-
-
-
-
-
-
-
-
-
---------------------last line for now--------------------