Fragment是什么
从Fragment的设定上来看,它类似于Activity。但是,它的概念又比作为Android四大组件之一的Activity弱一些,它是一个可以嵌入Activity使用的UI片段,并且依附于Activity存在,生命周期也类似于Activity,和Activity存在许多联系。从本身的意思来看,它好像是把Activity碎片化了,例如,一个Activity上可以存在多个Fragment,就像报纸一个版面的各个板块一样,但也有报纸的一面只有一个板块的,又或者是类似于电子报纸,看似只有一个版面,但是所有Fragment都以层叠方式存在。
使用之初遇到的问题
当我们使用Fragment的时候,我们会发现,有两个Fragment供我们选择android.app.Fragment 和 android.support.v4.app.Fragment,这时我们就需要看清,这两个Fragment的区别。
android.app.Fragment
- 这是你当前版本Framework中的Fragment,如果你的当前SDK版本低于API11,那么这个Fragment你是无法使用的。
我们需要在编辑代码时导入android.app.Fragment
import android.app.Fragment;
一般通过getFragmentManager 来获取FragmentManager。
- 不可以在Activity的布局文件中添加<fragment>标签。
android.support.v4.app.Fragment
- 这是Android的兼容库中v4版本的Fragment,最低可以支持到API4(Android 1.6)。
我们需要在编辑代码时导入android.support.v4.app.Fragment
import android.support.v4.app.Fragment;
一般通过getSupportFragmentManager来获取FragmentManager,其中多出的Support就指Android的支持库。
- 可以在Activity的布局文件中添加<fragment>标签。但是有个前提,Fragment所在的Activity必须直接或间接继承FragmentActivity。
- 使用v4包中的Fragment所在的Activity必须直接或间接继承FragmentActivity,我们可以看到getSupportFragmentManager这个方法就是在FragmentActivity中定义的,如果不继承FragmentActivity,则无法使用这个方法。
生命周期
下面介绍的生命周期在V4和Fragment包里相同
- onAttach() 当fragment与activity绑定时使用,这是创建一个fragment所执行的第一个回调
- onCreate() 当进行Fragment的初始化动作时回调,它发生在onAttach后,onCreateView前
- onCreateView 当Fragment开始实例化它的UI界面,这个方法可能在无界面的Fragment初始化动作发生时返回空,表明此Fragment无界面。
- onActivityCreated 当Fragment绑定的Activity的onCreate方法调用后使用,按照我的理解,这个方法一般只在Activity的生命周期内调用一次,但是事实不是这样,正确的应该是这样,当Fragment所在的活动已经创建并且该Fragment的的视图层次被实例化时调用,所以当我们的Fragment被事务压入返回栈后,再次显示的时候,就会调用这个方法。
- onStart Fragment启动时回调,此时Fragment可见
- onResume 和onStart差不多,都是类似于Activity中的方法,启动时随Activity中的方法回调而回调。
- onPause 和Activity一致,Fragment可见,但是无法与用户交互,调用在Activity的onPause之前。
- onStop 和Activity一致,Fragment不可见,调用在Activity之前,伴随着Activity的停止。还有另外一种情况,当被其它Fragment replace时,如果被加入返回栈,那么将会同样调用这个方法。
- onDestroyView 对应于onCreateView,当Fragment移除它的UI界面的时候调用。
- onDestroy 和Activity一致,销毁当前的Fragment对象。
- onDettach 对应于onAttach方法,与Activity解除绑定。
这里有一些需要解释的地方,当Fragment被压入返回栈后,所涉及的生命周期只在onCreateView和onDestroyView之间。但是,只要是不显示的Fragment,即使它在返回栈中,在内存不足的情况下,它也是会被销毁的。
另外,在onCreate,onCreateView,onActivityCreated这三个方法的参数中,都是有Bundle savedInstanceState这个参数存在的,我们可以在onSaveInstanceState方法中保存当前的数据,再次启动时可以在这三个方法中使用savedInstanceState来恢复数据的。
正式使用
这里只介绍不使用<fragment>标签的Fragment;并且,例子使用v4包。
1. Fragment类
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//布局加载到fragment上时调用
View view = inflater.inflate(R.layout.my_fragment, container, false);
return view;
}
这里是Fragment中需要写的基础代码,我们需要将布局加载到Fragment上,需要注意的是,inflate的第一个参数是Fragment对应的布局文件名。
2. Activity类
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.activity_main, new MyFragment())
.addToBackStack(null)
.commit();
}
}
这里我们首先需要获取FragmentManager,这是基本的Fragment管理类,通过这个类的对象,我们可以对Fragment进行许多操作。
beginTransaction方法是开始一系列对于本FragmentManager相关联的动作。在一个事务中,如果添加多个Fragment,但是显示的时候,我们只能看到最上层的Fragment,即使是将这个事务放入了返回栈中。
add方法将Fragment加入到某个layout中.同样的方法还有replace,将新的Fragment替换到布局上去。
addToBackStack的作用是将当前事务放入返回栈中,当下次新建事务并且使用替换方法将同样位置的Fragment替换掉后,被替换掉的Fragment会已事务的形式暂存在返回栈中,并不会被销毁(内存不足时例外),当点击返回键的时候,将会把事务弹出堆栈并且反转其操作,这时就会反转替换动作,将原来的Fragment替换回来。从生命周期的角度来看,当前一个Fragment被replace后,这个Fragment将会依次回调onPause,onStop,onDedtroyView这三个方法。等到点击返回键时,这个Fragment将会依次回调onCreateView,onActivityCreated,onStart,onResume方法。
commit方法调用时,计划启动事务,但是并未启动。这时提交将会等待,它将会被安排在主线程中,等待线程就绪时启动。需要一提的是,同样一个事务只能被提交一次,多次提交一个事务将会报错。
与Activity通信
这里提供两种最基本的方法来进行通信
1. 在Fragment中获取Activity,调用其方法。
//这是Activity中的方法
void setTextView(String text){
mTextView.setText(text);
}
//在Fragment中调用Activity中的方法
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.setTextView("hello");
2. 在Activity中调用Fragment中的方法
//Fragment中的方法
void setTextView(String text){
mTextView.setText(text);
}
//在Activity中调用Fragment中的方法。
MyFragment myFragment = (MyFragment)getSupportFragmentManager()
.findFragmentById(R.id.my_fragment_layout);
myFragment.setTextView("hello");
如果想要在两个Fragment之间通信,那么将这两个步骤连接起来使用就好了。