自学内容网 自学内容网

【Android】Fragment的数据传递

碎片和活动之间的通信

Activity向Fragment

通过方法传递

构造方法

将碎片动态地加载到活动当中,先得到一个碎片,再将其放到活动当中。就想到碎片的替代方法,将我们所要传输的数据直接放到新创建的碎片里面,替换到原来的碎片。

  1. 首先使其自动创建一个Fragment,修改对应的布局文件,加上id,并设置一个文本框,用来显示所接收到的数据:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".datapass1.fragment.DataPassFragment">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="25sp"
        android:gravity="center"
        android:id="@+id/tv_content"
        android:text="@string/hello_blank_fragment" />

</FrameLayout>
  1. 我们要将所传输的数据显示在文本框,而在Fragment类中只有一个无参构造,因此在其中加上一个有参构造,当我们创建碎片的时候,就直接把数据传进去了。加入有参构造,注意,此时碎片收到了数据,但是文本框并没有修改,修改所对应的onViewCreated()碎片与视图建立联系的时候调用的方法,先得到视图里的TextView的id,将文本内容设置为活动所传输的数据,代码如下:
public class DataPassFragment extends Fragment {
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    private String mParam1;
    private String mParam2;
//新建要得到才对其中的内容进行修改
    private TextView mTextView;
//加入的构造方法
    public DataPassFragment(String data) {
        mParam1 = data;
    }

    public DataPassFragment() {
        ;
    }

    public static DataPassFragment newInstance(String param1, String param2) {
        DataPassFragment fragment = new DataPassFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            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_data_pass, container, false);
    }
    //将传输的数据放到文本框当中
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTextView = view.findViewById(R.id.tv_content);
        if (!TextUtils.isEmpty(mParam1)) { //判断字符串不是空
            mTextView.setText(mParam1);
        }
    }
}

注意:我们需要在设置文字之前判断字符串是不是空的,否则无论如何都会执行修改文本的操作,当我们无参构造的时候,就会出现空白的情况。碎片就修改完毕了,就需要对主活动的按钮注册点击事件了,触发数据的传输。

  1. 创建一个活动来存放你所要放的碎片以及点击按钮,并为点击按钮注册点击事件
<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:id="@+id/main"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context=".datapass1.DataPassActivity">

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:id="@+id/fcv"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:id="@+id/construct"
        android:text="通过构造方法传递数据"/>

</LinearLayout>
public class DataPassActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_data_pass);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        //添加Fragment到布局当中
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.fcv, DataPassFragment.class, null).commit();
        Button buttonconstruct = (Button) findViewById(R.id.construct);
        buttonconstruct.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 passDataByConstruct(DataPassActivity.this.getCurrentFocus());
            }
        });
    }
    //通过构造方法传递数据
    public void passDataByConstruct (View view) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        DataPassFragment dataPassFragment = new DataPassFragment("这是构造方法传递的数据");
        fragmentTransaction.replace(R.id.fcv, dataPassFragment).commit();
    }
}

此时页面为一开始自动创建的碎片:

在这里插入图片描述

此时运行程序,按下按钮,文本内容转换成了我们所传入的数据,对碎片进行了替换。

运行程序:

在这里插入图片描述

public方法

碎片放在活动的布局当中,我们就可以根据碎片容器的ID得到此时的碎片,从而调用里面的方法对碎片的内容进行修改。

  1. 先通过查找方法找到此时的碎片,再通过调用碎片里的为字符串进行修改的方法,对其进行修改,先在主活动中注册点击事件:
Button buttoncommon = (Button) findViewById(R.id.common);
buttoncommon.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {                passDataByCommon(DataPassActivity.this.getCurrentFocus());
    }
});
public void passDataByCommon (View view) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    Fragment fragment = fragmentManager.findFragmentById(R.id.fcv);
    if (fragment != null) {
        ((DataPassFragment)fragment).setParam1("这是普通public传递的数据");
    }
}
  1. 在碎片类里加入方法setParam1(),用来将所收到的数据展示在文本框当中,此时需要将碎片进行强转,对应的碎片里的代码进行修改,加入以下内容:
public void setParam1 (String s) {
    this.mParam1 = s;
    if (!TextUtils.isEmpty(mParam1)) { //判断字符串不是空
        mTextView.setText(mParam1);
    }
}

