Android Fragment

Fragment

Fragment 的三个包:(互斥,A 类和 B 类需要使用相同的包。)

  • android.app. Fragment
  • android.app.v4.Fragment
  • androidx.fragment.app.Fragment

Fragment 的关键类:

  • FragmentManager。碎片化的管理。
  • FragmentTransation。操作 Fragment,做一些具体的事务。

Fragment 的切换方式

  • replace。替换,会重新走生命周期。
  • show / hide。显示和隐藏。

示例一

1
2
3
4
5
6
7
/**
* 1.初始化Frahment
* 2.显示Fragment
* 3.隐藏所有的Fragment
* 4.恢复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
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
    //聊天
private LinearLayout ll_chat;
private ChatFragment mChatFragment = null;
private FragmentTransaction mChatTransaction = null;

//我的
private LinearLayout ll_me;
private MeFragment mMeFragment = null;
private FragmentTransaction mMeTransaction = null;

/**
* 初始化Fragment
*/
private void initFragment() {
//聊天
if (mChatFragment == null) {
mChatFragment = new ChatFragment();
mChatTransaction = getSupportFragmentManager().beginTransaction();
mChatTransaction.add(R.id.mMainLayout, mChatFragment);
mChatTransaction.commit();
}

//我的
if (mMeFragment == null) {
mMeFragment = new MeFragment();
mMeTransaction = getSupportFragmentManager().beginTransaction();
mMeTransaction.add(R.id.mMainLayout, mMeFragment);
mMeTransaction.commit();
}
}

/**
* 显示Fragment
* @param fragment
*/
private void showFragment(Fragment fragment) {
if (fragment != null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
hideAllFragment(transaction);
transaction.show(fragment);
transaction.commitAllowingStateLoss();
}
}

/**
* 隐藏所有的Fragment
* @param transaction
*/
private void hideAllFragment(FragmentTransaction transaction) {
if (mChatFragment != null) {
transaction.hide(mChatFragment);
}
if (mMeFragment != null) {
transaction.hide(mMeFragment);
}
}

/**
* 切换主页选项卡
*/
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.ll_chat:
checkMainTab(2);
break;
case R.id.ll_me:
checkMainTab(3);
break;
}
}

private void checkMainTab(int index) {
switch (index) {
case 2:
showFragment(mChatFragment);
break;
case 3:
showFragment(mMeFragment);
break;
}
}

