掌握Android Fragment开发之魂:Fragment的深度解析(中)

2024-05-07 23:04

本文主要是介绍掌握Android Fragment开发之魂:Fragment的深度解析(中),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


在上一篇文章中,我们深入探讨Fragment的核心特性、优势、生命周期,以及如何通过静态和动态使用Fragment。感兴趣的朋友,请前往查阅: 掌握Android Fragment开发之魂:Fragment的深度解析(上) 。


在这篇文章中,我们将继续深入探讨 Android Fragment,包括以下几个核心主题:

Fragment 通信

  • Fragment 向 Activity 传递数据
  • Activity 向 Fragment 传递数据
  • Fragment 之间的通信方式

通过全面的理论解析、生动的示例代码和实践经验分享,这篇文章将帮助您彻底掌握 Fragment 在 Android 应用程序开发中的使用技巧,提升代码的可维护性和用户体验。现在,就让我们一起开始这段激动人心的 Fragment 之旅吧!


在 Android 应用程序中,通常情况下 Activity 和 Fragment 之间、Fragment 与 Fragment 之间都需要进行数据通信。对于这种跨层级的通信,我们有多种选择,每种方式都有其适用场景。接下来,我们就来一一拆解它们。


一、Fragment 向 Activity 传递数据


当 Fragment 需要将数据传递给宿主 Activity 时,有以下几种常见方式:


1、通过接口回调


这是最常用和标准的做法。我们需要在 Fragment 中定义一个接口,并在 Activity 中实现该接口的方法。当 Fragment 需要传递数据时,只需调用接口中的方法即可。

实现细节如下:

// 定义接口
public interface DataTransferInterface {void onDataReceived(String data);
}// Fragment 实现
public class MyFragment extends Fragment {private DataTransferInterface dataTransferInterface;@Overridepublic void onAttach(@NonNull Context context) {super.onAttach(context);if (context instanceof DataTransferInterface) {dataTransferInterface = (DataTransferInterface) context;} else {throw new RuntimeException("Activity must implement DataTransferInterface");}}public void sendData(String data) {dataTransferInterface.onDataReceived(data);}
}// Activity 实现接口
public class MainActivity extends AppCompatActivity implements DataTransferInterface {@Overridepublic void onDataReceived(String data) {// 处理从 Fragment 传递过来的数据}
}

2、通过 ViewModel


如果应用程序使用了 ViewModel 架构组件,那么通过共享 ViewModel 实例也是一种不错的选择。Fragment 可以更新 ViewModel 中的数据,Activity 监听这些数据变化并做出响应。


第一步,创建 ViewModel 类

public class SharedViewModel extends ViewModel {private MutableLiveData<String> mMessage = new MutableLiveData<>();public LiveData<String> getMessage() {return mMessage;}public void setMessage(String message) {mMessage.setValue(message);}
}

SharedViewModel 中,我们定义了一个私有的 mMessage MutableLiveData 对象和一个公共的 getMessage() 方法来获取 LiveData。setMessage() 方法用于设置 mMessage 的值。


第二步,创建 Fragment 布局和类

fragment_share_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/edit_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter some text" /><Buttonandroid:id="@+id/button_share"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Share Data" /></LinearLayout>
ShareDataFragment.java
public class ShareDataFragment extends Fragment {private SharedViewModel viewModel;private EditText editText;@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_share_data, container, false);}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);editText = view.findViewById(R.id.edit_text);view.findViewById(R.id.button_share).setOnClickListener(v -> {viewModel.setMessage(editText.getText().toString());});}
}

ShareDataFragment 中,我们首先获取 SharedViewModel 的实例。然后,我们在 onViewCreated() 方法中设置按钮的点击事件监听器。当点击按钮时,我们将 EditText 中的文本传递给 ViewModel 的 setMessage() 方法。


第三步, 创建 Activity 布局和类

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><FrameLayoutandroid:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><TextViewandroid:id="@+id/text_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="18sp" /></LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {private TextView textView;private SharedViewModel viewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.text_view);viewModel = new ViewModelProvider(this).get(SharedViewModel.class);viewModel.getMessage().observe(this, message -> {textView.setText(message);});if (savedInstanceState == null) {getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ShareDataFragment()).commit();}}
}

MainActivity 中,我们首先获取 SharedViewModel 的实例。然后,我们观察 ViewModel 中的 LiveData 对象,并在数据发生变化时更新 TextView 的文本。


