一. 简介

1. Fragment是什么,有什么用?

1
2
java.lang.Object
↳ android.app.Fragment

Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!

2. Fragment优点

  • Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI。
  • Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸。从手机到平板电脑。
  • Fragment是一个独立的模块,紧紧地与activity绑定在一起。可以运行中动态地移除、加入、交换等。
  • Fragment提供一个新的方式让你在不同的安卓设备上统一你的UI。
  • Fragment 解决Activity间的切换不流畅,轻量切换。
  • Fragment 替代TabActivity做导航,性能更好。
  • Fragment 在4.2.版本中新增嵌套fragmeng使用方法,能够生成更好的界面效果。
  • Fragment做局部内容更新更方便,原来为了到达这一点要把多个布局放到一个activity里面,现在可以用多Fragment来代替,只有在需要的时候才加载Fragment,提高性能

二. Fragment 的生命周期

1. onAttach

onAttach 在fragment与Activity关联之后调用。需要注意的是,初始化fragment参数可以从getArguments()获得,但是,当Fragment附加到Activity之后,就无法再调用setArguments()。所以除了在最开始时,其它时间都无法向初始化参数添加内容。

2. onCreate

onCreate 在fragment初次创建时调用。尽管它看起来像是Activity的OnCreate()函数,但这个只是用来创建Fragment的。此时的Activity还没有创建完成,因为我们的Fragment也是Activity创建的一部分。所以如果你想在这里使用Activity中的一些资源,将会获取不到。比如:获取同一个Activity中其它Frament的控件实例。(代码如下:),如果想要获得Activity相关联的资源,必须在onActivityCreated中获取。

3. onCreateView

当这个fragment构造它的用户接口视图(即布局)时调用 onCreateView。

4. onActivityCreated

在Activity的 OnCreate() 结束后,会调用此方法。所以到这里的时候,Activity已经创建完成!在这个函数中才可以使用Activity的所有资源。

5. onStart

当到 OnStart 时,Fragment对用户就是可见的了。但用户还未开始与Fragment交互。在生命周期中也可以看到Fragment的OnStart()过程与Activity的OnStart()过程是绑定的。意义即是一样的。以前你写在Activity的OnStart()中来处理的代码,用Fragment来实现时,依然可以放在OnStart()中来处理。

6. onResume

当这个fragment对用户可见并且正在运行时调用。这是Fragment与用户交互之前的最后一个回调。从生命周期对比中,可以看到,Fragment的OnResume与Activity的OnResume是相互绑定的,意义是一样的。它依赖于包含它的activity的Activity.onResume。当OnResume()结束后,就可以正式与用户交互了。

7. onPause

此回调与Activity的OnPause()相绑定,与Activity的OnPause()意义一样。

8. onStop

这个回调与Activity的OnStop()相绑定,意义一样。已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。

9. onDestroyView

如果Fragment即将被结束或保存,那么撤销方向上的下一个回调将是onDestoryView()。会将在onCreateView创建的视图与这个fragment分离。下次这个fragment若要显示,那么将会创建新视图。这会在onStop之后和onDestroy之前调用。这个方法的调用同onCreateView是否返回非null视图无关。它会潜在的在这个视图状态被保存之后以及它被它的父视图回收之前调用。

10. onDestroy

当这个fragment不再使用时调用。需要注意的是,它即使经过了onDestroy()阶段,但仍然能从Activity中找到,因为它还没有Detach。

11. onDetach

Fragment生命周期中最后一个回调是onDetach()。调用它以后,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放。

三. 创建 Fragment

1. 静态加载 Fragment

① 定义Fragment的Xml布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!--fragment_one.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00CC66">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FragmentOne"
android:textSize="30sp"
android:textStyle="bold"
android:layout_centerInParent="true"
/>
</RelativeLayout>

<!--fragment_two.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9966">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FragmentTwo"
android:textSize="30sp"
android:textStyle="bold"
android:layout_centerInParent="true"
/>
</RelativeLayout>

② 自定义类继承于Fragment,并加载布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* FragmentOne
*/
public class FragmentOne extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one,container,false);
}
}

