定义
Android在3.0后引入了Fragment,我们唤它叫碎片或片段。当初主要是为了给大屏幕(如平板电脑)上更加动态和灵活的UI设计提供支持。例如,新闻应用可以使用一个Fragment在左侧显示文章列表,使用另一个Fragment在右侧显示文章,两个Fragment并排显示在一个Activity中,每个Fragment都具有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。因此,用户不需要使用一个Activity来选择文章,然后使用另一个Activity来阅读文章,而是可以在同一个Activity内选择文章并进行阅读。后来随着应用功能越来越多,界面越来越复杂,Fragment被普通利用在对Activity界面进行UI模块化的使用。
一个Fragment是可以在多个Activity中复用的,所以在设计Fragment时,您应该尽量考虑把它设计为可重复使用的模块化 Activity 组件。也就是说,由于每个Fragment都会通过各自的生命周期回调来定义其自己的布局和行为,还要避免直接从某个Fragment直接操纵另一个Fragment。这特别重要,因为模块化片段让您可以通过更改Fragment的组合方式来适应不同的屏幕尺寸。在设计可同时支持平板电脑和手机的应用时,您可以在不同的布局配置中重复使用您的Fragment,以根据可用的屏幕空间优化用户体验。例如,在手机上,如果不能在同一 Activity 内储存多个Fragment,可能必须利用单独Fragment来实现单窗格 UI。
Fragment的生命周期
要想创建Fragment,您必须创建Fragment的子类。Fragment类的代码与Activity非常相似。它包含与 Activity 类似的回调生命周期方法,下图是官方文档中关于Fragment和Activity生命周期的对比:
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach() 当Fragment与Activity建立关联时调用
onCreate()
onCreateView() 用于创建Fragment的视图
onActivityCreated() 当Activity的onCreate()方法被返回时调用
当Fragment变得可见时,会经历以下状态:
onStart()
onResume()
当Fragment进入后台模式时,会经历以下状态:
onPause()
onStop()
当Fragment被销毁(它当前所在的Activity被销毁)时,会经历以下状态:
onDestroyView() 当Fragment的视图被移除时调用
onDestroy()
onDetach() 当Fragment与Activity的关联被移除时调用
与Activity一样,可以使用Bundle对象在以下状态中还原Fragment的实例:
onCreate()
onCreateView()
onActivityCreated()
与Activity生命周期回调方法的先后顺序
Fragment创建到激活过程与Activity相关的生命周期:
Activity | 顺序 | Fragment |
onCreate | <-1 |
|
| 2-> | onAttach |
onAttachFragment | <-3 |
|
| 4-> | onCreate |
| 5-> | onCreateView |
| 6-> | onActvitiyCreated |
onStart | <-7 |
|
| 8-> | onStart |
onResume | <-9 |
|
| 10-> | onResume |
Fragment销毁过程与Activity相关的生命周期:
Activity | 顺序 | Fragment |
| 1-> | onPause |
onPause | <-2 |
|
| 3-> | onSaveInstanceState |
onSaveInstanceState | <-4 |
|
| 5-> | onStop |
onStop | <-6 |
|
| 7-> | onDestroyView |
| 8-> | onDestroy |
| 9-> | onDetach |
onDetach | <-10 |
|
Fragment的使用
静态的使用Fragment
静态的意思就是在布局XML中预先定义好要使用的Fragment,我们来看看一个上下结构的使用Fragment示例步骤。
新建fragment_title.xml,用于title的布局:
<?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="40dp"
android:background="@color/title"
android:orientation="horizontal">
<TextView
android:id="@+id/txt_title1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="标题1"
android:textSize="20sp"
android:textColor="@color/title_text"
android:layout_weight="1"/>
<TextView
android:id="@+id/txt_title2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="标题2"
android:textSize="20sp"
android:textColor="@color/title_text"
android:layout_weight="1"/>
</LinearLayout>
新建TitleFragment,使其继承Fragment,并在onCreateView中关联fragment_title.xml:
public class TitleFragment extends Fragment implements View.OnClickListener {
private TextView mTxtTitle1;
private TextView mTxtTitle2;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_title, container, false);
mTxtTitle1 = (TextView) view.findViewById(R.id.txt_title1);
mTxtTitle1.setOnClickListener(this);
mTxtTitle2 = (TextView) view.findViewById(R.id.txt_title2);
mTxtTitle2.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.txt_title1) {
Toast.makeText(getActivity(), "点击了标题1",Toast.LENGTH_SHORT).show();
} else if (v.getId() == R.id.txt_title2) {
Toast.makeText(getActivity(), "点击了标题2",Toast.LENGTH_SHORT).show();
}
}
}
同理新建fragment_content.xml和ContentFragment:
<?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"
android:background="@color/content">
<Button
android:id="@+id/btn_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="30dp"
android:text="内容"/>
</RelativeLayout>
public class ContentFragment extends Fragment {
private Button mBtnContent;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_content, container, false);
mBtnContent = (Button) view.findViewById(R.id.btn_content);
mBtnContent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "点击了内容", Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
这样两个Fragment都定义好,接下来就开始在Acitity中使用它们,修改activity_main.xml:
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayout 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:background="@color/title">
<fragment
android:id="@+id/fragment_title"
android:name="完整包名.TitleFragment"
android:layout_width="match_parent"
android:layout_height="40dp" />
<fragment
android:id="@+id/fragment_content"
android:name="完整包名.ContentFragment"
android:layout_below="@id/fragment_title"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
MainActivity不用修改什么,默认就好:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
就这样,运行APP可见下图,而且可以看到所有的控件事件处理代码都由各自所在的Fragment去处理,这种代码独立分离使整个工程的代码的可读性、复用性和维护性都大大提升。
动态的使用Fragment
动态使用,就是向Activity代码中动态添加Fragment,可使用FragmentManager类。还需要在Activity中使用FragmentTransaction类来执行Fragment操作(如添加、删除或替换)。扩展上示例,新添加一个ContentFragment2:
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/content">
<Button
android:id="@+id/btn_content2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="30dp"
android:text="内容2"/>
</RelativeLayout>
public class ContentFragment2 extends Fragment {
privateButton mBtnContent;
@Override
public ViewonCreateView(LayoutInflater inflater, ViewGroup container, BundlesavedInstanceState) {
Viewview = inflater.inflate(R.layout.fragment_content2, container, false);
mBtnContent = (Button) view.findViewById(R.id.btn_content2);
mBtnContent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "点击了内容2",Toast.LENGTH_SHORT).show();
}
});
returnview;
}
}
修改activity_main.xml,在内容的Fragment中用FrameLayout替换fragment:
<?xml version="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns: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:background="@color/title">
<fragment
android:id="@+id/fragment_title"
android:name="完整包名.TitleFragment"
android:layout_width="match_parent"
android:layout_height="40dp" />
<!--<fragment-->
<!--android:id="@+id/fragment_content"-->
<!--android:name="完整包名.ContentFragment"-->
<!--android:layout_below="@id/fragment_title"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent" />-->
<FrameLayout
android:id="@+id/fragment_content"
android:layout_below="@id/fragment_title"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
修改MainActivity,使用FragmentManager 和 FragmentTransaction来执行Fragment操作:
public class MainActivity extends Activity implements View.OnClickListener {
private TextView mTxtTitle1;
private TextView mTxtTitle2;
private ContentFragment mContentFragment;
private ContentFragment2 mContentFragment2;
private FragmentManager mFragmentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTxtTitle1 = (TextView) findViewById(R.id.txt_title1);
mTxtTitle1.setOnClickListener(this);
mTxtTitle2 = (TextView) findViewById(R.id.txt_title2);
mTxtTitle2.setOnClickListener(this);
// 判断空为了防止屏幕翻转时重复添加Fragment
if (savedInstanceState == null) {
addContentFragment1();
}
}
private void addContentFragment1() {
if (mFragmentManager == null) {
mFragmentManager = getFragmentManager();
}
if (mContentFragment == null) {
mContentFragment = new ContentFragment();
}
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_content, mContentFragment);
fragmentTransaction.commit();
}
private void addContentFragment2() {
if (mFragmentManager == null) {
mFragmentManager = getFragmentManager();
}
if (mContentFragment2 == null) {
mContentFragment2 = new ContentFragment2();
}
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_content, mContentFragment2);
fragmentTransaction.commit();
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.txt_title1) {
addContentFragment1();
} else if (v.getId() == R.id.txt_title2) {
addContentFragment2();
}
}
}
这样修改后,通过点击TitleFragment中的标题1和标题2就能分别切换到ContentFragment和ContentFragment2了
Fragment常用类简介
android.app.Fragment //用于定义Fragment
android.app.FragmentManager // 用于在Activity中操作Fragment
android.app.FragmentTransaction // 用于以事务方式执行Fragment的各种操作
如果在Android3.0或以下中使用时,应该使用兼容包:
importandroid.support.v4.app.Fragment;
importandroid.support.v4.app.FragmentManager;
importandroid.support.v4.app.FragmentTransaction;
Fragment常用方法简介
FragmentManagerfragmentManager = getFragmentManager(); //获取FragmentManage对象,如果在v4中,可以使用getSupportFragmentManager
FragmentTransactionfragmentTransaction =mFragmentManager.beginTransaction(); //表示开启Fragment事务
fragmentTransaction.commit(); //表示提交Fragment事务
fragmentTransaction.add() //往Activity中添加一个Fragment,可多次执行便是添加多个Fragment
fragmentTransaction.remove() //从Activity中移除一个Fragmen
fragmentTransaction.replace() //本质上相当于调用了remove()方法,然后调用其add()方法
fragmentTransaction.hide() //隐藏Fragment,注意是隐藏,并不是销毁
fragmentTransaction.show() //显示隐藏的Fragment
fragmentTransaction.detach() //从UI中分离Fragment,和remove()不同,此时Fragment的状态依然由FragmentManager维护。或者说,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构。场景上,如果当前Activity一直存在,但不希望保留用户操作时,可以优先使用detach
fragmentTransaction.attach() //将分离的Fragment重新附加到UI上并显示
FragmentActivity
开头介绍时说到,Fragment是Android3.0后才引入的,所以在3.0之前是没有Fragment的,所以继承自Activity的FragmentActivity便是为解决3.0之前想使用Fragment而出现在android-support-v4.jar兼容包里的一个类。它的用法基本跟Fragment一样,目前已知不同的是在获取FragmentManage对象使用getSupportFragmentManager()方法,而不是getFragmentManager() 。