最后,我们在 onCreate() 方法中添加 ShareDataFragment 到Activity的布局中。

运行这个示例应用程序后,你会看到一个包含 EditText 和按钮的 Fragment。当你在 EditText 中输入一些文本并点击"Share Data"按钮时,文本将被传递给 ViewModel,并在 Activity 的 TextView 中显示出来。


3、通过事件总线 (EventBus)


使用事件总线库(如 EventBus 或 RxBus)也是一种解决方案。Fragment 发送事件,Activity 注册监听并处理事件。这种方式比较灵活,但也增加了复杂性。


下面是使用 EventBus 库演示 Fragment 向 Activity 传递数据的完整案例。


第一步,添加 EventBus 依赖

首先,我们需要在项目的 build.gradle 文件中添加 EventBus 库的依赖:

dependencies {implementation 'org.greenrobot:eventbus:3.3.1'
}

第二步,定义事件类

我们需要定义一个事件类,用于在 Fragment 和 Activity 之间传递数据。

public class MessageEvent {public final String message;public MessageEvent(String message) {this.message = message;}
}

第三步,创建 Fragment 布局和类

fragment_share_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/edit_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter some text" /><Buttonandroid:id="@+id/button_share"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Share Data" /></LinearLayout>
ShareDataFragment.java
public class ShareDataFragment extends Fragment {private EditText editText;@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_share_data, container, false);}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);editText = view.findViewById(R.id.edit_text);view.findViewById(R.id.button_share).setOnClickListener(v -> {String message = editText.getText().toString();EventBus.getDefault().post(new MessageEvent(message));});}@Overridepublic void onStart() {super.onStart();EventBus.getDefault().register(this);}@Overridepublic void onStop() {super.onStop();EventBus.getDefault().unregister(this);}
}

ShareDataFragment 中,我们在按钮的点击事件监听器中创建了一个 MessageEvent 对象,并将其发送到 EventBus。另外,我们需要在 onStart() 方法中注册 Fragment 到 EventBus,并在 onStop() 方法中注销。


第四步,创建 Activity 布局和类

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><FrameLayoutandroid:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /><TextViewandroid:id="@+id/text_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="18sp" /></LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {private TextView textView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = findViewById(R.id.text_view);if (savedInstanceState == null) {getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ShareDataFragment()).commit();}}@Overrideprotected void onStart() {super.onStart();EventBus.getDefault().register(this);}@Overrideprotected void onStop() {super.onStop();EventBus.getDefault().unregister(this);}@Subscribe(threadMode = ThreadMode.MAIN)public void onMessageEvent(MessageEvent event) {textView.setText(event.message);}
}

MainActivity 中,我们首先在 onCreate() 方法中添加 ShareDataFragment 到Activity的布局中。然后,我们在 onStart() 方法中注册 Activity 到 EventBus,并在 onStop() 方法中注销。


最后,我们定义了一个 onMessageEvent() 方法,该方法会在收到 MessageEvent 时被调用。在该方法中,我们将从 Fragment 传递过来的消息显示在 TextView 上。


注意,我们使用了 @Subscribe(threadMode = ThreadMode.MAIN) 注解,以确保事件处理逻辑在主线程中执行,从而避免潜在的线程安全问题。


运行这个示例应用程序后,你会看到一个包含 EditText 和按钮的 Fragment。当你在 EditText 中输入一些文本并点击"Share Data"按钮时,文本将被传递给 Activity,并在 Activity 的 TextView 中显示出来。


二、Activity 向 Fragment 传递数据


当 Activity 需要向 Fragment 传递数据时,我们有以下几种选择:


1、通过 Fragment arguments Bundle


这是官方推荐的做法。我们可以在实例化 Fragment 时,通过 Bundle 传递数据。Fragment 可以在 onCreate()onCreateView() 方法中获取 arguments Bundle。


// 创建 Bundle 并添加数据
Bundle bundle = new Bundle();
bundle.putString("key", "value");// 实例化 Fragment 并设置 arguments
MyFragment fragment = new MyFragment();
fragment.setArguments(bundle);// 在 Fragment 中获取数据
String value = getArguments().getString("key");

2、通过 FragmentManager 设置数据