/**
* 优化:
* 防止重叠
* 当应用的内存紧张的时候,系统会回收掉Fragment对象
* 再一次进入的时候会重新创建Fragment
* 非原来对象,我们无法控制,导致重叠
*
* @param fragment
*/
@Override
public void onAttachFragment(Fragment fragment) {
if (mChatFragment == null && fragment instanceof ChatFragment) {
mChatFragment = (ChatFragment) fragment;
}
if (mMeFragment == null && fragment instanceof MeFragment) {
mMeFragment = (MeFragment) 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
41
42
43
44
45
46
47
48
49
50
51
52
public class FragmentActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
// 1.通过反射来解决
showFragment(0);
}

/**
* 通过反射来加载 Fragment
* currIndex 上一个 fragment 的序列号
* showFragment() 展示 Fragment
*/
int currIndex = -1;
Fragment[] fragmentList = new Fragment[1];

public void showFragment(int index) {
if (currIndex == index)
return;
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (currIndex != -1) {
ft.detach(fragmentList[currIndex]);
}
if (fragmentList[index] != null) {
ft.attach(fragmentList[index]);
} else {
creatFragment(index);
ft.replace(R.id.layout_fragment, fragmentList[index]);
}
// if (currIndex != -1) {
// tab.getChildAt(currIndex).setSelected(false);
// }
// tab.getChildAt(index).setSelected(true);
currIndex = index;
ft.commit();
}
public String[] str = {"ShareFragment"};

public void creatFragment(int index) {
try {
Fragment fragment = (Fragment) Class.forName("com.example.share." + str[index]).newInstance();
fragmentList[index] = fragment;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

示例(Kotlin)

生命周期

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
/**
* 第一行代码第三版
*
* Fragment 的生命周期:
* 添加一个 Fragment ->
* onAttach(),onCreate(),onCreateView(),onActivityCreated(),onStart(),onResume()
* Fragment 已激活 ->
* 1. 用户点击返回键或 Fragment 被移除/替换:
* onPause(),onStop(),onDestroyView(),onDestroy(),onDetach()
* 2. 当 Fragment 被添加到返回栈,然后被移除/替换:
* onPause(),onStop(),onDestroyView() -> 从返回栈中回到上一个 Fragment -> onCreateView()
* Fragment 被销毁。
*
* ----------------------------------------------------------------------------------------------------
*
* Fragment 的状态分为:
* 1. 运行状态:当其所关联的 Activity 正处于运行状态时。
* 2. 暂停状态:当一个 Activity 进入暂停状态。
* 3. 停止状态:当一个 Activity 进入停止状态,或者通过调用 FragmentTransaction 的 remove、replace() 将其从 Activity 中移除,
* 但在事务提交之前 调用 了 addToBackStack(),这时的 Fragment 也会进入停止状态,总的来说,此状态是对用户完全不可见的,可能会被系统回收。
* 4. 销毁状态:Fragment 总是依附于 Activity 而存在,会随 Activity 的销毁而进入销毁状态。或者通过调用 FragmentTransaction 的 remove、replace() 将其从 Activity 中移除,
* 但在事务提交之前 并没有调用 了 addToBackStack(),此时会进入销毁状态。
*/
class LifeCycleFragment :Fragment() {

companion object{
// 定义了一个 TAG 常量
const val TAG = "TAG_LifeCycleFragment"
}

/**
* 多了几个与 Activity 不同的生命周期
* 当与 Activity 建立关联时调用
*/
override fun onAttach(context: Context) {
super.onAttach(context)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}

/**
* 为 Fragment 创建视图(加载布局)时调用
* --------------------------------------------------------------
* onCreate(),onCreateView(),onActivityCreated 三个方法中:
* 可通过 savedInstanceState 参数获取 onSaveInstanceState() 保存的数据
*/
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}

/**
* 确保与 Fragment 相关联的 Activity 已经创建完毕时调用
*/
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}

override fun onStart() {
super.onStart()
}

override fun onResume() {
super.onResume()
}

override fun onPause() {
super.onPause()
}

override fun onStop() {
super.onStop()
}

/**
* 当与 Fragment 关联的视图被移除时调用
*/
override fun onDestroyView() {
super.onDestroyView()
}

override fun onDestroy() {
super.onDestroy()
}

/**
* 当 Fragment 和 Activity 解除关联时调用
*/
override fun onDetach() {
super.onDetach()
}

}
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
/**
* Android 3.0 版本开始引入了 Fragment 的概念,它可以让界面在平板上更好地展示。
* Fragment 是一种可以嵌入在 Activity 中的 UI 片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用广泛。
*/
class FragmentActivity :BaseActivity(){

/**
* Fragment 和 Activity 之间的交互:
* ----------------------------------------------------------------------------------------------------
* 通过 FragmentManager,从布局中获取 Fragment 的实例:
* val fragment = supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment
* 并且插件也对 findFragmentById() 进行了扩展,允许直接使用布局文件中定义的 Fragment id 名称来自动获取相应实例。
* val fragment = leftFrag as LeftFragment
* ----------------------------------------------------------------------------------------------------
* 在 Fragment 中可通过 getActivity() 得到和当前 Fragment 相关联的 Activity 实例:
* if(activity != null){ // getActivity() 可能返回 null
* val activity = activity as FragmentActivity()
* }
* 另外当 Fragment 中需要 Context 对象时,也可使用 getActivity(),因为获取到的 Activity 本身就是一个 Context 对象。
* ----------------------------------------------------------------------------------------------------
* 首先在一个 Fragment 中可得到与之相关联的 Activity,然后通过这个 Activity 获取另一个 Fragment 实例,
* 这样就实现了不同 Fragment 之间的通信功能。
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 限定符(qualifier):比如 large 限定符,当屏幕被认为是 large 的设备就会加载 layout-large 文件夹下的布局。
// 常见的比如:
// 大小:small,normal,large,xlarge。对应(小,中等,大,超大)屏幕设备的资源。
// 分辨率:ldpi,mdpi,hdpi,xhdpi,xxhdpi。对应(低(120dpi以下),中等(120-160),高(160-240),超高(240-320),超超高(320-480))分辨率设备的资源。
// 方向:land,port。对应(横屏,竖屏)设备的资源。
// 最小宽度限定符:当程序运行在屏幕宽度大于等于 600 dp 的设备上时,会加载 layout-sw600dp/activity_fragment 布局,否则仍然加载默认的 layout/activity_fragment。
setContentView(R.layout.activity_fragment)

btn.setOnClickListener{
replaceFragment(RightFragment())
}

replaceFragment(AnotherRightFragment())
}

/**
* 动态添加 Fragment 主要分为 5 步:
* 1. 创建待添加 Fragment 实例
* 2. 获取 FragmentManager,在 Activity 中可直接调用 getSupportFragmentManager() 获取
* 3. 开启一个事务,通过调用 beginTransaction() 开启
* 4. 向容器内添加或替换 Fragment,一般使用 replace() 实现,需要传入容器的 id 和待添加的 Fragment 实例
* 5. 提交事务,调用 commit() 完成。
*/
private fun replaceFragment(fragment: Fragment) {
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.rightLayout,fragment)
// 在 Fragment 实现返回栈的效果,点击 Back 键回到上一个 Fragment。
// 不会执行 onCreate(),因为借助了 addToBackStack() 使得 Fragment 没有被销毁。
// 它接收一个名字用于描述返回栈的状态,一般传入 null 即可。
// transaction.addToBackStack(null)
transaction.commit()
}


companion object{
fun actionStart(context: Context){
val intent = Intent(context, FragmentActivity::class.java)
context.startActivity(intent)
}
}
}

