Android-Fragment(上)

  Fragment必须嵌入在Activity中使用,因此,虽然Fragment有自己的生命周期,但会受它所在的Activity的生命周期的控制

在MainActivity中使用Fragment

①创建fragment

创建一个普通的fragment

创建完毕后会自动生成对应的xml布局

②修改BlankFragment.java

删掉其中无用的代码

删除完毕后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


public class BlankFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_blank, container, false);
}
}

优化onCreateView

1
2
3
4
5
6
7
8
9
10
//    优化返回值写法,使其不会一直创建
private View root;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (root == null){
root = inflater.inflate(R.layout.fragment_blank,container,false);
}
return root;
}

③修改两个Fragment的布局

BlankFragment1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:text="BlankFragment1"
android:textSize="20sp"
android:textColor="@color/black"
android:textStyle="bold"
android:id="@+id/blankFragmentText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="按钮"
android:id="@+id/blankFragmentButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

BlankFragment2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:text="BlankFragment2"
android:textSize="20sp"
android:textColor="#ff0033"
android:textStyle="bold"
android:id="@+id/blankFragmentText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="按钮"
android:id="@+id/blankFragmentButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

④添加至MainActivity布局

  打开MainActivity的布局资源文件activity_main.xml,在其中插入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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:text="replaceFragment1"
android:textAllCaps="false"
android:id="@+id/replaceFragment1"
android:layout_alignParentBottom="true"
android:layout_marginBottom="60dp"
android:layout_width="wrap_content"
android:layout_marginLeft="20dp"
android:layout_height="wrap_content"/>
<Button
android:text="replaceFragment2"
android:textAllCaps="false"
android:id="@+id/replaceFragment2"
android:layout_marginRight="20dp"
android:layout_marginBottom="60dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:background="#ccffcc"
android:id="@+id/frameLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/linearLayout1"
android:orientation="vertical">
<!-- 插入的两个fragment布局-->
<fragment
android:id="@+id/mainActivityFragment1"
android:name="com.a.fragment.BlankFragment1"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="match_parent"/>
<fragment
android:id="@+id/mainActivityFragment2"
android:name="com.a.fragment.BlankFragment2"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="match_parent"/>
</LinearLayout>
</FrameLayout>
</RelativeLayout>


   1. 插入在activity_main.xml中的fragment一定要有id
   2. 插入在activity_main.xml中的fragment一定要有name(BlankFragment的完整类名)属性

给fragment添加事件

fragment首次绘制界面时调用onCreateView,所以可以在onCreateView下写代码

给BlankFragment的按钮写事件(两个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
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;


public class BlankFragment1 extends Fragment implements View.OnClickListener{
private View root;
private TextView blankFragmentText;
private Button blankFragmentButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (root == null){
root = inflater.inflate(R.layout.fragment_blank1,container,false);
}
blankFragmentButton = root.findViewById(R.id.blankFragmentButton);
blankFragmentText = root.findViewById(R.id.blankFragmentText);
// 给按钮添加OnClick
blankFragmentButton.setOnClickListener(this);
return root;
}

@Override
public void onClick(View v) {
blankFragmentText.setText("Welcome BlankFragment1");
}
}

③效果

MainActivity页显示两个Fragment互不影响(两个Fragment甚至可以来自同一个BlankFragment.java,即name属性都为com.a.fragment.BlankFragment1)

动态添加fragment

  在动态添加fragment前一定要搞懂FragmentManager类

①两种fragment

  fragment分为app包内的fragment和v4包内的fragment

二者在使用上的区别:

  1. app包中的fragment兼容自3.0起以后的版本,v4包中的fragment可以兼容到1.6的版本
  2. app.fragment使用标签时。v4.fragment使用标签时,当这个Activity的布局中有标签,必须,如果不继成FragmentActivity的话 编译系统会把认为是app包中的Fragment来处理。但是此时我们导入的是v4包中的Fragment,所以会报错
  3. 对应的是v4包,对应的是app包

  在3.0版本之前使用fragment需借助V4包的getSupportFragmentManager()方法来间接获取FragmentManager()对象,3.0版本之后,可以通过Fragment的api直接使用getFragmentManager()方法获取FragmentManager()对象

  为什么我的MainActivity布局中使用v4的fragment,却继承AppCompatActivity而不是FragmentActivity?
  因为AppCompatActivity继承FragmentActivity,这样的话,MainActivity -继承-> AppCompatActivity -继承-> FragmentActivity

  Android studio创建MainActivity自动extends AppCompatActivity,为什么不extends Activity
  AppCompatActivity继承v4包的FragmentAvtivity,并且加入了很多新特性,可以更好的兼容旧系统