除了使用 arguments Bundle,我们还可以利用 FragmentManager 直接向 Fragment 实例设置数据。不过这种方式需要提前获取 Fragment 实例的引用。


假设我们有一个MainActivity,它包含一个ContentFragment。我们希望在MainActivity中获取一些数据,并通过FragmentManager将这些数据传递给ContentFragment


MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 假设这是我们要传递给Fragment的数据String dataToSend = "Hello, Fragment!";// 获取FragmentManager实例FragmentManager fragmentManager = getSupportFragmentManager();// 开始Fragment事务FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();// 检查ContentFragment是否已经添加过,如果没有则添加if (fragmentManager.findFragmentByTag("ContentFragment") == null) {ContentFragment contentFragment = new ContentFragment();// 将数据通过Bundle传递给FragmentBundle args = new Bundle();args.putString("data", dataToSend);contentFragment.setArguments(args);// 添加Fragment到事务fragmentTransaction.add(R.id.fragment_container, contentFragment, "ContentFragment");}// 提交事务fragmentTransaction.commit();}
}

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="match_parent"/>

ContentFragment.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;public class ContentFragment extends Fragment {public ContentFragment() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_content, container, false);// 从Bundle中获取数据Bundle args = getArguments();if (args != null) {String data = args.getString("data");// 在Fragment的UI上显示传递过来的数据TextView textView = view.findViewById(R.id.textview_data);textView.setText(data);}return view;}
}

fragment_content.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textview_data"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp" /><!-- 其他布局内容 -->
</LinearLayout>

在这个例子中,MainActivityonCreate方法中创建了一个String类型的数据,并使用Bundle将其传递给ContentFragment。然后,通过FragmentTransactionContentFragment添加到布局中的FrameLayout上。在ContentFragmentonCreateView方法中,我们通过getArguments方法获取传递过来的Bundle,从中提取数据,并将其设置到TextView上显示。

这个案例展示了如何在Activity和Fragment之间通过FragmentManagerBundle进行数据传递。在实际应用中,你可以根据需要传递更复杂的数据结构。


3、通过 ViewModel


在Android架构组件中,ViewModel是一个用于存储和管理UI相关数据的类,它在配置更改(如屏幕旋转)时仍然保持数据,不随Activity或Fragment的销毁而销毁。使用ViewModel可以轻松地在Activity和Fragment之间共享数据,特别是当涉及到Fragment之间的通信时。

以下是使用ViewModelActivityFragment之间传递数据的一个完整案例。

假设我们有一个MainActivity,它使用一个SharedViewModel来存储一些数据,并希望将这些数据传递给一个ContentFragment

SharedViewModel.java

import androidx.lifecycle.ViewModel;public class SharedViewModel extends ViewModel {private String data;public void setData(String data) {this.data = data;}public String getData() {return data;}
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private SharedViewModel sharedViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);// 假设这是我们要传递给Fragment的数据sharedViewModel.setData("Hello, Fragment!");// 获取FragmentManager实例FragmentManager fragmentManager = getSupportFragmentManager();// 开始Fragment事务FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();// 检查ContentFragment是否已经添加过,如果没有则添加if (fragmentManager.findFragmentByTag("ContentFragment") == null) {ContentFragment contentFragment = new ContentFragment();fragmentTransaction.add(R.id.fragment_container, contentFragment, "ContentFragment");}// 提交事务fragmentTransaction.commit();}
}

ContentFragment.java

import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;public class ContentFragment extends Fragment {private SharedViewModel sharedViewModel;public ContentFragment() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_content, container, false);sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);// 在Fragment的UI上显示ViewModel中的数据TextView textView = view.findViewById(R.id.textview_data);textView.setText(sharedViewModel.getData());return view;}
}

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="match_parent"/>

fragment_content.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textview_data"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp" /><!-- 其他布局内容 -->
</LinearLayout>

在这个例子中,MainActivityonCreate方法中创建了一个SharedViewModel实例,并设置了一些数据。然后,它通过FragmentTransactionContentFragment添加到布局中的FrameLayout上。

ContentFragmentonCreateView方法中,我们通过ViewModelProvider获取了与MainActivity相同的SharedViewModel实例,并从其中获取数据,将其显示在TextView上。


使用ViewModel的好处在于,即使ActivityFragment被销毁并重新创建,ViewModel中的数据也不会丢失,这在处理屏幕旋转等配置更改时非常有用。此外,ViewModel也有助于实现MVVM架构模式,提高代码的可维护性和可测试性。