res / layout / activity_fragment.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- 通过 name 属性来显式声明要添加的 Fragment 类名 -->

<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/leftFrag"
android:name="com.example.myapplication.fragment.LeftFragment"/>

<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/rightLayout" />

</LinearLayout>

res / layout-large / activity_fragment.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- 通过 name 属性来显式声明要添加的 Fragment 类名 -->
<!-- large 限定符(qualifier) -->

<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/leftFrag"
android:name="com.example.myapplication.fragment.LeftFragment"/>

<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/rightLayout"
android:name="com.example.myapplication.fragment.RightFragment"/>


res / layout-sw600dp / activity_fragment.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- 最小宽度限定符 -->

<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/leftFrag"
android:name="com.example.myapplication.fragment.LeftFragment"/>

<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/rightLayout"
android:name="com.example.myapplication.fragment.RightFragment"/>

</LinearLayout>

Fragment 切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 继承 Fragment,两个不同包下的供选择:
* AndroidX 库中的:androidx.fragment.app.Fragment(可使特性在不同系统版本保持一致)
* 系统内置的:android.app.Fragment(9.0 版本中被废弃)
*/
class LeftFragment : Fragment(){

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 通过 LayoutInflater 的 inflate() 动态加载布局
return inflater.inflate(R.layout.fragment_left,container,false)
}
}
fragment_left.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?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">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="button"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 继承 Fragment,两个不同包下的供选择:
* AndroidX 库中的:androidx.fragment.app.Fragment(可使特性在不同系统版本保持一致)
* 系统内置的:android.app.Fragment(9.0 版本中被废弃)
*/
class RightFragment : Fragment(){

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 通过 LayoutInflater 的 inflate() 动态加载布局
return inflater.inflate(R.layout.fragment_right,container,false)
}
}
fragment_right.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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"
android:background="#00ff00">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="text"
android:textSize="24sp"
android:id="@+id/tvText" />
</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 继承 Fragment,两个不同包下的供选择:
* AndroidX 库中的:androidx.fragment.app.Fragment(可使特性在不同系统版本保持一致)
* 系统内置的:android.app.Fragment(9.0 版本中被废弃)
*/
class AnotherRightFragment : Fragment(){

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 通过 LayoutInflater 的 inflate() 动态加载布局
return inflater.inflate(R.layout.fragment_anotherright,container,false)
}
}
fragment_anotherright.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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"
android:background="#00ffff">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="text"
android:textSize="24sp"
android:id="@+id/tvText" />
</LinearLayout>