/**
* FragmentTwo
*/
public class FragmentTwo extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.fragment_two, container, false);
}
}

③ 在要使用的Activity的布局文件中添加fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">

<fragment
android:id="@+id/fragment_one"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="swu.xl.FragmentOne"
/>

<fragment
android:id="@+id/fragment_two"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="swu.xl.FragmentTwo"
/>
</LinearLayout>

④ 运行效果

2. 动态加载 fragment

① 定义Fragment的Xml布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!--fragment_one.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00CC66">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FragmentOne"
android:textSize="30sp"
android:textStyle="bold"
android:layout_centerInParent="true"
/>
</RelativeLayout>

<!--fragment_two.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF9966">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FragmentTwo"
android:textSize="30sp"
android:textStyle="bold"
android:layout_centerInParent="true"
/>
</RelativeLayout>

② 自定义类继承于Fragment,并加载布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* FragmentOne
*/
public class FragmentOne extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one,container,false);
}
}

/**
* FragmentTwo
*/
public class FragmentTwo extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return inflater.inflate(R.layout.fragment_two, container, false);
}
}

③ 在想要添加fragment的Activity的布局文件中设置一个容器存放fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:layout_centerInParent="true"
>

<Button
android:id="@+id/show_fragment_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示FragmentOne"
android:textAllCaps="false"
/>

<Button
android:id="@+id/show_fragment_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示FragmentTwo"
android:textAllCaps="false"
/>

<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="300dp"
/>

</LinearLayout>

</RelativeLayout>

④ 获取FragmentManager对象的transaction对象调用add加入fragment,并commit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

findViewById(R.id.show_fragment_one).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
FragmentOne fragmentOne = new FragmentOne();
transaction.add(R.id.fragment_container,fragmentOne);
transaction.commit();
}
});

findViewById(R.id.show_fragment_two).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
FragmentTwo fragmentTwo = new FragmentTwo();
transaction.add(R.id.fragment_container,fragmentTwo);
transaction.commit();
}
});
}
}

⑤ 运行效果

⑥ 相关方法讲解

1
2
3
4
5
6
//androidx.fragment.app.FragmentActivity类里面的
//由于AppCompatActivity类继承于FragmentActivity类,所以AppCompatActivity类里面也有
//还有就是android.app.Activity中的getFragmentManager()在FragmentActivity是弃用的
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
1
2
//FragmentManager类里面的方法用于开启一个事务
public abstract FragmentTransaction beginTransaction();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//FragmentTransaction类

//添加一个Fragment,不设置Tag
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}

//添加一个Fragment,设置Tag
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}

//类似于先remove掉视图容器所有的Fragment,再add一个想要添加的Fragment,不设置Tag值
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment) {
return replace(containerViewId, fragment, null);
}

//类似于先remove掉视图容器所有的Fragment,再add一个想要添加的Fragment,并设置Tag值
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
@Nullable String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}

//手动移除一个fragment
public FragmentTransaction remove(@NonNull Fragment fragment) {
addOp(new Op(OP_REMOVE, fragment));

return this;
}

详细内容参考:FragmentTransaction详解

四. Fragment管理与Fragment事务

1. Fragment管理

① Activity中有个FragmentManager,其内部维护fragment队列,以及fragment事务的回退栈。在Fragment被创建、并由FragmentManager管理时,FragmentManager就把它放入自己维护的fragment队列中。

② Activity管理Fragment主要依靠

  • FragmentManager可以调用findFragmentById()获取指定的fragment。
  • FragmentManager可以调用popBackStack()方法弹出后台Fragment。
  • FragmentManager可以调用addToBackStack(null)加入栈。
  • FragmentManager可以监听后台栈的变化addOnBackStackChangeListener。

2. Fragment事务

① FragmentManager不是直接去绑定Fragment然后把它set进自己的队列中,而是借助FragmentTransaction(Fragment事务)。

② FragmentManager调用beginTransaction()方法返回一个新建的事务,用于记录对于Fragment的add、replace等操作,最终将事务commit回FragmentManager,才开始启动执行事务的内容,实现真正的Fragment显示。