三、Fragment 之间的通信


对于 Fragment 之间的通信,我们有几种可选方案:


1、通过共享 ViewModel


在Android开发中,Fragment之间的通信可以通过共享ViewModel实现。这种方式特别适合于使用ViewModel来保持数据在配置更改(如屏幕旋转)时的持续性,并且可以在多个Fragment之间共享数据。

以下是使用共享ViewModel在两个Fragment之间进行通信的完整案例。

假设我们有两个Fragment:FragmentAFragmentBFragmentA允许用户输入一些数据,然后FragmentA将这些数据通过共享的ViewModel传递给FragmentB


SharedViewModel.java

import androidx.lifecycle.ViewModel;public class SharedViewModel extends ViewModel {private String data = "";public void setData(String data) {this.data = data;}public String getData() {return data;}
}

FragmentA.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;public class FragmentA extends Fragment {private SharedViewModel sharedViewModel;public FragmentA() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_a, container, false);sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);EditText editText = view.findViewById(R.id.editTextData);Button button = view.findViewById(R.id.buttonSendData);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String data = editText.getText().toString();sharedViewModel.setData(data);// 可以在这里进行FragmentB的导航Navigation.findNavController(v).navigate(R.id.action_fragmentA_to_fragmentB);}});return view;}
}

fragment_a.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/editTextData"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter data here"/><Buttonandroid:id="@+id/buttonSendData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send Data"/>
</LinearLayout>

FragmentB.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;public class FragmentB extends Fragment {private SharedViewModel sharedViewModel;public FragmentB() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_b, container, false);sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);TextView textView = view.findViewById(R.id.textViewData);textView.setText(sharedViewModel.getData());return view;}
}

fragment_b.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textViewData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp" /><!-- 其他布局内容 -->
</LinearLayout>

在这个例子中,我们创建了一个SharedViewModel类,它持有一个String类型的数据。在FragmentA中,用户输入的数据通过按钮点击事件被收集并存储到SharedViewModel中。然后,通过Navigation组件导航到FragmentB


FragmentB中,我们同样使用ViewModelProvider获取SharedViewModel的实例,并从其中读取数据,将其显示在TextView上。

这种方式的优点是,即使用户在FragmentAFragmentB之间切换,或者发生屏幕旋转等配置更改,SharedViewModel中的数据也不会丢失,因为ViewModel是设计为与UI控制器(如Fragment或Activity)的整个生命周期绑定的。


2、通过事件总线 (EventBus)


使用事件总线(如EventBus)在Fragment之间进行通信是一种轻量级的方式,它允许不同组件通过发布和订阅事件来异步通信。以下是使用EventBus在两个Fragment之间进行通信的案例。


首先,确保你已经在项目中添加了EventBus的依赖。在build.gradle文件中添加如下依赖:

dependencies {implementation 'org.greenrobot:eventbus:3.2.0'
}

假设我们有两个Fragment:FragmentAFragmentBFragmentA允许用户触发一个事件,然后通过EventBus发布这个事件。FragmentB订阅这个事件,并在接收到事件时更新UI。


EventBus 初始化

Application类中初始化EventBus,确保它在应用启动时被初始化。

import org.greenrobot.eventbus.EventBus;public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();EventBus.builder().addIndex(OptInIndex.class).installDefaultEventBus();}
}

确保在AndroidManifest.xml中指定MyApplication作为你的application标签的值。

定义事件

创建一个简单的事件类。

import org.greenrobot.eventbus.Subscribe;public class DataEvent {public final String data;public DataEvent(String data) {this.data = data;}
}

FragmentA.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;import org.greenrobot.eventbus.EventBus;public class FragmentA extends Fragment {private EventBus eventBus;public FragmentA() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_a, container, false);eventBus = EventBus.getDefault();Button button = view.findViewById(R.id.buttonSendData);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 发布事件eventBus.post(new DataEvent("Data from FragmentA"));// 导航到FragmentB,如果需要的话Navigation.findNavController(v).navigate(R.id.action_fragmentA_to_fragmentB);}});return view;}@Overridepublic void onStart() {super.onStart();eventBus.register(this);}@Overridepublic void onStop() public void onStop() {super.onStop();eventBus.unregister(this);}
}