②FragmentManager(Fragment的管理)

  1. 顾名思义FragmentManager是Fragment的管理类
  2. getSupportFragmentManager()获取所在fragment 的父容器的管理器(getFragmentManager()同理)
  3. getSupportFragmentManager()或者getFragmentManager()获得FragmentManager对象, 可以认为Activity是FragmentManager的宿主环境类
1
2
3
4
5
6
7
8
9
10
11
//通过getSupportFragmentManager()可以获取一个v4包的fragment管理类
FragmentManager fragmentManager = getSupportFragmentManager();

//通过getSupportFragmentManager()拿到fragmentManager的触发器FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

//将fragment替换到R.id.frameLayout
fragmentTransaction.replace(R.id.frameLayout, fragment);//这里的fragment参数一个Fragment类创建的对象的变量

//执行
fragmentTransaction.commit();

③动态添加

1、创建至MainActivity

1
2
3
4
5
6
7
8
//        获取v4包的Fragment的管理类
FragmentManager fragmentManager = getSupportFragmentManager();
// 获取Transaction管理
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 将新fragment替换至指定id的控件内
fragmentTransaction.replace(R.id.frameLayout1,fragment);
// 开始
fragmentTransaction.commit();

2、fragment入栈
  在FragmentManager.transaction.addToBackStack();向栈中添加Fragment,从而实现添加Fragment后,可以实现按返回键依次返回上个Fragment

1
2
//入栈
fragmentTransaction.addToBackStack(null);

3、完整代码

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
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button replaceFragment1,replaceFragment2;
private FrameLayout frameLayout1;
private LinearLayout linearLayout1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

replaceFragment1 = findViewById(R.id.replaceFragment1);
replaceFragment2 = findViewById(R.id.replaceFragment2);
frameLayout1 = findViewById(R.id.frameLayout1);
linearLayout1 = findViewById(R.id.linearLayout1);

replaceFragment1.setOnClickListener(this);
replaceFragment2.setOnClickListener(this);
}

@Override
// 两个按钮的事件
public void onClick(View v) {
// 隐藏FrameLayout里的LinearLayout
linearLayout1.setVisibility(View.GONE);
switch (v.getId()){
// 按钮一
case R.id.replaceFragment1:
// 创建BlankFragment1
BlankFragment1 blankFragment1 = new BlankFragment1();
setReplaceFragment(blankFragment1);
break;
// 按钮二
case R.id.replaceFragment2:
// 创建BlankFragment2
BlankFragment2 blankFragment2 = new BlankFragment2();
setReplaceFragment(blankFragment2);
break;
}
}

private void setReplaceFragment (Fragment fragment){
// 获取v4包的Fragment的管理类
FragmentManager fragmentManager = getSupportFragmentManager();
// 获取Transaction管理
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 将新fragment替换至指定id的控件内
fragmentTransaction.replace(R.id.frameLayout1,fragment);
// 入栈
fragmentTransaction.addToBackStack(null);
// 开始
fragmentTransaction.commit();
}
}

Fragment入栈后在回退时可以一个一个的返回

fragment的数据传递

fragment数据传递分三种模式,即

  1. Activity向Fragment传递数据(Activity发送,Fragment接收)
  2. Fragment向Activity传递数据(Fragment发送,Activity接收)
  3. Fragment向Fragment传递数据

①原生Bundle传递数据

  Bundle传递的数据,是以key-value(键值对)的形式存在的;传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组;当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口

传递基本类型

在MainActivity中发送