③ Fragment事务回滚步骤

  • 在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中。
  • 在需要回退时,使用popBackStack()将最上层的操作弹出回退栈。这里的popBackStack()是弹出默认的最上层的栈顶内容。当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//添加到回退栈
transaction.addToBackStack(String tag);

//弹出回退栈的最上层
manager.popBackStack();

//按照id弹出回退栈的指定层
//参数int id是当提交变更时transaction.commit()的返回值。
void popBackStack(int id, int flags);

//按照tag弹出回退栈的指定层
//参数string name是transaction.addToBackStack(String tag)中的tag值;
void popBackStack(String name, int flags);

//int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
//取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层;
//取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈;

五. Fragment 与 Activity 组件互取

1. Fragment 中获取 Activity 中的组件

1
getActivity().findViewById(R.id.XXX);

2. Activity 中获取 Fragment 中的组件

1
2
3
4
5
//根据fragment的id寻找
getFragmentManager.findFragmentByid(R.id.XXX);

//根据fragment的tag寻找
getFragmentManager.findFragmentByTag(String tag);

六. Fragment 与 Activity数据传递

1. Activity传递数据给Fragment

  • Activity中创建Bundle数据包,调用Fragment实例的setArguments(bundle) 从而将Bundle数据包传给Fragment。
  • Fragment中调用getArguments获得 Bundle对象,然后进行解析,建议在onCreateView方法中找到控件,onActivityCreated方法中解析Bundle。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<fragment
android:id="@+id/fragment"
android:name="swu.xl.activity_to_fragment.MyFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />

</RelativeLayout>

//MainActivity
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到fragment
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.fragment);

//设置数据
Bundle bundle = new Bundle();
bundle.putString("text","Activity->Fragment");
fragment.setArguments(bundle);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3399CC">

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="25sp"
android:layout_centerInParent="true"
/>

</RelativeLayout>

//MyFragment
public class MyFragment extends Fragment {

TextView textView;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//找到布局以及控件
View inflate = inflater.inflate(R.layout.fragment, container, false);
textView = inflate.findViewById(R.id.text);

return inflate;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

//解析数据
Bundle arguments = getArguments();
if (arguments != null){
String text = (String) arguments.get("text");

//设置数据
textView.setText(text);
}else {
Toast.makeText(getActivity(), "数据未传达", Toast.LENGTH_SHORT).show();
}
}
}

2. Fragment传递数据给Activity

  • 在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口。
  • Fragment就可以通过回调接口传数据了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//active_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<fragment
android:id="@+id/fragment"
android:name="swu.xl.fragment_to_activity.MyFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>

//activity
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到fragment
FragmentManager manager = getSupportFragmentManager();
MyFragment fragment = (MyFragment) manager.findFragmentById(R.id.fragment);

//接受参数
fragment.getData(new MyFragment.CallBack() {
@Override
public void getResult(String result) {
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3399CC">

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="25sp"
android:layout_centerInParent="true"
android:textAlignment="center"
android:layout_marginTop="100dp"
/>

</RelativeLayout>

//MyFragment
public class MyFragment extends Fragment {

TextView textView;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//找到布局以及控件
View inflate = inflater.inflate(R.layout.fragment, container, false);
textView = inflate.findViewById(R.id.text);
textView.setText("传递信息");

return inflate;
}

//回调数据
public void getData(CallBack callBack){
String s = textView.getText().toString();
callBack.getResult(s);
}

//接口
public interface CallBack{
void getResult(String result);
}
}

3. Fragment之间互相传递数据

1. fragment1跳转到fragment2

初始化要跳转的Fragment类 ——fragment2,调用其setArguments方法传入数据即可!

2. fragment1和fragment2即时传递

两个Fragment需要即时传数据,而非跳转的话,就需要先在Activity获得fragment1传过来的数据, 再传到fragment2了,就是以Activity为媒介

参考文章

Android Fragment 生命周期以及基本用法(一)

Android Fragment两种加载方式及ViewPage(二)

Android Fragment(三)传递数据的三种方法

Fragment 使用详解