Fragment英文原意是碎片,片段的意思,为了应对android的碎片化,Fragmnet的概念在Android3.0中被引入进来,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕比手机大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互。
Fragment在应用中应当是一个模块化和可重用的组件,因为Fragment定义了自己的布局,以及通过使用它自己的生命周期回调方法定义了它自己的行为,你可以将Fragment包含到多个Activity中,一个Activiy也可以包含多个Fragment。
下面这张图可以帮助理解Fragment的概念:
- Fragment可以作为Activity界面的一部分组成出现
- 可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用
- 在Activity运行过程中,可以添加,移除或替换Fragment
- Fragment可以响应自己的输入时间,并且有自己的生命周期,其生命周期受宿主Activity的生命周期影响
静态创建
静态创建需要在在布局文件中声明Fragment,需要特别注意的是Fragment中的android:name属性指定了在layout中实例化的Fragment类 ,同时必须要标识Fragment。
标识Fragment的方法:
android:id 属性提供一个唯一的ID
android:tag 属性提供一个唯一的字符串
创建方法:
- 在布局文件中声明一个fragment组件,name指向3中创建的类对象
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.w2.test.MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="button1"
android:text="Button1"
android:textSize="18sp"
/>
<!--声明一个fragment-->
<fragment
android:id="@+id/fragment"
android:name="com.w2.test.MyFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
2.创建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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一个静态加载的Fragment"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:textSize="20sp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change"
android:id="@+id/button"
android:layout_gravity="center_horizontal" />
</LinearLayout>
3.创建MyFragment类继承Fragment对象,重写onCreateView方法返回布局文件的View
public class MyFragment extends Fragment{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/**
* resource:Fragment需要加载的布局文件
* root:加载layout的父ViewGroup
* attactToRoot:false,不返回父ViewGroup
*/
View v = inflater.inflate(R.layout.fragment,container,false);
View view = inflater.inflate(R.layout.fragment, container, false);
final TextView text=(TextView) v.findViewById(R.id.textView);
Button button=(Button) v.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
text.setText("测试成功");
}
});
return v;
}
}
动态创建
在Java代码中撰写代码将Fragment添加到Activity layout中。
add():添加一个Fragment,指定要添加的Fragment和插入的View
与此类似的还有remove(),repalce()等
诸如上述对Fragment进行添加,移除,替换以及执行其他动作,提交给Activity的每一套变化被称为一个事物,处理这类事物需要Fragment Manager,通过FragmentManager开启事务。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
每一个事务都是同时执行一套变化,可以在一个事务中设置所要执行的操作,然后提交给Activity,之后必须要调用commit方法。
如果允许用户通过按下BACK按键返回到前一个Fragment状态,调用commit之前可以加入addToBackStack()方法。
FragmentManager fragmentManager = getFragmentManager(); //获取FragmentManager
FragmentTransaction beginTransaction = fragmentManager.beginTransaction(); //开启事务
beginTransaction.add(R.id.frame, fragment2); //添加Fragment,参数1containerViewId传入Fragment的父布局,参数2fragment穿入Fragment的实例化对象
beginTransaction.addToBackStack(null); //增加回退功能,回到上一个事务状态
beginTransaction.commit(); //提交事务
Fragment的生命周期
-
Fragment生命周期图
- 与Activity生命周期对比
每个方法的回调条件:
-
onCreateView()
每次创建都会绘制Fragment的View组件时回调该方法
Fragment第一次绘制它的用户界面的时候,系统会调用此方法,为了绘制Fragment的UI,此方法必须返回一个View,如果不显示UI,返回null即可。 -
onAttach
当Fragment被添加到Activity时候会回调这个方法,并且只调用一次 -
onCreate
创建Fragment时会回调,只会调用一次 -
onActivityCreated
当Fragment所在的Activty启动完成后调用 -
onStart
启动Fragment -
onResume
恢复Fragment时会被回调,调用onStart()方法后面一定会调用onResume()方法 -
onPause
暂停Fragment -
onStop
停止Fragment -
onDestroyView
销毁Fragment所包含的View组件时 -
onDestroy
销毁Fragment时会被回调 - onDetach
Fragment从Activity中删除时会回调该方法,并且这个方法只会调用一次
在Activity中创建Bundle数据包,并调用Fragment的setArgument(Bundle bundle)方法。这种方式是在加载Fragment之前传递数据的。
Activity:
//动态加载Fragment的同时传递数据
String text = editText.getText().toString(); //获取要传递的数据
Bundle bundle = new Bundle();
if (text!=null){
bundle.putString("msg",text);
}
MyFragment fragment = new MyFragment();
fragment.setArguments(bundle);
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.frame,fragment,"MyFragment");
transaction.addToBackStack(null);
transaction.commit();
Fragment:
//获取数据并令TextView显示
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment,container,false);
TextView text=(TextView) v.findViewById(R.id.textView);
text.setText(getArguments().get("msg").toString());
return v;
}
注意,这里取得传递过来的数据用的是@getArgument方法,@setArgument中传入的bundle只是一个数据类型,与savedInstanceState这个参数没有关系。我之前用savedInstanceState.getString()获取数据一直错误,很久都找不到错误。
Fragment向Activity传递数据需要在Fragment中定义回调接口,再让包含该Fragment的Activity实现该回调接口。这样Fragment可以调用该回调方法传数据给Activity。
- 在Fragment中定义接口,并在接口中定义传递数据的抽象方法
public interface MyListener{
public void send(String code);
}
- 令Activity实现定义的接口,并重写定义的方法
public class MainActivity extends AppCompatActivity implements MyFragment.MyListener
...
@Override
public void send(String code) {
}
- 在Fragment中将创建接口对象并指向Activity,最后传入String
MyListener listener;
@Override
public void onAttach(Activity activity) { //不一定非要在onAttach中
listener = (MyListener) activity;
super.onAttach(activity);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment,container,false);
TextView text=(TextView) v.findViewById(R.id.textView);
text.setText(getArguments().get("msg").toString());
listener.send("这是发送给Activity的数据");//也不一定非在onCreateView中
return v;
}
- 在Activity中接收数据
@Override
public void send(String code) {
editText.setText(code);
}
Activity向静态加载Fragment传递数据
-
首先按静态加载方式创建Fragment
- 在Fragment的创建数据用来接收传来的数据,设置get和set方法
public String a;
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
- 在Activity通过Fragment.findFragmentById找到Fragment,通过set方法传递数据
FragmentManager fragmentManager = getFragmentManager();
MyFragment fragment = (MyFragment) fragmentManager.findFragmentById(R.id.fragment);
fragment.setA(text);
- 最后在Fragment中通过get方法接收数据