Fragment传递数据

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子林Android

感谢老板,老板大气!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值