掌握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;

    @Override
    public 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 {

    @Override
    public 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">

    <EditText
        android:id="@+id/edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter some text" />

    <Button
        android: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
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_share_data, container, false);
    }

    @Override
    public 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">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <TextView
        android: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;

    @Override
    protected 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">

    <EditText
        android:id="@+id/edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter some text" />

    <Button
        android: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
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_share_data, container, false);
    }

    @Override
    public 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));
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public 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">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <TextView
        android: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;

    @Override
    protected 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();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected 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 {

    @Override
    protected 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传递给Fragment
            Bundle 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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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">

    <TextView
        android: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;

    @Override
    protected 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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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">

    <TextView
        android: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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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() {
            @Override
            public 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">

    <EditText
        android:id="@+id/editTextData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter data here"/>

    <Button
        android: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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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">

    <TextView
        android: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 {
    @Override
    public 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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_a, container, false);

        eventBus = EventBus.getDefault();

        Button button = view.findViewById(R.id.buttonSendData);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 发布事件
                eventBus.post(new DataEvent("Data from FragmentA"));
                // 导航到FragmentB,如果需要的话
                Navigation.findNavController(v).navigate(R.id.action_fragmentA_to_fragmentB);
            }
        });

        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        eventBus.register(this);
    }

    @Override
    public 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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_b, container, false);
        textView = view.findViewById(R.id.textViewData);

        eventBus = EventBus.getDefault();

        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        eventBus.register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        eventBus.unregister(this);
    }

    @Subscribe
    public void onEvent(DataEvent event) {
        // 更新UI
        textView.setText(event.data);
    }
}

fragment_a.xml 和 fragment_b.xml

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

<!-- fragment_a.xml -->
<Button
    android:id="@+id/buttonSendData"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Send Data"/>
