引入fragment的原因
- 假设开发一个这样的App,新增加一系列的项目,每个项目里有对应的明细内容。
- 如果仅仅使用Activity,想要看某一个项目的明细内容,只能新建一个Activity以显示明细。当然,这里面会涉及到项目列表的Activity的销毁,对应数据的保存,以及信息的传递。这样也可以实现,但是不具备灵活性,用户体验也不好
- frament就是在一个Activity中实现,多个界面布局的,这样使得UI更具备灵活性。
fragment的兼容性
- fragment是在API 11级时被引入的,也就是Android 3.0之后才被引入
- 如果需要在API 11级前引用fragment,则需要引用android.support.app.v4.*的类库
- 由于目前机器基本上是4.4以后的设备,所以实际开发过程中其实无需再考虑此兼容性,直接使用android.app.*类库的相关组件即可。
最简单的fragment创建例子
例子说明
- 创建一个界面,里面包含一个文本输入框
- 使用一个Activity,包含一个fragment
创建fragment的layout文件
此文件和之前Activity的没有区别,在一个LinearLayout中包含一个EidtText。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<EditText
android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/crime_title_hint" />
</LinearLayout>
创建一个fragement类
- 此类继承android.app.fragment类
- 与Activity类似,需要覆盖onCreate函数。但是此函数只是生成了实例,并未生成fragment视图。而Activity要使用到fragment,需要引用的是视图。
- 实现视图需要复写的onCreateView函数,函数的返回值是一个View视图对象。此函数有三个参数:android.view.LayoutInflater、android.view.ViewGroup、Bundle。前两个参数是生成布局的,Bundle参数是为了保持状态的。
- 视图的生成是通过,LayoutInflater的inflater函数实现的,有三个参数:第一个参数是第一步创建的布局文件;第二个参数是ViewGroup,表示视图的父视图;第三个参数如果为false,表示在activity中将以代码的方式添加视图。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
return v;
}
fragment中关联组件
- activity中关联组件是直接使用findViewById(R.id.xxxx)
mTrueButton = (Button)findViewById(R.id.true_button);
- fragment中不是直接使用findViewById函数,而是使用上一步创建的View对象,调用此View对象的findViewById函数来关联上对应的组件的。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence c, int start, int before, int count) {
mCrime.setTitle(c.toString());
}
public void beforeTextChanged(CharSequence c, int start, int count, int after) {
}
public void afterTextChanged(Editable c) {
}
});
return v;
}
将UI fragment添加到Activity中
Activity托管UI fragment的两种方式
- 添加fragment到activity布局中去。这种方式虽然简单,但是缺乏灵活性,基本不会使用此种方式。
- 在activity代码中添加fragment。这种方式是常用的方式
通过android.app.FragmentManager管理fragment
- 改造activity的layout布局文件。原来布局文件里是具体的组件,而现在只用包含一个FrameLayout用于容纳fragment。
注意需要设定id值,以保证代码可以引用到。
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 通过getFragmentManager获取FragmentManager的实例;通过findFragmentById关联上布局文件的FrameLayout
FragmentManager fm = getFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
- 通过FragmentTransaction类进行fragment的添加、移除、附加、分离或替换等动作。FragmentTransaction对象,可以通过FragmentManager.beginTransaction()函数获得。
- activity的整体代码如下:
public class CrimeActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
FragmentManager fm = getFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction().add(R.id.fragmentContainer,
}
}
}
FragmentManager与fragment的生命周期
各种场景下的调用说明
- 向FragmentManager添加fragment:onAttach(Activity)、onCreate(Bundle)、 onCreateView方法会被调用
- 启动Activity:activity的onCreate(Bundle)、onActivityCreate()方法会被调用,随后调用" 向FragmentManager添加fragment"。当fragment被添加后,onActivityCreate方法停止
- 向处于运行状态的activity中添加fragment: 以下fragment生命周期方法会被依次 调用: onAttach( Activity)、 onCreate( Bundle)、 onCreateView(...)、 onActivityCreate(Bundle)、onStart(),以及 onResume() 方法。
FragmentManager管理生命周期的特点
- Activity的状态android系统是知道其处于什么样的状态的,而fragment则不会被android系统知道。只能由FragmentManager进行管理。
- FragmentManager会保证fragment会和activity的状态保持一致
- 当Activity被销毁时,FragmentManager会将fragment的队列保存下来。当Activity重建时,会先获取保存的fragment队列,然后重建fragment队列,从而恢复到原来的状态。
典型的表现是,当我们在上面例子中的EditText中输入文字后,旋转手机界面,会发现在横屏状态下仍旧保存了竖屏时输入的文字信息。