Fragment的介绍
Fragment出现的初衷
Fragmen是在Android3.0(api11)的时候引入的,一开始的目的是为了大屏幕(如平板电脑)。
当时的Android手机普遍较少,在Android使用上面看起来的正常的界面,在平板电脑看起来可能会有点奇怪
比如,会留出比较大的空白的空间,所以,为了解决这种问题,谷歌在Android 3.0的时候推出了Fragment。
我们来看一个例子,新闻应用可以使用一个Fragment在左侧显示文章列表,
使用另一个Fragment在右侧显示文章 — 两个Fragment并排显示在一个 Activity 中,
每个Fragment都拥有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。
因此,用户无需使用一个 Activity 来选择文章,然后使用另一个 Activity 来阅读文章,
而是可以在同一个 Activity 内选择文章并进行阅读。详情如下图所示
Fragment的介绍
Fragment翻译成中文“片段”,它具有以下几个特点:
1、Activity中可以有多个Fragment。
2、Fragment有自己的生命周期,能够接收自己的输入事件。
3、Fragmen必须依赖于Activity,不能脱离Activity单独存在,事实上也脱离不了。
4、一个Fragment可以被多个Activity重复使用。
5、可以在Activity中动态的添加和删除Fragment,这一点我们在实际的开发中很经常使用到。
大家感兴趣的话可以看一下谷歌官网对Fragment的介绍
Fragment创建
它创建方式有两种:
1、自己创建一个类,并且继承自Fragment或者其子类(需要自己创建一个布局)
2、使用Android Studio子的Fragment模板创建,会自动帮我们生成一些代码,谷歌提供了几种模板,大家可以根据需求去选择
Fragment的静态添加
在创建完Fragment之后,如果你是使用的第一种方式创建的话,那么这是一个空的类,我们需要去实现一些方法,
将布局绑定到Fragment中。
如果你是一个新的Fragment,那么你的类的结构应该如下所示
public class TestFragment extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
然后我们需要在onCreateView()函数里面把对应的布局设置上去,我们新建一个fragment_test.xml文件,
fragment_test.xml文件如下:
需要注意的是,使用这种方式添加Fragment,一旦添加就不能在运行时删除.
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是测试" />
然后改造onCreateView()函数如下:
....
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
return LayoutInflater.from(getContext()).inflate(R.layout.fragment_test, container, false);
}
....
经过以上的处理,我们已经创建好了一个Fragment了,接着我们将这个Fragment添加到Activity里面,然后把它运行起来
修改activity_main.xml文件如下
<?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"
tools:context=".MainActivity">
<fragment
android:id="@+id/test_fragment"
android:name="com.example.fragment.TestFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
这边静态添加fragment时需要注意两点:
1、name字段填写的是要加载的fragment,指定要在布局中进行实例化的 `Fragment` 类
2、id 属性提供唯一 ID。必须加上去,否则会报错
需要注意的是,使用这种方式添加Fragment,一旦添加就不能在运行时删除。 并且唯一标识符,重启 Activity 时,系统可使用该标识符来恢复Fragment识符来捕获片段,从而执行某些事务,如将其移除)。可以通过两种方式为Fragment提供ID
- 为
android:id属性提供唯一 ID。 - 为
android:tag属性提供唯一字符串。
如果不为Fragment提供ID,那么程序会报错。
经过以上的处理,我们就可以把app运行起来了。如果运行起来没有崩溃,并且屏幕上已经显示“这是测试”文本,那么恭喜成功的走到了这一步。
Fragment的生命周期
我们再来看一下Fragment有哪些生命周期,具体的我们可以看下这张图
上图详细的介绍了Fragment中有哪些生命周期
接着我们重写一下这个生命周期的方法,并且把相应的日志打上,观察一下Fragment的生命周期,改造后代码如下:
public class TestFragment extends Fragment {
private static final String TAG = "TestFragment";
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: ");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: ");
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: ");
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated: ");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: ");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause: ");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach: ");
}
}
经过以上的处理,我们把App重新运行起来,观察一下生命周期。从app运行到app可见,Fragment的生命周期如下:
接着我们来看一下,从app前台按home键退出到app后台的生命周期,只走了一个onPause()函数
接着我们从app后台重新返回app前台,看一下具体的生命周期,可以看到一次走了onStart(),onResume()函数
接着我们再来看一下按返回键退出app的生命周期又是怎么样,可以看到最后是onDeatch()函数,
Fragment从activity分离的函数回调
以上基本介绍了常见的几种情况的生命周期的回调,接着我们来看一下,没个生命周期函数所对应的意思:
onAttach() 此Fragment已与Activity关联时调用(Activity传递到此方法内)
onCreate() 系统会在创建Fragment时调用此方法。当Fragment经历暂停或停止状态继而恢复后,如果您希望保留此Fragment的基本组件,则应在您的实现中将其初始化。
onCreateView() 系统会在Fragment首次绘制其界面时调用此方法。如要为您的Fragment绘制界面,您从此方法中返回的 View 必须是片段Fragment的根视图。如果片段未提供界面,您可以返回 null。
onActivityCreated() 当 Activity 的 onCreate() 方法已返回时进行调用。
onStart() 当Fragment 对用户可见时调用。这通常与Activity.onStart包含活动的生命周期有关
onResume() 当Fragment可见时调用
onPause() 系统会将此方法作为用户离开Fragment的第一个信号(但并不总是意味着此片段会被销毁)进行调用。 通常,您应在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
onStop() 当 Fragment 不再启动时调用。这通常与Activity.onStop包含活动的生命周期有关。
onDestroyView() 在移除与Fragment关联的视图层次结构时进行调用。
onDestroy() 在移除与Fragment关联的视图层次结构时进行调用。
onDetach() 在取消Fragment与 Activity 的关联时进行调用。
经过以上生命周期对应的每个函数的详细讲解,再结合上述各个场景生命周期的回调,相信大家已经很好的掌握了Fragment的生命周期了
Fragment的动态添加
动态添加Fragment大致分为一下几步
1、添加一个Fragment的容器,通常为FranmeLayout(只要是ViewGroup即可)
2、拿到FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
3、通过FragmentManager拿到FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
4、实例化需要添加的Fragment
TestFragment testFragment = new TestFragment();
5、第一个参数是 ViewGroup,即应放置片段的位置,由资源 ID 指定,第二个参数是要添加的片段。
fragmentTransaction.add(R.id.fl_container, testFragment);
6、提交,一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效。
fragmentTransaction.commit();
MainActivity完整代码如下
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1、拿到FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
//2、通过FragmentManager拿到FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//3、实例化需要添加的Fragment
TestFragment testFragment = new TestFragment();
//4、第一个参数是 ViewGroup,即应放置片段的位置,由资源 ID 指定,第二个参数是要添加的片段。
fragmentTransaction.add(R.id.fl_container, testFragment);
//5、提交,一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效。
fragmentTransaction.commit();
}
}
到这,你已经成功的学会了Fragment动态加载的方式了,接着我们来看下一些需要了解和注意的点。
在 Activity 中使用片段的一大优点是,您可以通过片段执行添加、移除、替换以及其他操作,从而响应用户交互。提交给 Activity 的每组更改均称为事务,并且您可使用 FragmentTransaction 中的 API 来执行一项事务。
在上面的代码中我们有拿到事务,接下来我们可以拿事务来做一些其它的操作
FragmentManager fragmentManager = getSupportFragmentManager();
//这句话的意思就是拿到Fragment的事务
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
这时候我们可以拿fragmentTransaction,您可以使用 add()、remove() 和 replace() 等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到 Activity,您必须调用 commit()。也就是说我们在重调用add()、remove()、replace()函数时,我们必须重新调用commity()才能生效。
接着我们来看一下fragmentTransaction这个类的常用的几个函数的意义
add() 将Fragment添加到Activity 如果多次调用add()没有调用remove()或者hide()函数的话则多个fragment会覆盖在一起。
hide() 隐藏添加的Fragment。
remove() 删除添加的Fragment。
replace() 往某个容器替换Fragment。
show() 显示以前隐藏的Fragment。
commit() 将事务提交。
attach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
detach() 重建view视图,附加到UI上并显示。
addToBackStack() 将事务添加到后台堆栈。这就意味着事务在提交之后将在堆栈里面,并切返回的时候将会从堆栈退出
更加详细的用法我们可以看一下官方文档 里面的讲解非常详细。 大家在实际的开发中可以根据自己需求选择适合自己的函数去使用。
Fragment的回退栈
Fragment可以通过添加返回栈可以达到Activity跳转并且可以返回上一个页面的效果,我们可以掉用下面这个函数为您的Fragment添加回退栈
fragmentTransaction.addToBackStack(null)
修改ainActivity代码如下
//1、拿到FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
//2、通过FragmentManager拿到FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//3、实例化需要添加的Fragment
TestFragment testFragment = new TestFragment();
//4、第一个参数是 ViewGroup,即应放置片段的位置,由资源 ID 指定,第二个参数是要添加的片段。
fragmentTransaction.add(R.id.fl_container, testFragment);
//5、为Fragment添加回退栈
fragmentTransaction.addToBackStack(null);
//6、提交,一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效。
fragmentTransaction.commit();
修改activity_main.xml代码如下
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:text="你好"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
修改fragment_test.xml代码如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#fff000"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="这是测试" />
</LinearLayout>
我们重新运行一下代码,可以看到如下图效果
然后我们再按一下返回键,可以看到如下图效果
很明显,现在的Fragment的回退栈的功能已经实现了,是不是很简单。
Fragment传递和接收数据
向Fragment传递数据一般是使用Bundle传递数据,再调用Fragment的setArguments()函数 具体代码如下,往TestFragment添加如下代码
public static void instance() {
TestFragment testFragment = new TestFragment();
Bundle bundle = new Bundle();
bundle.putString("test", "test");
testFragment.setArguments(bundle);
}
然后我们重写onViewCreate()函数去接收这个数据,具体代码如下:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle arguments = getArguments();
if (arguments != null) {
String result = arguments.getString("test");
Log.d(TAG, "onViewCreated: " + result);
}
}
getArguments()就是拿到我们刚才传递过来的Bundle,然后我们再通过bundle拿到刚才传递过来的数据即可。
注意:这边接收的数据类型和Key必须与传递过来的Key和数据类型完全一致才能成功的接收到数据