与其他 Fragment 通信

为重复使用 Fragment 界面组件,您应以完全独立且模块化的形式构建每个组件,使其定义自己的布局和行为。在您定义这些可重复使用的 Fragment 后,便可将其与 Activity 相关联,然后再将它们与应用逻辑连接起来,从而实现整体复合界面。

您常常希望 Fragment 之间能够进行通信,以实现某种目的,例如根据用户事件改变内容。所有 Fragment 间的通信通过共享的 ViewModel 或关联的 Activity 来完成。两个 Fragment 不得直接通信。

建议创建共享的 ViewModel 对象以实现 Fragment 间通信。两个 Fragment 均可通过其包含的 Activity 访问 ViewModel。这些 Fragment 可更新 ViewModel 中的数据,并且如果使用 LiveData 公开数据,只要 Fragment 观察到 ViewModel 中的 LiveData,系统便会将新状态推送给其他 Fragment。要了解如何实现这种通信,请阅读 ViewModel 指南中的“在 Fragment 间共享数据”部分。

如果无法使用共享的 ViewModel 在 Fragment 间通信,您可以使用接口手动执行通信流程。但是,这最终只会导致更多执行工作,并且无法在其他 Fragment 中轻松重复使用。

定义接口

要允许 Fragment 与其 Activity 通信,您可以在 Fragment 类中定义一个接口,然后在 Activity 内实现该接口。Fragment 在其 onAttach() 生命周期方法执行期间捕捉接口实现,然后便可调用接口方法与 Activity 通信。

以下是 Fragment 与 Activity 的通信示例:

HeadlinesFragment

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener callback;

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener callback) {
        this.callback = callback;
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    // ...
}

MainActivity

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    // ...

    @Override
    public void onAttachFragment(Fragment fragment) {
        if (fragment instanceof HeadlinesFragment) {
            HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
            headlinesFragment.setOnHeadlineSelectedListener(this);
        }
    }
}

现在 Fragment 可以使用 OnHeadlineSelectedListener 接口的 mCallback 实例调用 onArticleSelected() 方法(或接口中的其他方法),从而向 Activity 传递消息。

例如,用户点击某个列表项时,系统会调用 Fragment 中的以下方法。Fragment 利用回调接口将事件传回给父 Activity。

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Send the event to the host activity
        callback.onArticleSelected(position);
    }

实现接口

为接收 Fragment 发出的事件回调,Fragment 的宿主 Activity 必须实现 Fragment 类中定义的接口。

例如,以下 Activity 可实现上例中的接口。

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

向 Fragment 传递消息

宿主 Activity 可以使用 findFragmentById() 捕捉 Fragment 实例来向 Fragment 传递消息,然后直接调用 Fragment 的公共方法。

例如,假设以上所示的 Activity 可能包含另一个 Fragment,其用途是显示由以上回调方法中返回的数据所指定的项目。在这种情况下,该 Activity 可将回调方法中收到的信息传递给另一个 Fragment,并由其显示该项目:

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            ArticleFragment newFragment = new ArticleFragment();
            Bundle args = new Bundle();
            args.putInt(ArticleFragment.ARG_POSITION, position);
            newFragment.setArguments(args);

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}

要了解关于实施 Fragment 的更多信息,请参阅 Fragment。您也可以通过探索相关示例应用了解详情。