之后运行程序,得到的结果为:
在这里插入图片描述

通过Argument

这是Android本身为我们提供的向Fragment传递数据的方式,可用来一次性传递复杂、大量数据。就是将你所要传入的数据进行打包

  1. 同样的在主活动中为按钮注册点击事件
public void passDataByargument1 (View view) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    DataPassFragment dataPassFragment = new DataPassFragment("这是argument传递的数据");
    Bundle bundle = new Bundle();
    bundle.putString("data", "这是argument传递的数据");
    bundle.putInt("int_data", 100);
    dataPassFragment.setArguments(bundle);
    fragmentTransaction.replace(R.id.fcv, dataPassFragment).commit();
}
  1. 我们使用bundle对所传入的数据进行打包,以键值对的形式,使用setArguments()将数据发送过去,此时在碎片类里面本身就有对应的getArguments()方法,我们根据这个与键值对对其进行数据的接收,此时需要修改对应的代码:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
        mData = getArguments().getString("data");
        mintData = getArguments().getInt("int_data");
    }
}
  1. 得到了所要传输的数据,数据是在第一次被创建的时候得到,之后会经历onViewCreated()碎片与视图建立联系的时候调用,因此要将代码放在onViewCreated()里面:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mTextView = view.findViewById(R.id.tv_content);
    if (!TextUtils.isEmpty(mData)) {
        mTextView.setText(mData + mintData);
    }
}

按下通过argument传递数据的按钮,此时就接收到数据了:
在这里插入图片描述

通过接口

接口传递是一种基于编程语言自身性质的数据通信方式。当数据由A传到B的时候,A就为被观察者,B为观察者,此时我们想有一个角色可以将A所发生的变化告诉B,这个角色就被接口所承担。一般情况下,谁被观察就写在谁的内部。

  1. 同样地我们在主活动的界面对按钮进行点击事件的注册
public void passDataByInterface (View view) {
    mDataChangeListener.onDataChange("这是通过接口传递的数据");
}

private onDataChangeListener mDataChangeListener;

public void setmDataChangeListener(onDataChangeListener DataChangeListener) {
    mDataChangeListener = DataChangeListener;
}

//这个就是数据传输的接口
public interface onDataChangeListener {
    void onDataChange(String data);
}

定义接口(Interface)onDataChangeListener 是一个接口,它定义了一个方法 onDataChange(String data)

接口成员变量mDataChangeListener 是一个接口类型的成员变量,用于存储实现了 onDataChangeListener 接口的对象的引用。

设置接口的引用setmDataChangeListener 是一个公共方法,它接受一个实现了 onDataChangeListener 接口的对象作为参数,并将这个对象赋值给 mDataChangeListener 变量。

调用接口方法passDataByInterface 方法通过调用 mDataChangeListeneronDataChange 方法来传递数据。

  1. 碎片类的代码修改:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mTextView = view.findViewById(R.id.tv_content);
    
    ((DataPassActivity)getActivity()).setmDataChangeListener(new DataPassActivity.onDataChangeListener() {
        @Override
        public void onDataChange(String data) {
            if (!TextUtils.isEmpty(data)) {
                mTextView.setText(data);
            }
        }
    });
}

在碎片当中就有自己的方法得到所在的活动getActivity(),我们要用活动里的其他方法,因此先对其进行强转,之后的操作与Button按钮注册点击事件一样,重写里面所对应的方法。

此时运行程序:

在这里插入图片描述

Fragment向Activity

通过getActivity

每个Fragment都可以通过getActivity()方法获取承载它的活动对象,从而调用活动的方法向碎片传递数据。因此我们可以在碎片里面获取到活动,从而使用活动里的方法为活动传入数据,1. 在活动当中设置一个TextView,用来显示传入的数据:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAllCaps="false"
    android:textSize="20sp"
    android:text="还没有传入数据!!!"
    android:gravity="center"
    android:id="@+id/tv_reveive"/>

主活动当中写为TextView设置文本内容的方法:

public void setReceive (String data) {
    tvreceive.setText(data);
}
  1. 接下来就需要在碎片里面设置一个按钮用来触发发送数据,并获取活动使用活动的setReceive()方法对他的内容进行修改:
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:id="@+id/Bygetactivity"
    android:text="通过getActivity传递数据"/>
