△概述
→Fragment是从安卓的3.0开始有的,主要针对大屏幕的设备进行工作(例如平板)。
→Fragment中文翻译“片段,碎片”,"Fragment"确实是作为"Activity"的一个片段存在的,你可以将他理解为一个模块,或者"Activity"里的"Activity",如果将"Activity"当做是房子,"Fragment"就是里面一个房间。
→Fragment有他自己的生命周期,他有自己一个布局,能够接受和处理自己的输入事件,能在"Activity"运行时对"ragment"进行添加、删除等操作。
→Fragment必须嵌入到"Activity"里运行(就像房间必须放到房子里面),他的生命周期受到"Activity"周期影响(房间总受房子影响),比较典型的影响效果如:
(1)当“Activity"被pause了(即回调了"onPause()"方法),他里面的所有Fragment也被pause。
(2)当"Activity"被stop(即回调了"onStop()"方法),他里面的"Fragment"也全都会stop。
(3)当"Activity"被销毁了(即回调了"onDestroy()"方法),他里面的"Fragment"也全都会被销毁掉。
→虽然"Fragment"在一定程度上受到"Activity"影响,但是当"Activity"处于resume状态(即回调了“onResume()”方法),这个时候可以很自由地去对"Fragment"进行添加、移除、更替等等不同操作。
→一个"Activity"可以装填多个的"Fragment",同个"Fragment"的不同实例也能存在不同的"Activity"里面。
→刚才说了fragment有自己的布局文件,虽然如此,不过你也可以建立一个没有布局的fragment,此时,这个fragment只是为了完成某个逻辑功能,不需要和用户交互。
总的来说,"Fragment"就像一个更轻巧的"Activity",在你学习"Fragment"时,很多地方将他和"Activity"进行对比,将有助于更好理解以及使用"Fragment"。
下文,将分别讨论动态、静态方式来使用"Fragment",以及动静态方式下"Fragment"与"Activity"进行通信,先来看一下效果图:
最外层的就是一个"Activity"布局,里面放个"Fragment",另外我可以将背景颜色改了,就是为了能够明显看出"Fragment"到底加在到哪去了,我们将分别以静态和动态的方式完成这个例子。
△静态方式使用Fragment
主要包括如下步骤:
(1)创建一个类并继承系统"Fragment"类。
(2)定义一个布局文件,作为这个"Fragment"布局文件。
(3)在"Fragment"的"onCreateView()"方法里面,绘制并且返回你的布局文件。
(4)在"Activity"布局文件里,使用<fragment>标签添加你自己的"Fragment"。
→创建一个类并继承系统"Fragment"类:
public class TestFragment extends Fragment{
}
覆写生命周期方法的代码稍后再补上。→定义一个布局文件,作为"Fragment"布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TestFragment"
android:gravity="center"
android:textSize="24sp"/>
</LinearLayout>
非常简单一个布局文件,只有一个“TextView”,另外我将线性布局背景颜色改了,方便识别。→在"Fragment"的"onCreateView()"方法里面,绘制并且返回你的布局文件:
public class TestFragment extends Fragment{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_test,container,false);
}
}
相比第一步的代码,只是多了一个方法回调,其中"inflater"参数是"onCreateView()"方法里面传进来的,然后调用"inflate()"方法绘制布局文件,这个方法接受几个参数,这里分别解释一下:→在"Activity"布局文件里,使用<fragment>标签添加你自己的"Fragment":
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/ll_activity_main">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是MainActivity"
android:textSize="24sp"
android:gravity="center_horizontal"/>
<fragment
android:name="com.jf.fragmenttest.TestFragment"
android:id="@+id/first_Fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
里面只有两个控件,"TextView"是方便我们来识别"MainActivity",重点在于<fragment>标签,他里面的两个属性比较重要:△动态方式使用"Fragment"
(2)定义一个布局文件,作为这个"Fragment"布局文件。
(3)在"Fragment"的"onCreateView()"方法里面,绘制并且返回你的布局文件。
(4)在"Activity"里,使用"getFragmentManager()"方法获得"Fragment"的管理器,通过管理起来获取事务(FragmentTransaction对象),通过事务对"Fragment"做动态的一些操作。
虽然前面几步代码是一样的,不过我们还是一步一步来吧。→创建一个类并继承"Fragment":
public class TestFragment extends Fragment{
}
→定义一个布局文件,作为这个"Fragment"布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TestFragment"
android:gravity="center"
android:textSize="24sp"/>
</LinearLayout>
→在"Fragment"的"onCreateView()"方法里面,绘制并且返回你的布局文件:
public class TestFragment extends Fragment{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_test,container,false);
}
}
inflate()方法里面各个参数作用,已在前文作出解释,这里就不再重复了。→在"Activity"里,使用"getFragmentManager()"方法获得"Fragment"的管理器,通过管理起来获取事务(FragmentTransaction对象),通过事务对"Fragment"做动态的一些操作。
宿主"Activity"的名字是"MainActivity",我们先来看看其对应的布局文件里的代码,相比静态添加方式,有点变化,文件名字:activity_main,像这样子:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/ll_activity_main">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是MainActivity"
android:textSize="24sp"
android:gravity="center_horizontal"/>
</LinearLayout>
相比静态方式添加,这里去掉了<fragment>标签,因为我们使用的是动态加载,所以不再去添加<fragment>标签,注意我在线性布局哪里添加id属性,这个将在动态添加"Fragment"时会用到。接下来是"MainActivity"里的代码,像这样子:
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 通过"getFragmentManager()"方法获取"Fragment"管理器
FragmentManager fragmentManager = getFragmentManager();
// 通过"Fragment"管理器来获取事务对象
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 通过事务里的方法对"Fragment"进行动态的操作
transaction.add(R.id.ll_activity_main,new TestFragment());
// 操作完成之后记得提交,否则的话所有操作无效
transaction.commit();
}
}
可以看到,动态添加"Fragment",都是通过"Fragment"事务完成,其实不但动态添加,就算更替或者移除,都是通过事务来完成的,这里说下"fragmentTransaction.add()"方法里的两个参数:△Fragment与Activity传信息
Activity发消息到Fragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
<TextView
android:id="@+id/tv_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="显示来自Activity内容"
android:textSize="24sp"/>
</LinearLayout>
内容非常地简单,只有一个"TextView",用来显示从"Activity"那里接收到的内容。public class TestFragment extends Fragment{
private TextView messageTv;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 绘制对应布局文件
View rootView = inflater.inflate(R.layout.fragment_test,container,false);
// 获取布局文件里的"TextView"
messageTv = (TextView)rootView.findViewById(R.id.tv_message);
return rootView;//返回所绘制的布局文件对象
}
/*
定义一个公有方法,
用来接收来自"Activity"的数据
*/
public void sendToFragment(String message){
messageTv.setText(message);
}
}
注意下面那个方法,"sendToFragment()"这个方法被"Fragment"所在"Activity"所调用,接收来自"Activity"里的信息,收到之后就会调用textview.setText()方法显示所收到的数据。<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/ll_activity_main">
<!--EditText用来输入要发送的内容-->
<EditText
android:id="@+id/et_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="发给Fragment内容"/>
<!-- 发送按钮-->
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送"/>
<!-- 静态加载Fragment-->
<fragment
android:id="@+id/test_fragment"
android:name="com.jf.fragmenttest.TestFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这里一共三个控件,一个就是文本输入框了,还有一个就是发送按钮,另外一个就是我们的主角啦——静态添加的"Fragment"。public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用"findFragmentById()"获取静态加载的Fragment实例
final TestFragment testFragment = (TestFragment)getFragmentManager().findFragmentById(R.id.test_fragment);
// 获取EditText
final EditText contentEt = (EditText)findViewById(R.id.et_content);
// 获取按钮对象并设置监听器
findViewById(R.id.btn_send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取要发送的内容
String content = contentEt.getText().toString();
// 如果内容不为空则发送
if(!content.equals("")){
testFragment.sendToFragment(content);//这里就是调用共有方法发送消息
}
}
});
}
}
我们看到,一共做了几件事情:获取各个控件对象,包括"EditText"对象,"Button"对象,当然还有要通过"fragmentManager.findFragmentById()"方法获取"Fragment"实例对象。然后再为按钮设置监听事件,当按下时,判断一下文本框里有没数据,如果用户有输入,直接调用"TestFragment"所提供的公有方法,就能将信息发过去。这样就已经完成了"Activity"→"Fragment"发消息的一个过程,本质上就是"Activity"调用"Fragment"里的公有方法发送消息,然后再"Fragment"方法里面自己决定怎么处理。
对于动态加载的"Fragment",由于"Fragment"对象是直接new出来,所以不用"findFrgmentById()",只要在new那时候留住对象的引用即可,随时随地可以发送消息。
Fragment发消息到Avtivity