FragmentB.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;public class FragmentB extends Fragment {private EventBus eventBus;private TextView textView;public FragmentB() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_b, container, false);textView = view.findViewById(R.id.textViewData);eventBus = EventBus.getDefault();return view;}@Overridepublic void onStart() {super.onStart();eventBus.register(this);}@Overridepublic void onStop() {super.onStop();eventBus.unregister(this);}@Subscribepublic void onEvent(DataEvent event) {// 更新UItextView.setText(event.data);}
}

fragment_a.xml 和 fragment_b.xml

这两个Fragment的布局文件分别包含一个按钮和一个文本视图,用于触发事件和显示事件数据。

<!-- fragment_a.xml -->
<Buttonandroid:id="@+id/buttonSendData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send Data"/>
<!-- fragment_b.xml -->
<TextViewandroid:id="@+id/textViewData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp"/>

在这个例子中,FragmentA在用户点击按钮时发布一个DataEventFragmentB注册了EventBus,并且有一个方法onEvent,它使用@Subscribe注解来监听DataEvent事件。当FragmentB接收到事件时,它会更新其TextView来显示事件中的数据。

请注意,使用EventBus时,务必在Fragment的onStart方法中注册EventBus,在onStop方法中取消注册,以避免内存泄漏。此外,由于EventBus是一个全局通信工具,使用时要注意避免不同组件间的事件冲突。


3、通过中介 Activity


在Android开发中,有时需要通过中介Activity来实现Fragment之间的通信。这通常发生在Fragment之间没有直接的联系,或者需要由Activity来协调Fragment间的交互时。以下是通过中介Activity在两个Fragment之间进行通信的完整案例。

假设我们有两个Fragment:FragmentAFragmentBFragmentA允许用户输入一些数据,然后通过调用Activity中的方法将这些数据传递给FragmentB

MainActivity.java

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.EditText;public class MainActivity extends AppCompatActivity implements FragmentA.OnDataPassListener {public interface OnDataPassListener {void onDataPass(String data);}private String receivedData = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (savedInstanceState == null) {FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();fragmentTransaction.add(R.id.fragment_container, new FragmentA(), "FragmentA");fragmentTransaction.commit();}}public void receiveData(String data) {receivedData = data;updateFragmentB();}private void updateFragmentB() {Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);if (currentFragment instanceof FragmentB) {FragmentB fragmentB = (FragmentB) currentFragment;fragmentB.updateData(receivedData);}}
}

FragmentA.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.AppCompatButton;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;public class FragmentA extends Fragment {private OnDataPassListener onDataPassListener;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_a, container, false);final EditText editText = view.findViewById(R.id.editTextData);AppCompatButton button = view.findViewById(R.id.buttonSendData);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String data = editText.getText().toString();if (onDataPassListener != null) {onDataPassListener.onDataPass(data);}}});return view;}@Overridepublic void onAttach(Context context) {super.onAttach(context);if (context instanceof MainActivity.OnDataPassListener) {onDataPassListener = (MainActivity.OnDataPassListener) context;} else {throw new RuntimeException(context.toString()+ " must implement OnDataPassListener");}}@Overridepublic void onDetach() {super.onDetach();onDataPassListener = null;}
}

FragmentB.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;public class FragmentB extends Fragment {private TextView textView;private MainActivity mainActivity;public FragmentB() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_b, container, false);textView = view.findViewById(R.id.textViewData);return view;}public void updateData(String data) {textView.setText(data);}@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);try {mainActivity = (MainActivity) activity;} catch (ClassCastException e) {throw new ClassCastException(activity.toString() + " must implement MainActivity");}}@Overridepublic void onDetach() {super.onDetach();mainActivity = null;}
}

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="match_parent"/>

fragment_a.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/editTextData"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter data here"/><Buttonandroid:id="@+id/buttonSendData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send Data"/>
</LinearLayout>

fragment_b.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textViewData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp" /><!-- 其他布局内容 -->
</LinearLayout>

在这个例子中,MainActivity实现了一个自定义的接口OnDataPassListener,该接口定义了一个方法onDataPass,用于接收从FragmentA传递过来的数据。FragmentA在用户输入数据并点击发送按钮后,通过onDataPassListener.onDataPass(data);调用将数据传递给MainActivity。然后,MainActivity通过调用updateFragmentB()方法将数据传递给FragmentB