1
2
3
4
5
6
7
8
9
10
//  创建数据传递对象
Bundle bundle = new Bundle();
// 写入键值对
bundle.putString("message1","Bundle方式传递的数据");
// 创建Fragment对象
BlankFragment1 blankFragment1 = new BlankFragment1();
// 设置需要传递的Fragment参数
blankFragment1.setArguments(bundle);
// 在setReplaceFragment方法将Fragment实例化
setReplaceFragment(blankFragment1);

在Fragment中接收

1
2
3
4
5
6
//  创建Bundle对象
Bundle bundle = this.getArguments();
// 获取对应key的键值对
String msg = bundle.getString("message");
// 输出
Toast.makeText(getActivity(),msg,Toast.LENGTH_SHORT).show();

写入MainActivity和Fragment

先去删掉activity_main中FrameLayout的LinearLayout

MainActivity

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
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.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button replaceFragment1,replaceFragment2;
private FrameLayout frameLayout1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

replaceFragment1 = findViewById(R.id.replaceFragment1);
replaceFragment2 = findViewById(R.id.replaceFragment2);
frameLayout1 = findViewById(R.id.frameLayout1);

replaceFragment1.setOnClickListener(this);
replaceFragment2.setOnClickListener(this);
}

@Override
// 两个按钮的事件
public void onClick(View v) {
// 创建数据传递对象
Bundle bundle = new Bundle();
switch (v.getId()){
// 按钮一
case R.id.replaceFragment1:
BlankFragment1 blankFragment1 = new BlankFragment1();
// 设置需要传递的Fragment参数
blankFragment1.setArguments(bundle);
// 写入键值对
bundle.putString("message1","Bundle方式向BlankFragment1传递的数据");
setReplaceFragment(blankFragment1);
break;
// 按钮二
case R.id.replaceFragment2:
BlankFragment2 blankFragment2 = new BlankFragment2();
// 设置需要传递的Fragment参数
blankFragment2.setArguments(bundle);
// 写入键值对
bundle.putString("message1","Bundle方式向BlankFragment2传递的数据");
setReplaceFragment(blankFragment2);
break;
}
}

private void setReplaceFragment (Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayout1,fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}

两个BlankFragment类似

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
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class BlankFragment1 extends Fragment implements View.OnClickListener{
private View root;
private TextView blankFragmentText;
private Button blankFragmentButton;
private String msg;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建Bundle对象
Bundle bundle = this.getArguments();
// 获取对应的键值对
msg = bundle.getString("message1");
Toast.makeText(getActivity(),msg,Toast.LENGTH_SHORT).show();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (root == null){
root = inflater.inflate(R.layout.fragment_blank1,container,false);
}
blankFragmentButton = root.findViewById(R.id.blankFragmentButton);
blankFragmentText = root.findViewById(R.id.blankFragmentText);
blankFragmentButton.setOnClickListener(this);
return root;
}

@Override
public void onClick(View v) {
blankFragmentText.setText("Welcome BlankFragment1");
}
}

②使用接口传递数据

创建接口
1
2
3
4
public interface CallBack {
String getMessage();
void sendMessage(String msg);
}
BlankFragment中

1、让BlankFragment再继承一个OnLongClickListener,使单击时从MianActivity获取,长按时向MainActivity发送

2、用接口定义变量,并创建setCallBack方法供MainActivity调用

1
2
3
4
5
6
//    用接口定义的变量
private CallBack callBack;
// 在MianActivity中调用
public void setCallBack(CallBack callBack){
this.callBack = callBack;
}

3、按钮单击时获取

1
2
String str = callBack.getMessage();
Toast.makeText(getActivity(),"BlankFragment1:" + str,Toast.LENGTH_SHORT).show();

4、按钮长按时发送

1
2
callBack.sendMessage("BlankFragment1发送的数据");
return true;

BlankFragment1.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
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class BlankFragment1 extends Fragment implements View.OnClickListener,View.OnLongClickListener{
private View root;
private TextView blankFragmentText;
private Button blankFragmentButton;
private String msg;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建Bundle对象
Bundle bundle = this.getArguments();
// 获取对应的键值对
msg = bundle.getString("message1");
Toast.makeText(getActivity(),msg,Toast.LENGTH_SHORT).show();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (root == null){
root = inflater.inflate(R.layout.fragment_blank1,container,false);
}
blankFragmentButton = root.findViewById(R.id.blankFragmentButton);
blankFragmentText = root.findViewById(R.id.blankFragmentText);
// 按钮事件
blankFragmentButton.setOnClickListener(this);
blankFragmentButton.setOnLongClickListener(this);
return root;
}

