为了重复使用 Fragment 界面组件,您应将每个组件构建为一个完全独立的模块化组件,定义它自己的布局和行为。定义这些可重用的 Fragment 后,您可以将它们与 Activity 相关联,并将其与应用逻辑相关联以实现整个复合界面。
您经常需要一个 Fragment 与另一个 Fragment 通信,比如为了根据用户事件更改内容。所有 Fragment 到 Fragment 的通信都是通过共享的 ViewModel
或关联的 Activity 来完成的。两个 Fragment 绝不能直接通信。
如需在 Fragment 之间通信,建议创建一个共享的 ViewModel
对象。两个 Fragment 都可以通过所在的 Activity 访问 ViewModel。Fragment 可在 ViewModel 内更新数据,如果使用 LiveData
公开该数据,新状态会被推送至其他 Fragment(只要它正在从 ViewModel 观察 LiveData)。要了解如何实现这种通信机制,请参阅 ViewModel 指南中的“在 Fragment 之间共享数据”部分。
如果无法使用共享的 ViewModel 在 Fragment 之间进行通信,可以使用接口手动实现通信机制。但是,这种方式最终需要实现更多的工作,并且无法在其他 Fragment 中轻松重用。
定义接口
要允许 Fragment 与其 Activity 进行通信,可以在 Fragment 类中定义接口并在 Activity 中实现该接口。Fragment 在其 onAttach() 生命周期方法中捕获接口实现,然后可以调用接口方法,以便与 Activity 通信。
以下是 Fragment 到 Activity 通信的示例:
HeadlinesFragment
Kotlin
class HeadlinesFragment : ListFragment() { internal var callback: OnHeadlineSelectedListener fun setOnHeadlineSelectedListener(callback: OnHeadlineSelectedListener) { this.callback = callback } // This interface can be implemented by the Activity, parent Fragment, // or a separate test implementation. interface OnHeadlineSelectedListener { fun onArticleSelected(position: Int) } // ... }
Java
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
Kotlin
class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener { // ... fun onAttachFragment(fragment: Fragment) { if (fragment is HeadlinesFragment) { fragment.setOnHeadlineSelectedListener(this) } } }
Java
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。
Kotlin
override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) { // Send the event to the host activity callback.onArticleSelected(position) }
Java
@Override public void onListItemClick(ListView l, View v, int position, long id) { // Send the event to the host activity callback.onArticleSelected(position); }
实现接口
要从 Fragment 接收事件回调,托管它的 Activity 必须实现 Fragment 类中定义的接口。
例如,以下 Activity 将实现上述示例中的接口。
Kotlin
class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener { ... fun onArticleSelected(position: Int) { // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article } }
Java
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,该 Fragment 用于显示由上述回调方法中返回的数据指定的项。在这种情况下,Activity 可以将回调方法中收到的信息传递给显示该项的另一个 Fragment:
Kotlin
class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener { ... fun onArticleSelected(position: Int) { // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article val articleFrag = supportFragmentManager.findFragmentById(R.id.article_fragment) as ArticleFragment? 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 val newFragment = ArticleFragment() val args = Bundle() args.putInt(ArticleFragment.ARG_POSITION, position) newFragment.arguments = args val transaction = supportFragmentManager.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() } } }
Java
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(); } } }