前言

  • 本文是根据上海应用技术大学余艳芳老师的移动应用课程写的上课笔记。
  • 部分素材和代码没有给出(例如drawable中的文件代码),请读者自己补全或者删除,或者可以到本人github下载完整素材和代码。
  • 完整的项目在本人的GitHub仓库中,以下是相关链接:https://github.com/ufovsmba/TodayApplication

Fragment基础知识

Fragment碎片

  • fragment必须依赖Activity存在,必须嵌入到Activity当中,不能独立存在。

  • Android从3.0开始引入Fragment(碎片)

  • 允许将Activity拆分成多个完全独立封装的可重用的组件
  • 每个组件拥有自己的生命周期和UI布局
  • 为不同型号、尺寸、分辨率的设备提供统一的UI设计方案

image-20220404111008148

image-20220404111013831

  • 自定义的Fragment必须继承Fragment类或其子类

image-20220404111037940

创建Fragment

  • 通常在创建Fragment时,需要实现方法:
    • onCreateView()
  • 将Fragment加载到Activity中主要有两种方式:
    • 把Fragment添加到Activity的布局文件中
    • 在Activity的代码中动态添加Fragment

管理Fragment

  • 通过FragmentManager实现Fragment对象的管理
  • 通过getFragmentManager()获取FragmentManager对象
  • FragmentManager能够完成以下三方面的操作:

    • 通过findFragmentById()或findFragmentByTag()方法,来获取Activity中已存在的Fragment对象
    • 通过popBackStack()方法将Fragment从Activity的后退栈中弹出
    • 通过addOnBackStackChangedListerner()方法来注册一个侦听器以监视后退栈的变化
  • 获取FragmentTransaction对象

1
2
FragmentManager fragmentManager=getFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();

ps:FragmentTransaction被称作Fragment事务,与数据库事务类似,Fragment事务代表了Activity对Fragment执行的多个改变操作。

  • 使用FragmentTransaction
1
2
3
4
5
6
7
8
9
10
//创建一个新的Fragment对象
Fragment newFragment=new ExampleFragment();
//通过FragmentManager获取Fragment事务对象
FragmentTransaction transaction=getFragmentManager().beginTransaction();
//通过replace()方法把fragment_container替换成新的Fragment对象
transaction.replace(R.id.fragment_container,newFragment);
//添加到回退栈
transaction.addToBackStack(null);
//提交事务
transaction.commit();

事务中动作的执行顺序可以随意,但需注意以下两点:

  • 程序的最后必须调用commit()方法
  • 程序中添加了多个Fragment对象,显示的顺序跟添加顺序一致
  • 当删除Fragment对象时,在没有调用addToBackStack()方法情况下,Fragment对象会被销毁

ps:调用commit()后,事务并不会马上提交,而是会在Activity的UI线程中等待直到线程能执行的时候才执行。

与Activity通讯

  • Fragment获取其所在的Activity中的组件
1
View listView=getActivity().findViewById(R.id.list);
  • Activity获取指定Fragment实例
1
2
ExampleFragment fragment = (ExampleFragment)getFragmentManager()
.findFragmentById(R.id.example_fragment)
  • 在Fragment中定义回调接口
1
2
3
4
5
6
7
8
9
public static class FragmentA extends ListFragment { 
......省略
//Activity必须实现下面的接口
public interface OnNewsSelectedListener{
//传递当前被选中的标题的id
public void onNewsSelected(long id);
}
......省略
}
  • 使用onAttach()方法检查Activity是否实现回调接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class FragmentA extends ListFragment { 
OnNewsSelectedListener mListener;
......省略
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
mListener =(OnNewsSelectedListener)activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+"必须继承接口 OnNewsSelectedListener");
}
}
......省略
}

Fragment的生命周期

Fragment的生命周期状态

  • Fragment的生命周期具有以下四个状态:
    • 活动状态: onResume()
    • 暂停状态: onPause()
    • 停止状态: onStop()
    • 销毁状态: onDestroyView() || onDestroy()

img

image-20220404113812287

ps: onActivityCreated()方法已经过期

