文章目录
Fragment与Activity之间
Activity向Fragment传递数据
最基本的情况是,通过方法参数传递数据。
通过构造方法的参数传递
在Fragment创建的时候,通过构造方法传递简单、少量的变量,如String字符串。
代码示例
FragmentA.java
// 省略导包...
public class FragmentA extends Fragment {
private String mData;
// 构造方法,接收数据
public FragmentA(String data) {
this.mData = data;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
}
TestActivity.Java
// 省略导包...
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 创建Fragment, 同时通过构造方法传递数据
FragmentA fragmentA = new FragmentA("这是要传递的数据");
// 添加Fragment
fragmentTransaction.replace(R.id.fragment_a, fragmentA).commit();
}
}
Fragment对外提供设置数据的方法,也就是public类型的方法
代码示例
FragmentA.java
// 省略导包...
public class FragmentA extends Fragment {
private String mData;
public FragmentA() {
}
// 对外提供的设置数据的方法
public setData(String data) {
this.mData = data;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
}
TestActivity.Java
// 省略导包...
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 创建Fragment, 同时通过构造方法传递数据
FragmentA fragmentA = new FragmentA();
fragmentA.setData("这是要传递的数据");
// 添加Fragment
fragmentTransaction.replace(R.id.fragment_a, fragmentA).commit();
}
}
通过Argument传递
上面提到的通过方法传递,还只是最基本的,利用编程语言的基本性质传递数据,没有涉及到Android层面。而Android本身也为我们提供了向Fragment传递数据的方式,也就是提供了一些API供我们调用来传递复杂、大量数据,如setArgument。
与通过方法参数传递不同的是,通过Argument传递是把数据以键值对(key-value)的形式打包成一个包裹,即Bundle。这意味着可以一次性传递很多key-value数据。
代码示例
TestActivity.Java 传递数据setArgument
// 省略导包...
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 准备要传递的数据(key-value形式),打包成Bundle
Bundle bundle = new Bundle();
bundle.putString(FragmentA.ARG_PARAM1, "这是activity向fragment传递的数据1");
// 创建Fragment, 同时通过构造方法传递数据
FragmentA fragmentA = new FragmentA();
// 通过setArguments传递数据“包裹”
fragmentA.setArguments(bundle);
// 添加Fragment
fragmentTransaction.replace(R.id.fragment_a, fragmentA).commit();
}
}
FragmentA.java 接收数据getArguments()
// 省略导包...
public class FragmentA extends Fragment {
// 用来接收数据的key
public static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// 承接数据的变量
private String mParam1;
private String mParam2;
public FragmentA() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 在Fragment的onCreate阶段接收包裹
// 接收数据包裹,包裹里面是真正的数据,以key-value的形式构成
if (getArguments() != null) {
// 通过key 得到 value,然后用自身的变量承接
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
}
另外,随着Fragment的发展,官方推出了更加适合添加Fragment的方式。
如果你的项目引入Fragment的依赖是implementation 'androidx.appcompat:appcompat:1.3.0’及以上,那么就可以如下方式来添加Fragment,以及传递参数。
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 准备要传递的数据(key-value形式),打包成Bundle
Bundle bundle = new Bundle();
bundle.putString(FragmentA.ARG_PARAM1, "这是activity向fragment传递的数据1");
// 添加Fragment,这里不再需要我们创建Fragment了,它内部会自动调用其构造方法来创建。具体原因可参考上一篇文章
// 传递数据也不用setArgument了,而是直接作为第三个参数传入即可
fragmentTransaction.replace(R.id.fragment_a, FragmentA.class, bundle).commit();
通过接口传递数据
这种方式也是一种基于编程语言自身性质的数据通信方式,与Android本身无关,对于初学者可以作为了解。
接口通信乍一看可能感觉比较抽象难懂,其实没什么。本质上可以看作就是个观察者模式,即“被观察者–观察者”。
数据由A传到B,A就是被观察者,B是观察者吗?是的,但又不必完全是。就这点小事儿,B不必自己亲自观察,它可以派一个小兵替自己观察,有了结果通知它即可。这个小兵就是接口。
代码示例
TestActivity.Java 被观察者,数据改变后,通知观察者Fragment
// 省略导包...
public class TestActivity extends AppCompatActivity {
/**
* 数据改变的接口
* Activity自身是被观察着,OnDataChangeListener是观察者
*/
interface OnDataChangeListener {
void onDataChange(String data);
}
// 承接观察者的变量
private OnDataChangeListener mDataChangeListener;
// 提供给外界设置观察者的方法
public void setDataChangeListener(OnDataChangeListener dataChangeListener) {
mDataChangeListener = dataChangeListener;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 以官方推荐的方式添加Fragment,此处没有传递数据参数
fragmentTransaction.replace(R.id.fragment_a, FragmentA.class, null).commit();
}
public void passData(View view) {
// 观察者做出响应。这个观察者是外界设置进来的,所以会响应到外界传递进来的那个接口对象实例
if (mDataChangeListener != null) {
mDataChangeListener.onDataChange("这是activity向fragment传递的数据");
}
}
}
activity_test.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"
>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fcv"
android:layout_width="match_parent"
android:layout_height="300dp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="passData"
android:text="传递数据" />
</LinearLayout>
FragmentA.java 观察者,观察Activity里的变化,作出响应
// 省略导包...
public class FragmentA extends Fragment {
TextView textView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
textView = view.findViewById(R.id.tv_content);
// 设置观察者(实现了接口的对象实例),观察Activity的数据变化,并作出响应(相当于接收了来自Activity的数据)
((TestActivity) getActivity()).setDataChangeListener(new TestActivity.OnDataChangeListener() {
@Override
public void onDataChange(String data) {
textView.setText(data);
}
});
}
}
注意,这里getActivity()得到的是Activity类型的对象,要强转成真正持有该Fragment的Activity类,也就是TestActivity。
TestActivity继承了父类Activity,而且我们知道这里getActivity得到的对象肯定是TestActivity类型的,所以可以强转。
我们的setDataChangeListener方法就是在TestActivity里写的,如果不强转,会发现根本调不到这个方法。
下面也有类似的强转的地方,原因和这里一样。
Fragment向Activity传递数据
通过getActivity()
由于Activity是Fragment的承载者,每个Fragment都可以通过getActivity()方法获得承载它的Activity对象,从而调用这个Activity的方,自然可以通过这个方法向Activity传递数据。很简单。
代码示例
FragmentA.java
// 省略导包...
public class FragmentA extends Fragment {
Button btn;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btn = view.findViewById(R.id.btn_pass);
// 点击按钮,响应这里,向Activity传递数据
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((TestActivity) getActivity()).receiveDataFromFragment("这是传递给Activity的数据");
}
});
}
}
TestActivity.Java
// 省略导包...
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 添加Fragment
fragmentTransaction.replace(R.id.fragment_a, FragmentA.class, null).commit();
}
// 对外提供方法,接收数据
public void receiveDataFromFragment(String data) {
Toast.makeText(this, "收到来自Fragment的数据:" + data, Toast.LENGTH_SHORT).show();
}
}
通过接口传递数据
与上面Activity通过接口向Fragment传递方式一样,这时的被观察者是Fragment,观察者是Activity。不再赘述。
Fragment之间
Fragment之间传递数据
通过Activity中转
由于Activity是各个Fragment的承载者,所以可以作为中间桥梁为各个Fragment转递数据。
上面我们得知,在Fragment中可以通过getActivity()得到Activity对象。
在Activity中可以通过findFragmentById或者findFragmentByTag找到指定的Fragment。或者如果Activity成员变量里有Fragment的对象,那么可以直接使用,不用find了。
所以,一个Fragment先拿到其Activity对象,再通过这个Activity找到指定的Fragment对象,然后调用其方法,从而传递数据。
代码示例
TestActivity.Java
// 省略导包...
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
// 准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 添加FragmentA
fragmentTransaction.replace(R.id.fragment_container_a, FragmentA.class, null).commit();
// 添加FragmentB
fragmentTransaction.replace(R.id.fragment_container_b, FragmentB.class, null).commit();
}
}
FragmentA.java
// 省略导包...
public class FragmentA extends Fragment {
private Button btn;
private String mData;
public FragmentA() {
}
// 对外提供的设置数据的方法
public setData(String data) {
this.mData = data;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btn = view.findViewById(R.id.btn_pass);
// 点击按钮,响应这里,向FragmentB传递数据
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = ((TestActivity) getActivity()).getSupportFragmentManager();
FragmentB fragmentB = (FragmentB)fragmentManager.findFragmentById(R.id.fragment_container_b);
fragmentB.setData("这是FragmentA传给FragmentB的数据");
}
});
}
}
FragmentB.java
// 省略导包...
public class FragmentB extends Fragment {
private Button btn;
private String mData;
public FragmentB() {
}
// 对外提供的设置数据的方法
public setData(String data) {
this.mData = data;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_b, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btn = view.findViewById(R.id.btn_pass);
// 点击按钮,响应这里,向FragmentA传递数据
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fragmentManager = ((TestActivity) getActivity()).getSupportFragmentManager();
FragmentA fragmentA = (FragmentA)fragmentManager.findFragmentById(R.id.fragment_container_a);
fragmentA.setData("这是FragmentB传给FragmentA的数据");
}
});
}
}
通过接口传递数据
上文就介绍了,接口回调是一种基于编程语言自身性质的数据通信方式,与Android本身无关。所以,在Fragment之间也可以使用这种方式。
还是被观察者、观察者,不再赘述。
其他:ViewModel、FragmentResultListener
这些是基于Android JetPack库以及比较高版本的Fragment API的方法,也是官方推荐的方式。不过需要对JetPack有一定了解,这里不再展开。
虽然新技术层出不穷,但传统的方式也值得学习,毕竟新的东西也是在原有基础上演变而来的。旧的方式,在一开始也是新的。新的技术,在未来回头看也是旧方式。
详见官方文档: https://developer.android.com/guide/fragments/communicate