<!-- fragment_b.xml -->
<TextView
    android: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 = "";

    @Override
    protected 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;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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() {
            @Override
            public void onClick(View v) {
                String data = editText.getText().toString();
                if (onDataPassListener != null) {
                    onDataPassListener.onDataPass(data);
                }
            }
        });

        return view;
    }

    @Override
    public 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");
        }
    }

    @Override
    public 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
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mainActivity = (MainActivity) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement MainActivity");
        }
    }

    @Override
    public 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">

    <EditText
        android:id="@+id/editTextData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter data here"/>

    <Button
        android: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">

    <TextView
        android: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);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 设置回调接口
        listener.onFragmentBAction("Initial data");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View 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
    @Override
    public 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() {
            @Override
            public void onClick(View v) {
                String data = editText.getText().toString();
                // 调用FragmentB的方法,传递数据
                onFragmentBAction(data);
            }
        });

        return view;
    }

    @Override
    public 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 {

    @Override
    protected 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">

    <EditText
        android:id="@+id/editTextData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter data here"/>

    <Button
        android: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">

    <TextView
        android: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 开发能力。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/601492.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【边东随笔】(2) 北美鳄龟的生存智慧:细心 | 信心 | 狠心 | 耐心

非常谨慎&#xff0c;在水域中会先找到躲避将自身安置于有利地形 ( 细心 &#xff09;。 浮出水面换气&#xff0c;水体稍有异动就会退回水中&#xff0c;优秀掠食者对自身优势牢牢的把握&#xff08; 信心 &#xff09;。 非常优雅&#xff0c;猎食动作不存在任何花里胡哨&a…

Spring Boot3.x集成Disruptor4.0

Disruptor介绍 Disruptor是一个高性能内存队列&#xff0c;研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCon演讲后&#xff0c;获得了业界关注。2011年&…

css--控制滚动条的显示位置

各种学习后的知识点整理归纳&#xff0c;非原创&#xff01; ① direction属性 滚动条在左侧显示② transform:scaleY() 滚动条在上侧显示 正常的滚动条会在内容超出规定的范围后在区域右侧和下侧显示在有些不正常的需求下会希望滚动条在上侧和左侧显示自己没有想到好的解决方案…

Vue3:menu导航栏出现多个同一跳转路径的菜单处理

文章目录 需求整理实现思路实现过程 需求整理&#xff0c;实现思路 最近公司想将之前老的项目整理出来&#xff0c;因为这个老项目内容太杂什么页面都往里面塞&#xff0c;导致菜单特别多&#xff0c;公司就像将这个老的项目迁出来&#xff0c;这个旧的项目本来是后端PHP写的。…

面试分享——订单超30分钟未支付自动取消用什么实现?如何使用Redis实现延迟队列?

目录 1.订单超时未支付自动取消&#xff0c;这个你用什么方案实现&#xff1f; 2.如何使用Redis实现延迟队列 2.1实验步骤 2.2实现生产可用的延迟队列还需关注什么 3.总结 电商场景中的问题向来很受面试官的青睐&#xff0c;因为业务场景大家都相对更熟悉&#xff0c;相关…

禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》Chapter 5插图

禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 Chapter 5插图

【Verilog】big_small_cnt

通用大小计数器 timescale 1ns / 1ps // // Company: // Engineer: wengf // Create Date: // Design Name: // Module Name: big_small_cnt // Project Name: // Target Devices: // Tool Versions: // Description: // Dependencies: // Revision: // Revision 0…

超声波测距传感器--第七天

1.超声波测距 型号:HC-SR04 接线参考:模块除了两个电源引脚外,还有TRIG,ECHO引脚,这两个引脚分别接我们开发板的P1.5和P1.6端 超声波模块是用来测量距离的一种产品,通过发送超声波,利用时间差和声音传播速度,计算模块到前方障碍物的距离。 2. 如何让它发送波: Tri…

Linux入门攻坚——22、通信安全基础知识及openssl、CA证书

Linux系统常用的加解密工具&#xff1a;OpenSSL&#xff0c;gpg&#xff08;是pgp的实现&#xff09; 加密算法和协议&#xff1a; 对称加密&#xff1a;加解密使用同一个秘钥&#xff1b; DES&#xff1a;Data Encryption Standard&#xff0c;数据加密标准&…

【postgreessql 】查询数据库表占用物理空间

查询单个表的磁盘使用量&#xff1a; SELECTrelname,pg_size_pretty ( pg_total_relation_size ( relid ) ) AS total_size FROMpg_catalog.pg_statio_user_tables; 查询所有表的总磁盘使用量&#xff1a; SELECTpg_size_pretty ( SUM ( pg_total_relation_size ( relid ) )…

简洁大气APP下载单页源码

源码介绍 简洁大气APP下载单页源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 效果截图 源码下载 简洁大气APP下载单页源码

[oeasy]python0016_在vim中直接运行python程序

回忆上次内容 上次 置换 esc 和 caps lock 任何操作 都可以在不移动 手腕的状态下完成了 每次都要 退出vim编辑器&#x1f634; 才能 在shell中 运行python程序有点麻烦 想要 不退出vim 直接在 vim应用 中运行 py程序可能吗&#xff1f;&#x1f914; 运行程序 以前都是 先退…

0507华为od二面

只记录自己没回答上的问题 1、ZGC的缺点&#xff1a; 1)只是适用于32位系统 2)最大只是支持4TB内存容量 3)最糟糕的情况下吞吐量会下降15%&#xff0c;这都不是事至于吞吐量&#xff0c;通过扩容分分钟解决 4)分代的原因:不同对象的生命周期不相同&#xff0c;可能会扫描整个堆…

TiDB数据库 使用tiup 缩容遇到的tikv处于下线中状态无法转为tombstone状态

官方的缩容文档 https://docs.pingcap.com/zh/tidb/stable/scale-tidb-using-tiup 论坛地址 https://tidb.net/ 问题&#xff1a;使用tiup 缩容遇到的tikv处于下线中状态无法转为tombstone状态 解决方法 1.缩容 tiup cluster scale-in --node 10.0.1.5:20160 2.查看 tiup…

2024.5.6 关于 SpringCloud 的基本认知

目录 引言 微服务框架所包含的技术栈 微服务架构演变 单体架构 分布式架构 微服务架构 微服务技术对比 认识 SpringCloud SpringBoot 版本兼容关系 服务拆分和远程调用 服务拆分注意事项 远程调用 引言 微服务是一种框架风格&#xff0c;按照业务板块来划分应用代码…

彻底解决python的pip install xxx报错(文末附所有依赖文件)

今天安装pip install django又报错了&#xff1a; C:\Users\Administrator>pip install django WARNING: Ignoring invalid distribution -ip (d:\soft\python\python38\lib\site-pac kages) Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting djan…

构建 WebRTC 一对一信令服务器

构建 WebRTC 一对一信令服务器 构建 WebRTC 一对一信令服务器前言为什么选择 Nodejs&#xff1f;Nodejs 的基本原理浏览器使用 Nodejs安装 Nodejs 和 NPMsocket.io信令服务器搭建信令服务器客户端服务端启动服务器并测试 总结参考 构建 WebRTC 一对一信令服务器 前言 我们在学…

Bookends for Mac v15.0.2 文献书籍下载管理

Bookends Mac版可以轻松地将其导入参考 &#xff0c;并直接搜索和进口从数以百计的线上资料来源。Bookends Mac版使用内置在浏览器中下载参考与PDF格式的文件&#xff0c;或和/或网页的点击。 Bookends for Mac v15.0.2注册激活版下载 本文由 mdnice 多平台发布

信息系统项目管理师0092:项目管理原则(6项目管理概论—6.4价值驱动的项目管理知识体系—6.4.1项目管理原则)

点击查看专栏目录 文章目录 6.4价值驱动的项目管理知识体系6.4.1项目管理原则1.原则一:勤勉、尊重和关心他人2.原则二:营造协作的项目管理团队环境3.原则三:促进干系人有效参与4.原则四:聚焦于价值5.原则五:识别、评估和响应系统交互6.原则六:展现领导力行为7.原则七:根…

python菜鸟级安装教程 -下篇(安装编辑器)

来来~接着上篇的来~ 安装好python.exe之后&#xff0c;我们可以根据cmd命令窗口&#xff0c;码代码。 这算最简单入门了~ 如果我们在安装个编辑器。是什么效果&#xff0c;一起体验一下吧 第一步&#xff0c;下载编辑器&#xff0c;选择官网&#xff0c;下载免费版本入门足…
最新文章