FragmentB中,我们定义了一个updateData方法来更新UI。MainActivityFragmentB之间的通信通过mainActivity对象实现。

这种方式的优点是,Activity可以作为一个中介者来协调Fragment之间的交互,同时可以对数据进行验证和处理。然而,这种方式可能会导致代码的耦合度增加,因此在设计时需要仔细考虑。


4、通过 Interface

在Android开发中,使用接口(Interface)作为回调机制是一种常见的Fragment之间通信的方式。这种方式允许Fragment在需要与另一个Fragment或Activity交互时请求对方执行特定的操作。

以下是使用接口在FragmentAFragmentB之间进行通信的完整案例。

假设我们有两个Fragment:FragmentAFragmentBFragmentA需要将一些数据传递给FragmentB,但它们之间没有直接的引用。因此,我们将在FragmentB中定义一个接口,然后让FragmentA实现这个接口,以便在需要时调用FragmentB的方法。

FragmentB.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;public class FragmentB extends Fragment {private OnFragmentBActionListener listener;// 定义一个接口,用于回调public interface OnFragmentBActionListener {void onFragmentBAction(String data);}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);// 设置回调接口listener.onFragmentBAction("Initial data");}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {// Inflate the layout for this fragmentView view = inflater.inflate(R.layout.fragment_b, container, false);TextView textView = view.findViewById(R.id.textViewData);textView.setText("Waiting for data from Fragment A");return view;}// 设置回调接口的实现者public void setOnFragmentBActionListener(OnFragmentBActionListener listener) {this.listener = listener;}
}

FragmentA.java

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;public class FragmentA extends Fragment implements FragmentB.OnFragmentBActionListener {private EditText editText;private Button button;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_a, container, false);editText = view.findViewById(R.id.editTextData);button = view.findViewById(R.id.buttonSendData);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String data = editText.getText().toString();// 调用FragmentB的方法,传递数据onFragmentBAction(data);}});return view;}@Overridepublic void onFragmentBAction(String data) {// 在这里实现FragmentB的回调方法// 由于FragmentA实现了FragmentB的接口,所以这里可以直接调用这个方法// 但是我们需要持有FragmentB的引用,以便传递数据}
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FragmentManager fragmentManager = getSupportFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();FragmentA fragmentA = new FragmentA();fragmentTransaction.add(android.R.id.content, fragmentA);fragmentTransaction.commit();// 假设FragmentB已经实例化并添加到Activity中FragmentB fragmentB = (FragmentB) fragmentManager.findFragmentById(R.id.fragment_b);if (fragmentB != null) {// 设置FragmentB的回调接口fragmentB.setOnFragmentBActionListener(fragmentA);}}
}

layout 文件

以下是fragment_a.xmlfragment_b.xml的示例布局文件。

<!-- fragment_a.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/editTextData"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter data here"/><Buttonandroid:id="@+id/buttonSendData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send Data"/>
</LinearLayout>
<!-- fragment_b.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/textViewData"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp" /><!-- 其他布局内容 -->
</LinearLayout>

在这个例子中,`FragmentB`定义了一个接口`OnFragmentBActionListener`,该接口包含一个方法`onFragmentBAction`,用于接收数据。`FragmentA`实现了这个接口,并在用户输入数据并点击发送按钮时,调用该方法。
在`MainActivity`中,我们实例化了`FragmentA`并将其添加到布局中。同时,我们通过调用`FragmentB`的`setOnFragmentBActionListener`方法,将`FragmentA`作为回调接口的实现者传递给`FragmentB`。现在,当`FragmentA`需要与`FragmentB`通信时,它可以通过调用`onFragmentBAction`方法来实现。

请注意,为了使这个例子工作,你需要确保FragmentB的实例已经存在于MainActivity中,并且有一个方法来设置其回调接口。此外,由于FragmentA实现了FragmentB的接口,它可以直接调用接口中定义的方法,但前提是它需要持有FragmentB的引用。在实际应用中,你可以通过在MainActivity中设置这个引用,然后将它传递给需要它的Fragment。


结语:

以上就是我总结的一些 Fragment 通信方式。当然,实际项目中还需要根据具体情况来权衡和调整。希望通过这篇文章,您能够全面掌握 Fragment 的使用技巧,提升您的 Android 开发能力。


这篇关于掌握Android Fragment开发之魂:Fragment的深度解析(中)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/968643

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta