第四章 Fragemnt(碎片)
4.1 碎片是什么
碎片( Fragment)是一种可以嵌入在活动当中的 UI 片段,它能让程序更加合理和充分地利用大屏幕的空间, 因而在平板上应用的非常广泛。
4.2 碎片的使用方式
4.2.1碎片的简单用法
有两个不同包下的 Fragment 供你选择, 建议使用android.app.Fragment, 因为我们的程序是面向 Android 4.0以上系统的,另一个包下的 Fragment 主要是用于兼容低版本的 Android 系统。
建立两个Fragment,一个Activity,Activity包含两个Fragment,可以在XML中进行配置:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
4.2.2 动态添加碎片
布局文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="@+id/right_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" >
<fragment
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
MainActivity中:
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
AnotherRightFragment fragment = new AnotherFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
}
}
动态添加碎片主要分5步:
1.创建待添加碎片实例。
2.获取FragmentManager,在活动中可以直接调用getFragmentManager().
3.开启一个事务,通过调用beginTransaction()
4.向容器中加入碎片,一般使用replace()实现,需传入容器id和待添加碎片实例。
5.提交事务,调用commit().
4.2.3 在碎片中模拟返回栈
如果需要按下 Back 键可以回到上一个碎片:
FragmentTransaction 中提供了一个 addToBackStack()方法,可以用于将一个事务添加到返回栈中
AnotherRightFragment fragment = new AnotherRightFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.
beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();
在事务提交之前调用了 FragmentTransaction 的 addToBackStack()方法,它可以接收一个名字用于描述返回栈的状态, 一般传入 null 即可。
4.2.4 碎片和活动之间进行通信
活动获取碎片实例
RightFragment rightFragment = (RightFragment) getFragmentManager()
.findFragmentById(R.id.right_fragment);
碎片获取活动:
MainActivity activity = (MainActivity) getActivity();
当碎片中需要使用 Context 对象时, 也可以使用 getActivity()方法, 因为获取到的活动本身就是一个 Context对象了。
碎片之间通信:
在一个碎片中可以得到与它相关联的活动, 然后再通过这个活动去获取另外一个碎片的实例。
4.3 碎片的生命周期
4.3.1 碎片的状态和回调
1. 运行状态
当一个碎片是可见的, 并且它所关联的活动正处于运行状态时, 该碎片也处于运行状态。
2. 暂停状态
当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入到暂停状态。
3. 停止状态
当一个活动进入停止状态时, 与它相关联的碎片就会进入到停止状态。 或者通过调用 FragmentTransaction 的 remove()、 replace()方法将碎片从活动中移除,但有在事务提交之前调用 addToBackStack()方法,这时的碎片也会进入到停止状态。总的来说,进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收。
4. 销毁状态
碎片总是依附于活动而存在的, 因此当活动被销毁时, 与它相关联的碎片就会进入到销毁状态。或者通过调用 FragmentTransaction 的 remove()、 replace()方法将碎片从活动中移除,但在事务提交之前并没有调用 addToBackStack()方法,这时的碎片也会进入到销毁状态。
比activity附加的回调方法
- onAttach()
当碎片和活动建立关联的时候调用。 - onCreateView()
为碎片创建视图(加载布局)时调用。 - onActivityCreated()
确保与碎片相关联的活动一定已经创建完毕的时候调用。 - onDestroyView()
当与碎片关联的视图被移除的时候调用。 - onDetach()
当碎片和活动解除关联的时候调用
生命周期图如下:
注意:这里从返回栈中回到上一个碎片,上一个碎片的从onActivityCreated()开始,而不是onCreateView()!!!
在碎片中也可以通过 onSaveInstanceState()方法来保存数据,因为进入停止状态的碎片有可能在系统内存不足的时候被回收。保存下来的数据 在onCreate()、 onCreateView()和 onActivityCreated()这三个方法中你都可以重新得到, 它们都含有一个 Bundle 类型的 savedInstanceState 参数。
4.4 动态加载布局
4.4.1 使用限定符
如果平板使用双页模式,手机使用单页模式,怎样才能在运行时判断程序应该是使用双页模式还是单页模式呢?这就需要借助
限定符(Qualifiers) 来实现。
- 项目中的 activity_main.xml 文件,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- 在 res目录下新建 layout-large 文件夹,在这个文件夹下新建一个布局,也叫做 activity_main.xml,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/right_fragment"
android:name="com.example.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" />
</LinearLayout>
Android 中一些常见的限定符可以参考下表:
4.4.2 使用最小宽度限定符
最小宽度限定符允许我们对屏幕的宽度指定一个最小指(以 dp 为单位), 然后以这个最小值为临界点, 屏幕宽度大于这个值的设备就加载一个布局, 屏幕宽度小于这个值的设备就加载另一个布局。
在 res 目录下新建 layout-sw600dp 文件夹,然后在这个文件夹下新建 activity_main.xml布局,当程序运行在屏幕宽度大于 600dp 的设备上时,会加载 layout-sw600dp/activity_main 布局,当程序运行在屏幕宽度小于 600dp 的设备上时,则仍然加载默认的layout/activity_main 布局。需要注意一点,最小宽度限定符是在 Android 3.2 版本引入的。
4.5 碎片的最佳实践——一个简易版的新闻应用
同一个app,根据分辨率不同加载不同布局实现不同功能,如平板用双页,app用单页。主要判断双页中多出来的fragement是否为空,如果为空实现单页功能如跳页,如果不为空实现双页功能如在右侧fragment中显示左侧列表项对应的内容。