Fragment
Android是在Android 3.0 (API level 11)开始引入Fragment的。
可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。
可以把Fragment设计成可以在多个Activity中复用的模块。
当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。
如图:
Fragment的特征
1.Fragment 总是作为 Activity 界面的组成部分。Fragment 可调用 getActivity() 方法获取它所在的 Activity(),Activity 可调用 FragmentManager 的 findFragmentById() 或 findFragmentByTag() 方法来获取 Fragment。
2.在 Activity运行过程中,可调用 FragmentManager 的 add()、remove()、replace() 方法动态地添加、删除或替换 Fragment。
3.一个 Activity 可以同时组合多个 Fragment;反过来,一个 Fragment 也可以被多个 Activity 复用。
Fragment 与 Activity 通信
为了在 Activity 中更现实 Fragment,还必须将 Fragment 添加到 Activity 中。将 Fragment 添加到 Activity 中有如下两种方式:
1.在布局文件中使用 <fragment.../> 元素添加 Fragment,<fragment.../> 元素的 android:name 属性指定 Fragment 的实现类。
2.在 Java 代码中通过 FragmentTransation 对象的 add() 方法来添加 Fragment。
注1:Activity 的getFragmentManager() 方法可返回 FragmentManager,FragmentManager 对象的 beginTransaction() 方法可开启并返回 FragmentTransaction 对象。
注2:如果 import android.support.v4.app.FragmentManager; 那么使用的是:FragmentManager fragmentManager =getSupportFragmentManager();
将 Fragment 添加到 Activity 之后,Fragment 必须与 Activity 交互信息,这就需要 Fragment 能获取它所在的 Activity,Activity 也能获取它所包含的任意的 Fragment。可按如下方法进行:
1. Fragment 获取它所在的 Activity:调用 Fragment 的getActivity() 方法即可返回它所在的 Activity。
2. Activity 获取它所包含的 Fragment:调用 Activity 关联的FragmentManager的findFragmentById(int id)或 findFragmentByTag(String tag)方法即可获取指定的 Fragment。(需要在 <fragment.../> 中指定 android:id 或者 android:tag 属性)
除此之外,Fragment 和 Activity 可能还需要相互传递数据,可按如下方法进行:
1. Activity 向 Fragment 传递数据:在 Activity 中创建 Bundle 数据包,并调用 Fragment 的 setArguments(Bundle bundle) 方法即可将 Bundle 数据包传递给 Fragment。
2. Fragment 向 Activity 传递数据或者 Activity 需要在 Fragment 运行中进行实时通信:在 Fragment 中定义一个内部回调接口,再让包含该 Fragment 的 Activity 实现该回调接口,这样 Fragment 即可调用该回调方法将数据传给 Activity。
Fragment 管理与 Fragment 事务
Activity 管理 Fragment 主要依靠 FragmentManager。FragmentManager 可以完成如下几方面的功能:
1. 使用findFragmentById(int id) 或 findFragmentByTag(String tag) 方法来获取指定的 Fragment。
2. 调用 popBackStack() 方法将 Fragment 从后台栈中弹出(模拟用户按下 BACK 按键)。
3. 调用 addOnBackStackChangeListener() 注册一个监听器,用于监听后台栈的变化。
如果需要添加、删除、替换 Fragment,则需要借助于 FragmentTransaction 对象,FtagmentTransaction 代表 Activity 对 Fragment 执行的多个改变。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransation = frangmentManager.beginTransaction();
每个 FragmentTransaction 可以包含多个对 Fragment 修改,比如包含调用了多个 add()、remove()、和 replace() 操作,最后还要调用 commit() 方法提交事务即可。
在调用 commit() 之前,开发者也可调用 addToBackStack() 将事务添加到 back 栈,该栈由 Activity 负责管理,这样允许用户按 Back 按键返回到前一个 Fragment 状态。
//创建一个新的 Fragment 并打开事务
Fragment newFragment = new ExampleFragment();
FragmentTransaction transation = getFragmentManager().beginTransaction();
//替换该界面中 fragment_container 容器内的 Fragment
transaction.replace(R.id.fragment_container, newFragment);
//将事务填加到 back 栈,允许用户按 BACK 按键返回到替换 Fragment 之前的状态
transaction.addToBackStack(null);
//提交事务
transaction.commit();
Fragment 的生命周期
因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。
如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。
但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。
当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的back stack中,这样用户就可以进行返回操作。
1. 使用Support Library
Support Library 是一个提供了 API 库函数的J AR 文件,这样就可以在旧版本的 Android 上使用一些新版本的 APIs。
比如 android-support-v4.jar.它的完整路径是:
<sdk>/extras/android/support/v4/android-support-v4.jar.
它就提供了 Fragment 的 APIs,使得在 Android 1.6 (API level 4) 以上的系统都可以使用 Fragment。
为了确定没有在旧版本系统上使用新版本的 APIs,需要如下导入语句:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
同时应该将上述的包拷入 libs 项目下的 libs 文件夹,然后在项目的 Properties 中添加:右键单击项目,选 Properties,左边选 Java Build Path,然后 Add External JARs…,添加 android-support-v4.jar.
2. 必须实现的三个回调函数
1. onCreate()
系统在创建 Fragment 的时候调用这个方法,这里应该初始化相关的组件,一些即便是被暂停或者被停止时依然需要保留的东西。
2. onCreateView()
当第一次绘制 Fragment 的 UI 时系统调用这个方法,必须返回一个 View,如果 Fragment 不提供 UI 也可以返回 null。
注意,如果继承自 ListFragment,onCreateView() 默认的实现会返回一个 ListView ,所以不用自己实现。
3. onPause()
当用户离开 Fragment 时第一个调用这个方法,需要提交一些变化,因为用户很可能不再返回来。
3. 实现 Fragment 的 UI
提供 Fragment 的 UI,必须实现 onCreateView() 方法。
假设 Fragment 的布局设置写在 example_fragment.xml 资源文件中,那么 onCreateView() 方法可以如下写:
public static class ExampleFragment extends Fragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
onCreateView() 中 container 参数代表该 Fragment 在 Activity 中的父控件;savedInstanceState 提供了上一个实例的数据。
inflate() 方法的三个参数:
第一个是resource ID,指明了当前的Fragment对应的资源文件;
第二个参数是父容器控件;
第三个布尔值参数表明是否连接该布局和其父容器控件,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。
例:
ExampleFragment.java
<span style="font-size:10px;">public class ExampleFragment extends Fragment
{
//三个一般必须重载的方法
@Override
public void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
System.out.println("ExampleFragment--onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
System.out.println("ExampleFragment--onCreateView");
return inflater.inflate(R.layout.example_fragment_layout, container, false);
}
@Override
public void onPause()
{
// TODO Auto-generated method stub
super.onPause();
System.out.println("ExampleFragment--onPause");
}
@Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
System.out.println("ExampleFragment--onResume");
}
@Override
public void onStop()
{
// TODO Auto-generated method stub
super.onStop();
System.out.println("ExampleFragment--onStop");
}
}</span>
example_fragment_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/num1"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/num2"
/>
</LinearLayout>
LearnFragment.java
public class LearnFragment extends FragmentActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_learn_fragment);
//在程序中加入Fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.linear, fragment);
fragmentTransaction.commit();
}
}
<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"
>
<Button
android:id="@+id/btn1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/btn1"
/>
<fragment
android:name="com.example.learningfragment.ExampleFragment"
android:id="@+id/fragment1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/btn2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/btn2"
/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<Button
android:id="@+id/btn3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/btn3"
/>
</LinearLayout>
</LinearLayout>