方法 功能描述
onAttach() 当一个Fragment对象关联到一个Activity时被调用
onCreate() 初始化创建Fragment对象时被调用
onCreateView() 当Activity获得Fragment的布局时调用此方法
onActivityCreated() 当Activity对象完成自己的onCreate()方法时调用 (该方法已经过期)
onStart() Fragment对象在UI界面可见时调用
onResume() Fragment对象的UI可以与用户交互时调用
onPause() 由Activity对象转为onPause状态时调用
onStop() 有组件完全遮挡,或者宿主Activity对象转为onStop状态时调用
onDestroyView() Fragment对象清理View资源时调用,即移除Fragment中的视图
onDestroy() Fragment对象完成对象清理View资源时调用
onDetach() 当Fragment被从Activity中删掉时被调用

Fragment和Activity两者之间生命周期的关系

  • Activity直接影响其所包含的Fragment的生命周期

  • Fragment的回调方法要比Activity多,多出的方法主要用于与Activity的交互

  • 当Activity进入运行状态时(即running状态),才允许添加或删除Fragment

  • 有当Activity处于resumed状态时,Fragment的生命周期才能独立运转

  • 其他阶段依赖于Activity的生命周期

image-20220404114229962

静态方式

img

  • Fragment的生命周期调用过程

    • 1当首次展示布局页面时,其生命周期方法调用的顺序是:

      • onAttach()

      • onCreate()

      • onCreateView()

      • onStart()

      • onResume()

    • 当关闭手机屏幕或者手机屏幕变暗时,其生命周期方法调用顺序是:

      • onPause()
    • onStop()
  • 使用时方法,在Activity布局文件中写入fragment,属性中加入android:name,值写关联的Fragment类

1
2
3
4
5
6
<fragment
android:name="com.gallifrey.todayapplication.fragment.MessageFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/fl_container"/>

动态方式

img

  • 通过重写Fragment生命周期的方法
  • 在Activity代码中动态使用Fragment

测试样例代码

image-20220404183956838

image-20220404191200920

image-20220404191323103

  • FragmentActivity.java
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.gallifrey.todayapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;

import android.widget.RadioGroup;

import com.gallifrey.todayapplication.fragment.DiscoverFragment;
import com.gallifrey.todayapplication.fragment.MeFragment;
import com.gallifrey.todayapplication.fragment.MessageFragment;
import com.gallifrey.todayapplication.fragment.PhoneFragment;

import java.util.ArrayList;
import java.util.List;

public class FragmentActivity extends AppCompatActivity implements PhoneFragment.IPhoneCallBack{
private FragmentManager fragmentManager;
private FragmentTransaction fragmentTransaction;
private PhoneFragment phoneFragment;
private MessageFragment messageFragment;

private MeFragment meFragment;
private DiscoverFragment discoverFragment;
private RadioGroup mRg;
private List<Fragment> fragmentList=new ArrayList<>();


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

mRg=findViewById(R.id.fl_rg);

fragmentManager=getSupportFragmentManager();

messageFragment =new MessageFragment();
fragmentList.add(messageFragment);

hideOtherFragment(messageFragment,true);
mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
switch (i){
case R.id.fl_rb_message:
hideOtherFragment(messageFragment,false);
break;
case R.id.fl_rb_phone:
if(phoneFragment==null){
phoneFragment=PhoneFragment.newInstance("phone 12315");
fragmentList.add(phoneFragment);
hideOtherFragment(phoneFragment,true);
}else {
hideOtherFragment(phoneFragment,false);
}
break;
case R.id.fl_rb_me:
if(meFragment==null){
meFragment=new MeFragment();
fragmentList.add(meFragment);
hideOtherFragment(meFragment,true);
}else {
hideOtherFragment(meFragment,false);
}
break;
case R.id.fl_rb_discover:
if(phoneFragment==null){
discoverFragment=new DiscoverFragment();
fragmentList.add(discoverFragment);
hideOtherFragment(discoverFragment,true);
}else {
hideOtherFragment(discoverFragment,false);
}
break;
}
}
});

}
//隐藏其他fragment
private void hideOtherFragment(Fragment currentFragment,boolean b){
fragmentTransaction=fragmentManager.beginTransaction();//创建事务
if(b) {
fragmentTransaction.add(R.id.fl_container, currentFragment);//如果从未加入,则将当前Fragment添加到activity
}

for(Fragment tempFragment:fragmentList){
if(tempFragment.equals(currentFragment)){
fragmentTransaction.show(tempFragment);
}else {
fragmentTransaction.hide(tempFragment);
}
}
fragmentTransaction.commitAllowingStateLoss();
}