// 用接口定义的变量
private CallBack callBack;
// 在MianActivity中调用
public void setCallBack(CallBack callBack){
this.callBack = callBack;
}

@Override
public void onClick(View v) {
blankFragmentText.setText("Welcome BlankFragment1");
String str = callBack.getMessage();
Toast.makeText(getActivity(),"BlankFragment1:" + str,Toast.LENGTH_SHORT).show();
}

@Override
public boolean onLongClick(View v) {
callBack.sendMessage("BlankFragment1发送的数据");
return true;
}
}

MainActivity中

1、调用setCallBack方法

在按钮单击事件下通过已经创建好的blankFragment对象调用

1
2
3
4
5
6
7
8
9
10
blankFragment1.setCallBack(new CallBack() {
@Override
public String getMessage() {
return "MainActivity获取的数据";
}
@Override
public void sendMessage(String msg) {
Toast.makeText(MainActivity.this,"MainActivity:" + msg,Toast.LENGTH_SHORT).show();
}
});

2、按钮二

1
2
3
4
5
6
7
8
9
10
blankFragment2.setCallBack(new CallBack() {
@Override
public String getMessage() {
return "MainActivity获取的数据";
}
@Override
public void sendMessage(String msg) {
Toast.makeText(MainActivity.this,"MainActivity:" + msg,Toast.LENGTH_SHORT).show();
}
});

MainActivity.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
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.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button replaceFragment1,replaceFragment2;
private FrameLayout frameLayout1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

replaceFragment1 = findViewById(R.id.replaceFragment1);
replaceFragment2 = findViewById(R.id.replaceFragment2);
frameLayout1 = findViewById(R.id.frameLayout1);

replaceFragment1.setOnClickListener(this);
replaceFragment2.setOnClickListener(this);
}

@Override
// 两个按钮的事件
public void onClick(View v) {
// 创建数据传递对象
Bundle bundle = new Bundle();
switch (v.getId()){
// 按钮一
case R.id.replaceFragment1:
BlankFragment1 blankFragment1 = new BlankFragment1();
// 设置需要传递的Fragment参数
blankFragment1.setArguments(bundle);
// 写入键值对
bundle.putString("message1","Bundle方式向BlankFragment1传递的数据");
// 在setReplaceFragment方法将Fragment实例化
setReplaceFragment(blankFragment1);

blankFragment1.setCallBack(new CallBack() {
@Override
public String getMessage() {
return "MainActivity获取的数据";
}
@Override
public void sendMessage(String msg) {
Toast.makeText(MainActivity.this,"MainActivity:" + msg,Toast.LENGTH_SHORT).show();
}
});
break;
// 按钮二
case R.id.replaceFragment2:
BlankFragment2 blankFragment2 = new BlankFragment2();
// 设置需要传递的Fragment参数
blankFragment2.setArguments(bundle);
// 写入键值对
bundle.putString("message1","Bundle方式向BlankFragment2传递的数据");
// 在setReplaceFragment方法将Fragment实例化
setReplaceFragment(blankFragment2);

blankFragment2.setCallBack(new CallBack() {
@Override
public String getMessage() {
return "MainActivity获取的数据";
}
@Override
public void sendMessage(String msg) {
Toast.makeText(MainActivity.this,"MainActivity:" + msg,Toast.LENGTH_SHORT).show();
}
});
break;
}
}

private void setReplaceFragment (Fragment fragment){
// 获取v4包的Fragment的管理类
FragmentManager fragmentManager = getSupportFragmentManager();
// 获取Transaction管理
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 将新fragment替换至指定id的控件内
fragmentTransaction.replace(R.id.frameLayout1,fragment);
// 入栈
fragmentTransaction.addToBackStack(null);
// 开始
fragmentTransaction.commit();
}
}

效果图