buttongetactivity = view.findViewById(R.id.Bygetactivity);
buttongetactivity.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ((DataPassActivity) getActivity()).setReceive("这是Fragment向活动传入的数据");
    }
});

重新运行程序,一开始的界面为:
在这里插入图片描述

从之前的布局可以知道,上面是一个碎片,给碎片加入了一个点击按钮用来触发通过getActivity()向活动传入数据,下面即为活动的TextView控件,当我们点击按钮:

在这里插入图片描述

此时就将数据传输到了活动,并显示在TextView当中。

通过接口

上面提到了通过接口从Activity向Fragment传递数据,从Fragment向Activity传递数据也是类似的,Fragment成为了被观察者,我们需要在Fragment里面写接口。

  1. 我们先在Fragment里设置发送数据的按钮,的代码:
<Button
        android:id="@+id/ByinterfaceActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="50dp"
        android:text="通过接口传递数据" />

Fragment的点击事件的注册:

//在onViewCreated()方法里面进行按钮的实现
buttonbyinterface = (Button) view.findViewById(R.id.ByinterfaceActivity);
buttonbyinterface.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       if (monFragmentDataChangeListener != null) {
            monFragmentDataChangeListener.onFragmentDataChange("这是Fragment通过接口向activity传入的数据");
        }
    }
});
  1. 碎片A的接口定义:
private OnFragmentDataChangeListener monFragmentDataChangeListener;

public void setFragmentDataChangeListener (OnFragmentDataChangeListener monFragmentDataChangeListener) {
    this.monFragmentDataChangeListener = monFragmentDataChangeListener;
}

public interface OnFragmentDataChangeListener {
    void onFragmentDataChange (String data);
}

在承载碎片的活动对接口的实现:

Fragment fra = fragmentManager.findFragmentById(R.id.fcv);
if (fra != null) {
    ((DataPassFragment)fra).setFragmentDataChangeListener(new DataPassFragment.OnFragmentDataChangeListener() {
        @Override
        public void onFragmentDataChange(String data) {
            tvreceive.setText(data);
        }
     );
}

我们运行程序发现按下按钮并没有显示出我们传递的数据,将提交事务改变为commitNow()

//添加Fragment到布局当中
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fcv, DataPassFragment.class, null).commitNow();

此时运行程序,数据就传输并显示出来了:
在这里插入图片描述

commitNow()commit()方法的区别:

  1. 异步与同步
    • commit(): 这个方法是异步的。当你调用commit()时,它会将事务放入一个队列中,然后返回。这意味着事务的执行是延迟的,不会立即执行。这有助于提高应用的响应性,因为它允许其他UI操作在事务执行之前继续进行。
    • commitNow(): 这个方法是同步的。当你调用commitNow()时,它会立即执行事务,而不是将其放入队列中。这意味着事务会立即完成,不会等待其他UI操作。
  2. 执行时机
    • commit(): 由于是异步执行,事务会在稍后的时间点执行,通常是在下一个UI绘制循环中。这使得在事务提交后立即进行其他UI操作成为可能,而不会导致阻塞。
    • commitNow(): 事务会立即执行,因此在执行期间,UI会暂时冻结,直到事务完成。这可能会导致用户界面在事务执行期间变得无响应。
  3. 适用场景
    • commit(): 通常用于需要提高应用响应性的场景,尤其是在事务执行期间需要进行其他UI操作的情况下。例如,在一个复杂的用户界面中,你可能需要同时进行多个UI操作,使用commit()可以避免阻塞UI。
    • commitNow(): 通常用于需要立即执行事务的场景,尤其是在事务执行后需要立即进行某些操作的情况下。例如,如果你需要在事务执行后立即检查某些条件或执行某些操作,使用commitNow()可以确保这些操作在事务完成后立即执行。

Fragment之间传递数据

此时我们是让两个碎片之间进行数据传递,新设立两个碎片,各自的碎片当中都设立一个TextView来展现所接收的数据,设置一个按钮用来发送数据,碎片A的布局文件为:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#f0a1a8"
    tools:context=".datapass1.fragment.FragmentPassA">

    <TextView
        android:paddingTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"
        android:text="此时还未进行数据传递"
        android:id="@+id/tv_a_receive"
        android:layout_gravity="center"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="通过活动传递数据给碎片B"
        android:id="@+id/passDataByActivityA"/>

