Fragment 翻译过来就是碎片的意思。fragment 和 Activity很像,都有自己的布局,都有自己对应的java类,都有一样的生命周期。
Fragment 应该理解为一个UI片段,是业务逻辑上的一个模块,比如我们有一个注册新用户的Activity,它包含了设置邮箱密码和填写个人资料两步,我们可以把这两部分别做成一个 fragment。在手机上 我们分成两步,要点击下一步才会填写个人资料,在平板上我们可以直接在一个屏幕中显示出来。
Fragment 存在的意义就是把UI按功能抽离出来封装成独立模块,然后在不同的activity中引用这些UI模块。所以如果应用设计合理,Activity中只是拼装fragment,而不会直接在activity中写按钮之类的组件,所有的UI实现都由Fragment来完成。这样可以做到在根据设备大小的不同显示不同的布局,甚至可以让用户自由定制自己的布局。
假设我们要做一个阅读应用,在手机上有两个界面,一个是文章列表,一个是文章内容。在手机上是点击文章列表跳转到文章内容。但是在平板上因为屏幕大,我们希望能左侧显示列表,右侧直接显示文章内容,而不需要切换。
那么这个时候我们需要用 Fragment 来实现。首先我们创建两个Fragment的XML文件,他们和Activity的写法没区别:
fragment_detail.xml:
fragment_list.xml:
然后我们创建对应的java类,这些JAVA类要继承自 android.app.Fragment
,并且重写 onCreateView
方法来指定对应的视图:
FragmentDetail.java:
public class FragmentDetail extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
FragmentList.java
public class FragmentList extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_list, container, false);
}
}
现在我们有了两个Fragment可以用,那么只需要在 activity_main.xml 中引用即可:
这是默认的布局,其中只用了 FragmentList ,也就是默认只是显示一个文章列表,点击之后才会显示文章详情。
然后我们再创建 res/layout-large/activity_main.xml
,在平板上左边显示列表,右边显示详情:
分别在手机上和平板上运行代码,可以看到如下图不同的布局,左边是手机 右边是平板:
上面的用法只是静态引用 Fragment,其实Fragment 真正强大的地方在于它可以动态操作。现在我们实现上面一个没完成的功能:在手机上点击列表跳转到 文章详情。
为了动态替换Fragment,我们首先需要给他指定一个容器,这里我们用一个 FrameLayout
作为容器。修改 activity_main.xml
如下:
然后我们在 fragment_list.xml
中增加一个按钮,然后在 MainActivity.java
中监听按钮的点击事件,并且用 FragmentManager
来动态替换掉当前的fragment:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.initialize();
}
public void initialize() {
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentDetail detail = new FragmentDetail();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.
beginTransaction();
transaction.addToBackStack(null); //入栈,以便可以通过点击后退按钮返回
transaction.replace(R.id.frame1, detail); //替换一下
transaction.commit();
}
});
}
@Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0 ){//当栈中还有fragment的时候,弹出。
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
上面代码中,通过 getFragmentManager()
获取一个 fragmentManager
实例,然后通过它提供的 replace 方法就可以动态替换掉一个 Fragment
。
Fragment 和 Activity 可以互相拿到对方的实例引用,因此可以互相通信。在Activity 中通过 getSupportFragmentManager().findFragmentById(R.id.article_fragment)
来拿到fragment引用。在 Fragment 中通过覆盖 public void onAttach(Activity activity)
方法就可以拿到 activity 引用,或者直接通过 getActivity()
方法获取。
官方不建议多个fragment之间直接通信,而是通过他们的 Activity 进行通信。也就是说不应该在一个 Fragment 中直接调用 另一个Fragment的方法。
在官方教程中,建议通过接口来实现通信。在 Fragment 中提供一个接口,然后在 Activity 中实现。通过这个接口进行通信,这样在 Fragment 中就不会和 Activity 有耦合。
接口的写法参见这里: http://developer.android.com/training/basics/fragments/communicating.html