/**
* 定义一个回调接口,接口里面定义一个方法,
* 该接口由宿主"Activity"实现,并且实现这个方法
*/
public interface TestFragmentListener{
public void editTextContent(String message);
}
注意这段代码是在"TestFragment"类里面的,这段代码就表示了"Fragment" 及其对应的宿主"Activity"通信接口。public class MainActivity extends Activity implements TestFragment.TestFragmentListener{
//<span style="white-space:pre"> </span>实现接口里的方法
@Override
public void editTextContent(String message) {
textView.setText(message);//调用textView.setText()显示接收到的信息
}
}
可以看到,这和普通Java实现接口以及重写方法,基本没有什么区别。public class TestFragment extends Fragment{
//<span style="white-space:pre"> </span>定义一个成员变量,用来指向宿主"Activity"
private TestFragmentListener testFragmentListener;
//<span style="white-space:pre"> </span>在onAttach()方法里面获取并且转型"Activity"
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
testFragmentListener = (TestFragmentListener)activity;
}
}
这里需要注意的是记得进行类型转变,好了关键的代码看完了,现在看看完整代码。<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
<!--这是用户输入文本用的-->
<EditText
android:id="@+id/et_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入文本发送到Activity里"
android:textSize="24sp"
android:textColorHint="@android:color/white"/>
<!-- 发送按钮-->
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我发送"/>
</LinearLayout>
可以看到,就只放了两个控件,"EditText"用来接受用户输入,"Button"用来发送消息到"Activity"。
然后,"TestFragment"类的代码,像这样子:
public class TestFragment extends Fragment{
private EditText contentEt;//这是用户文本输入控件
private Button sendBtn;//这个指向发送按钮
private TestFragmentListener testFragmentListener;//这个是自定义接口
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 绘制对应布局文件
View rootView = inflater.inflate(R.layout.fragment_test,container,false);
// 找到相对应的控件
contentEt = (EditText)rootView.findViewById(R.id.et_content);
sendBtn = (Button)rootView.findViewById(R.id.btn_send);
// 设置按钮的监听器
sendBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取"EditText"里的内容
String content = contentEt.getText().toString();
// 如果内容不为空则发送消息
if (!content.equals("")) {
// 调用接口里的方法发送消息
testFragmentListener.editTextContent(content);
}
}
});
return rootView;
}
/**
* 定义一个回调接口,接口里面定义一个方法,
* 该接口由宿主"Activity"实现,并且实现这个方法
*/
public interface TestFragmentListener{
public void editTextContent(String message);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
testFragmentListener = (TestFragmentListener)activity;
}
}
这里主要逻辑如下:定义一个回调接口,接口里面定义通信方法,在onAttach()方法里面获取宿主"Activity",在点击了按钮之后调用接口里的方法,发送消息。
然后到了"Activity"布局文件,文件名字:activity_main,像这样子:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/ll_activity_main">
<!--用来显示Fragment发的内容-->
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="接收来自Fragment里的内容"
android:textSize="24sp"
android:gravity="center_horizontal"/>
<!-- 静态加载Fragment-->
<fragment
android:id="@+id/test_fragment"
android:name="com.jf.fragmenttest.TestFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这里只有两个控件,TextView用来显示Fragment发的内容,然后就是静态添加的"Fragment"。
接下来是宿主"Activity"里的内容,类名叫做:MainActivity,像这样子:
public class MainActivity extends Activity implements TestFragment.TestFragmentListener{
private TextView textView;//显示消息用的"TextView"
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.tv_content);
}
// 实现Fragment所定义的接口里的方法
@Override
public void editTextContent(String message) {
// 将"Fragment"发过来的消息显示在"TextView"里面
textView.setText(message);
}
}
这里就是实现接口以及实现接口里的方法,然后收到消息之后,就显示在自己的"TextView"里面。
到此,从"Fragment"发消息到"Activity"也介绍完。