@Override
public void setData(String str) {
setTitle(str);
}
}
  • activity_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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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=".FragmentActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/fl_container"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#E4E3E3"/>
<RadioGroup
android:checkedButton="@id/fl_rb_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/fl_rg">
<RadioButton
style="@style/flRadioButton"
android:paddingVertical="5dp"
android:id="@+id/fl_rb_message"
android:text="消息"
android:drawableTop="@drawable/fl_rb_message_bg"
/>
<RadioButton
style="@style/flRadioButton"
android:paddingVertical="5dp"
android:id="@+id/fl_rb_phone"
android:text="通信录"
android:drawableTop="@drawable/fl_rb_phone_bg"
/>
<RadioButton
style="@style/flRadioButton"
android:paddingVertical="5dp"
android:id="@+id/fl_rb_discover"
android:text="发现"
android:drawableTop="@drawable/fl_rb_discover_bg"
/>
<RadioButton
style="@style/flRadioButton"
android:paddingVertical="5dp"
android:id="@+id/fl_rb_me"
android:text="我"
android:drawableTop="@drawable/fl_rb_me_bg"
/>
</RadioGroup>
</LinearLayout>
  • PhoneFragment.java
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.gallifrey.todayapplication.fragment;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.gallifrey.todayapplication.R;


public class PhoneFragment extends Fragment {
private static final String ARG_PARAM="param1";
private static final String TAG="PhoneFragment";
private String param;
private IPhoneCallBack iPhoneCallBack;
public PhoneFragment(){
Log.i(TAG, "PhoneFragment: ");
}
public static PhoneFragment newInstance(String param1){
Log.i(TAG, "newInstance: into");
PhoneFragment phoneFragment=new PhoneFragment();
Bundle args=new Bundle();
args.putString(ARG_PARAM,param1);
phoneFragment.setArguments(args);
Log.i(TAG, "newInstance: out");
return phoneFragment;
}

@Override
public void onAttach(@NonNull Context context) {
Log.i(TAG, "onAttach: ");
super.onAttach(context);
iPhoneCallBack= (IPhoneCallBack) context;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
Log.i(TAG, "onCreate: ");
super.onCreate(savedInstanceState);
if(getArguments()!=null){
param=getArguments().getString(ARG_PARAM);
}
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.i(TAG, "onCreateView: ");
View view=inflater.inflate(R.layout.fragment_phone,container,false);
TextView textView=view.findViewById(R.id.fl_tv);
textView.setText(param);
iPhoneCallBack.setData("通讯录Activity");
return view;
}

@Override
public void onDestroyView() {
Log.i(TAG, "onDestroyView: ");
super.onDestroyView();
}

@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}

@Override
public void onDetach() {
Log.i(TAG, "onDetach: ");
super.onDetach();
}

public interface IPhoneCallBack{
void setData(String str);
}
}
  • fragment_phone.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".fragment.PhoneFragment">
<TextView
android:id="@+id/fl_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是通讯录"
android:textSize="32sp"
android:gravity="center"/>
</FrameLayout>
  • styles.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="blue_textview">
<item name="android:textSize">30sp</item>
<item name="android:textColor">#673AB7</item>
<item name="android:textStyle">bold|italic</item>
</style>

<style name="flRadioButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:textSize">24sp</item>
<item name="android:gravity">center</item>
<item name="android:button">@null</item>
<item name="android:textColor">@drawable/fl_rb_textcolor</item>
</style>
</resources>