</LinearLayout>

我们将碎片A的背景设置为粉色,碎片B与碎片A的布局是相同的,为了区分将碎片B的背景颜色设置为蓝色,将两个碎片放在一个活动当中进行数据的传递,承载碎片的活动的布局文件与将碎片放进活动当中的代码为:

<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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".datapass1.fragmentPassBetween">

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:id="@+id/fcv_a"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="@color/black"/>

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:id="@+id/fcv_b"/>

</LinearLayout>
public class fragmentPassBetween extends AppCompatActivity {

    private FragmentPassA fragmentPassA;
    private FragmentPassB fragmentPassB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_fragment_pass_between);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        fragmentPassA = new FragmentPassA();
        fragmentPassB = new FragmentPassB();
        getSupportFragmentManager().beginTransaction().replace(R.id.fcv_a, fragmentPassA).commit();
        getSupportFragmentManager().beginTransaction().replace(R.id.fcv_b, fragmentPassB).commit();
    }
}

此时运行程序活动界面为:
在这里插入图片描述

准备工作就完成了,先来看看从碎片A传数据到碎片B吧!

通过Activity中转

在前面的学习当中我们了解到根据碎片可以得到它所在的活动,进一步我们就可以根据所得到的活动来获取它的另一个碎片,即我们所要将数据传递的接收者碎片B,调用碎片B里的方法就可以对碎片B的TextView进行文本的重新修改,显示出我们所要传递的数据。因此在碎片A与B当中我们需要先获取它们的按钮与TextView,在碎片B当中写将接收到的数据展示在文本框:

public void setmDataB (String data) {
    mData = data;
    textViewB.setText(mData);
}

对于碎片A需要对按钮注册点击事件,上面已经提到了碎片A向B的传递思路,代码如下:

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    textViewA = view.findViewById(R.id.tv_a_receive);
    buttonpassA = view.findViewById(R.id.passDataByActivityA);
    buttonpassA.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //向碎片B传递数据
            Fragment fragmentB = ((fragmentPassBetween)getActivity())
                    .getSupportFragmentManager()
                    .findFragmentById(R.id.fcv_b);
            if (fragmentB != null) {
                ((FragmentPassB)fragmentB).setmDataB("这是FragmentA传来的数据");
            }
        }
    });
}

此时运行程序,按下碎片A里的按钮:

在这里插入图片描述

通过接口

在两个碎片当中都各自添加一个按钮,它们的点击事件就为通过接口实现数据传递,与此同时我们需要对两个碎片的按钮进行加载并注册点击事件。还是给大家展示从碎片A传递数据给碎片B吧,此时A就为被观察者,在A的内部定义接口:

private OnFragmentAChangeLiatener monFragmentAChangeLiatener;

public void setOnFragmentAChangeLiatener(OnFragmentAChangeLiatener onFragmentAChangeLiatener) {
    monFragmentAChangeLiatener = onFragmentAChangeLiatener;
}

public interface OnFragmentAChangeLiatener {
        void onFragementAChange(String data);
}

碎片A的点击事件:

buttoninterfacaA = (Button) view.findViewById(R.id.passDataByInterfacaA);
buttoninterfacaA.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       if (monFragmentAChangeLiatener != null) {
            monFragmentAChangeLiatener.onFragementAChange("这是通过接口来自FragmentA的数据");
        }
    }
});

在碎片B当中实现这个接口:

//先获取FragmentPassA
FragmentPassA fragmentPassA = (FragmentPassA) ((fragmentPassBetween)getActivity())
                .getSupportFragmentManager()
                .findFragmentById(R.id.fcv_a);
//当它不为空的时候即找到了FragmentPassA,并调用FragmentPassA的、setOnFragmentAChangeLiatener方法并实现接口
if (fragmentPassA != null) {
    fragmentPassA.setOnFragmentAChangeLiatener(new FragmentPassA.OnFragmentAChangeLiatener() {
        @Override
        public void onFragementAChange(String data) {
            textViewB.setText(data);
        }
    });
}

此时运行程序,按下按钮碎片B就接收到了来自碎片A的数据:

在这里插入图片描述

到这里就结束了!


原文地址:https://blog.csdn.net/2301_79828182/article/details/140724943

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!