他のフラグメントとの通信

Fragment UI コンポーネントを再利用するには、各コンポーネントを、自身のレイアウトや動作を定義する自己完結型のモジュラー コンポーネントとしてビルドする必要があります。一度再利用可能な形で定義された Fragment は、Activity に関連付けたり、全体の複雑な UI を実現するためのアプリケーション ロジックへ接続したりできるようになります。

たとえば、ユーザー イベントに基づいてコンテンツを変える場合など、1 つの Fragment から他の Fragment を利用したい場合もあります。Fragment 間のすべての通信は、共有 ViewModel または関連する Activity のいずれかで行われます。2 つの Fragment が直接やりとりすることはありません。

フラグメント間での通信を行うための推奨される方法は、共有 ViewModel オブジェクトを作成する方法です。両方のフラグメントは、自分に含まれる Activity により ViewModel にアクセスできます。Fragment は ViewModel 内のデータをアップデートできます。データが LiveData 使用して公開されている場合、他のフラグメントが ViewModel からの LiveData を監視している限り、新しい状態が他のフラグメントにプッシュされます。この種の通信を実装するには、ViewModel ガイドの「Fragment 間でデータを共有する」をご覧ください。

Fragment 間の通信に共有 ViewModel を使用できない場合は、インターフェースを使用した通信フローを手動で実装する必要があります。しかし、この方法は実装のための工程が多く、他の Fragment で簡単に再利用できません。

インターフェースを定義する

Fragment が Activity とやりとりするには、Fragment クラス内でインターフェースを定義し、それを Activity 内に実装します。onAttach() ライフサイクル メソッド中に Fragment がインターフェースの実装をキャプチャし、Interface メソッドを呼び出して 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.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);
        }
    }
}

これで、フラグメントは OnHeadlineSelectedListener インターフェースの mCallback インスタンスを使用して、onArticleSelected() メソッド(またはインターフェース内の他のメソッド)を呼び出してアクティビティにメッセージを配信できるようになります。

たとえば、ユーザーがリストのアイテムをクリックしたとき、フラグメントの次のメソッドが呼び出されます。フラグメントはコールバック インターフェースを使用してイベントを親アクティビティに配信します。

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

インターフェースを実装する

フラグメントからイベントのコールバックを受信するには、それをホストするアクティビティは 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
    }
}

Fragment にメッセージを配信する

ホスト アクティビティは、findFragmentById() を使用して 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 をご覧ください。関連するサンプル アプリを調べることによっても、詳細が分かります。