Skip to content

Most visited

Recently visited

navigation

フラグメント

Fragment は、Activity でのユーザー インターフェースの挙動や部位を表すものです。 1 つのアクティビティに複数のフラグメントを組み合わせてマルチペインの UI をビルドしたり、複数のアクティビティでフラグメントを再利用したりできます。 フラグメントとは、アクティビティのモジュール型セクションのようなもので、独自のライフサイクルを持ち、独自の入力イベントを受信します。フラグメントはアクティビティの実行中に追加したり削除したりできます(別のアクティビティで再利用できる「サブ アクティビティ」のようなものです)。

フラグメントは常にアクティビティに埋め込まれている必要があり、フラグメントのライフサイクルはホストのアクティビティのライフサイクルの影響を直接受けます。 たとえば、アクティビティが一時停止しているとき、その中のすべてのフラグメントも一時停止し、アクティビティが破棄されると、すべてのフラグメントも破棄されます。 ただし、アクティビティの実行中(ライフサイクルで再開された状態)は、追加や削除といった操作は各フラグメントだけで行うことができます。 このようなフラグメントのトランザクションを実行するとき、アクティビティで管理されているバックスタックにそれを追加することもできます。 アクティビティの各バックスタック エントリは、発生したフラグメントのトランザクションの記録になります。 バックスタックでは、「戻る」を選択するとフラグメントのトランザクションを戻す(前に戻る)ことができます。

アクティビティのレイアウトの一部としてフラグメントを追加すると、フラグメントはアクティビティのビュー階層の ViewGroup に置かれ、フラグメントが自身のビュー レイアウトを定義します。フラグメントをアクティビティのレイアウトに挿入するには、アクティビティのレイアウト ファイルでフラグメントを <fragment> 要素として定義するか、アプリのコードで既存の ViewGroup に追加します。 ただし、フラグメントは必ずしもアクティビティの一部になる必要はなく、アクティビティの目に見えないワーカーとして、フラグメント独自の UI なしで使用することもできます。

このドキュメントでは、アクティビティのバックスタックに追加した時のフラグメントの状態を維持する方法、アクティビティ内でアクティビティと他のフラグメントとイベントを共有する方法、アクティビティのアクションバーへの影響など、フラグメントを使用してアプリケーションをビルドする方法について説明します。

デザインの指針

Android では、タブレットなどの大画面でのよりダイナミックで柔軟な UI デザインに対応するため、Android 3.0(API レベル 11)でフラグメントが採用されました。 タブレットの画面はハンドセットよりもかなり大きいため、UI コンポーネントを組み合わせたり入れ替えたりできる領域が広くなります。 フラグメントでは、ビュー階層に複雑な変更を加えることなく、そのようなデザインを実現できます。 アクティビティのレイアウトをフラグメントに分割することで、アクティビティの外観を実行時に変更でき、それらの変更をアクティビティが管理するバックスタックに保持できます。

たとえば新しいアプリケーションでは、1 つのフラグメントを使って左側に記事の一覧を表示したり、別のフラグメントを使って右側に記事の内容を表示したりできます。両方のフラグメントが 1 つのアクティビティに横並びに表示され、それぞれのフラグメントには自身のライフサイクル メソッドがあり、それぞれのユーザー入力イベントを処理します。 つまり、1 つのアクティビティで記事を選択して、別のアクティビティで記事を閲覧するのではなく、図 1 のタブレットのレイアウトのように 1 つのアクティビティ内で記事を選択して閲覧できるようになります。

各フラグメントはモジュール型の、再利用可能なアクティビティ コンポーネントとしてデザインする必要があります。各フラグメントは独自のライフサイクル コールバックを使用して自身のレイアウトと挙動とを定義するため、1 つのフラグメントを複数のアクティビティに含めることができます。このことから、再利用可能なデザインを用いることに加えて、1 つのフラグメントを他のフラグメントから直接操作しないようにする必要があります。 これは、モジュール型のフラグメントでは画面のサイズごとにフラグメントの組み合わせを変更できる点からも、特に重要です。 タブレットとハンドセットの両方に対応したアプリケーションをデザインする場合、異なるレイアウト構成でフラグメントを再利用することで、利用可能な画面の領域に応じて最適な使い心地を実現できます。 たとえばハンドセットの場合、同一のアクティビティ内に 2 つ以上のフラグメントが収まりきらないときは、フラグメントを分割してシングルペインの UI を提示する必要があることもあります。

図 1 1 つのアクティビティ内で、フラグメントで定義された 2 つの UI モジュールを組み合わせたタブレット用デザインと、それぞれを分けたハンドセット用デザインの例。

引き続きニュース アプリケーションの例を使うと、アプリケーションがタブレット サイズの端末で実行中は、 2 つのフラグメントをアクティビティ A に埋め込むことができます。 しかし、ハンドセット サイズの画面では両方のフラグメントを表示する領域が足りないため、アクティビティ A には記事の一覧のフラグメントだけが含まれます。記事を選択すると、記事を閲覧するための 2 つ目のフラグメントが含まれるアクティビティ B が開始されます。 そのため、図 1 のようにアプリケーションはフラグメントを異なる組み合わせで再利用することで、タブレットとハンドセットの両方に対応できるようになります。

異なる画面構成ごとに異なるフラグメントの組み合わせを用いたアプリケーションのデザインの詳細については、Supporting Tablets and Handsets のガイドをご覧ください。

フラグメントを作成する

図 2 フラグメントのライフサイクル(アクティビティの実行中)

フラグメントを作成するには、Fragment のサブクラス(またはその既存のサブクラス)を作成する必要があります。 Fragment クラスには、Activity に非常に似ているコードがあります。 これには、onCreate()onStart()onPause()onStop() のようなアクティビティと同様のコールバック メソッドが含まれています。 実際に、既存の Android アプリケーションを変換してフラグメントを使用するには、アクティビティのコールバック メソッドから、フラグメントの各コールバック メソッドにコードを移動させるだけで。

通常は、少なくとも次のライフサイクル メソッドを実装する必要があります。

onCreate()
フラグメントの作成時にシステムが呼び出します。実装内で、フラグメントが一時停止、停止、再開されたときに保持するフラグメントの必須コンポーネントを初期化する必要があります。
onCreateView()
フラグメントが初めてユーザー インターフェースを描画するタイミングでシステムがこれを呼び出します。 フラグメントの UI を描画するには、このメソッドからフラグメントのレイアウトのルートとなっている View を返す必要があります。 フラグメントが UI を提示しない場合は、null を返すことができます。
onPause()
ユーザーがフラグメントから離れたことを初めて示すときに、このメソッドを呼び出します(フラグメントが破棄されていない場合も含む)。 通常はここで、現在のユーザー セッション後も維持する必要のある変更点を保存しておきます(ユーザーが戻ってこない可能性があるため)。

ほとんどのアプリケーションでは、各フラグメントで少なくともこれら 3 つのメソッドを実装する必要がありますが、フラグメントのライフサイクルのさまざまなステージを処理する際に使用する他のコールバック メソッドもいくつかあります。 すべてのライフサイクル コールバック メソッドについては、フラグメントのライフサイクルの処理のセクションで説明します。

基本の Fragment クラスの代わりに拡張できるサブクラスもいくつかあります。

DialogFragment
フローティング ダイアログを表示します。Activity のダイアログ ヘルパー メソッド代わりにこのクラスを使用してダイアログを作成すると、アクティビティで管理されるフラグメントのバックスタックにフラグメント ダイアログを組み込むことができるため、ユーザーは終了したフラグメントに戻ることが可能になります。
ListFragment
ListActivity と同様に、アダプタで管理されるアイテムのリストを表示します(SimpleCursorAdapter など)。クリック イベントを処理するための onListItemClick() コールバックなど、リスト ビューを管理するメソッドがいくつか提供されます。
PreferenceFragment
PreferenceActivity と同様に、Preference オブジェクトの階層をリストとして表示します。 アプリケーションの「設定」アクティビティの作成時に便利です。

ユーザー インターフェースを追加する

通常、フラグメントはアクティビティのユーザー インターフェースの一部であり、独自のレイアウトをアクティビティに提示します。

フラグメントのレイアウトを提供するには、onCreateView() コールバック メソッドを実装する必要があります。これは、フラグメントがレイアウトを描画するタイミングで Android システムが呼び出します。 このメソッドの実装では、フラグメントのレイアウトのルートである View を返す必要があります。

注: フラグメントが ListFragment のサブクラスの場合、デフォルトの実装で ListViewonCreateView() から返されるため、これを実装する必要はありません。

onCreateView() からレイアウトを返すには、XML で定義したレイアウト リソースからインフレートできます。これを行うため、onCreateView() から LayoutInflater オブジェクトが提供されます。

たとえば、次の例では Fragment のサブクラスが example_fragment.xml ファイルからレイアウトを読み込みます。

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

onCreateView() に渡される container パラメータは、(アクティビティのレイアウトにより)フラグメントのレイアウトが挿入される親の ViewGroup になります。 savedInstanceState パラメータは、フラグメントが再開された場合にフラグメントの前のインスタンスに関する情報を提供する Bundle です(状態の復元の詳細については、フラグメントのライフサイクルの処理で説明します)。

inflate() メソッドは、次の 3 つの引数を受け取ります。

ここまで、レイアウトを提供するフラグメントの作成方法について説明しました。次は、フラグメントをアクティビティに追加する必要があります。

フラグメントをアクティビティに追加する

通常、フラグメントはホスト アクティビティに UI の一部を提供し、アクティビティの全体的なビュー階層の一部として埋め込まれます。 アクティビティのレイアウトにフラグメントを追加する方法は 2 つあります。

UI のないフラグメントを追加する

上の例では、UI を提供するためにフラグメントをアクティビティに追加する方法を紹介しましたが、UI を提示せずにアクティビティのバックグラウンド動作を提供するために、フラグメントを使うこともできます。

UI のないフラグメントを追加するには、add(Fragment, String) を使用してアクティビティからフラグメントを追加します(ビュー ID ではなくフラグメントの一意の文字列である「タグ」を提供します)。 これでフラグメントが追加されますが、アクティビティ レイアウトのビューには関連付けられていないため、onCreateView() への呼び出しは受け取りません。 そのため、このメソッドを実装する必要はありません。

フラグメントに文字列のタグを提供するのは UI のないフラグメントの場合だけではありません。UI のあるフラグメントにも文字列のタグを提供することはできますが、UI のないフラグメントにとっては、文字列のタグがフラグメントを識別する唯一の手段になります。 後でアクティビティからフラグメントを取得する場合は、 findFragmentByTag() を使用する必要があります。

たとえば、フラグメントを UI を持たないバックグラウンド ワーカーとして使用するアクティビティの場合は、SDK サンプルに含まれている(Android SDK Manager で利用可能)FragmentRetainInstance.java のサンプルでシステムに <sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java として置かれているものをご覧ください。

フラグメントを管理する

アクティビティのフラグメントを管理するには、FragmentManager を使用する必要があります。フラグメントを取得するには、アクティビティから getFragmentManager() を呼び出します。

FragmentManager では、次の操作が可能です。

これらのメソッドや他のメソッドの詳細については、FragmentManager クラスのドキュメントをご覧ください。

前のセクションで説明したように、FragmentManager を使用して FragmentTransaction を開くことで、フラグメントの追加や削除といったトランザクションを実行することもできます。

フラグメントのトランザクションを実行する

アクティビティでのフラグメントの使用における優れた機能として、フラグメントを追加、削除、置換したり、ユーザー操作への応答にフラグメントを使用して他のアクションを実行したりできる点が挙げられます。 アクティビティに加える変更はそれぞれ 1 つのトランザクションとして、FragmentTransaction の API を使用して実行できます。 各トランザクションはアクティビティが管理するバックスタックに保存でき、ユーザーはフラグメントの変更を元に戻すことができます(アクティビティ間で元に戻す動作と似ています)。

FragmentTransaction のインスタンスは、次のように FragmentManager から作成できます。

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

各トランザクションは、同時に実行する一連の変更点です。add()remove()replace() などのメソッドを使って、トランザクションで実行するすべての変更点をセットアップできます。 次に、トランザクションをアクティビティに適用するには、commit() を呼び出す必要があります。

ただし、commit() を呼び出す前に、addToBackStack() を呼び出して、フラグメントのトランザクションのバックスタックにトランザクションを追加することもできます。 このバックスタックはアクティビティによって管理され、ユーザーが 「戻る」ボタンを選択して前のフラグメントの状態に戻れるようにします。

例として、フラグメントを別のフラグメントに置き換えて、バックスタックで前の状態を保持する方法を次に示します。

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

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

// Commit the transaction
transaction.commit();

この例では、R.id.fragment_container ID で識別されるレイアウト コンテナに現在あるフラグメント(存在する場合)を newFragment に置き換えます。 addToBackStack() を呼び出すと、置き換えのトランザクションがバックスタックに保存されるため、ユーザーは「戻る」ボタンを選択することで、トランザクションを元に戻して前のフラグメントに戻れるようになります。

トランザクションに複数の変更を加えて(add()remove() など)、addToBackStack() を呼び出した場合、commit() を呼び出す前に適用されたすべての変更点が 1 つのトランザクションとしてバックスタックに追加され、「戻る」ボタンを選択すると、すべてが同時に元に戻るようになります。

次の場合を除き、FragmentTransaction に加える変更の順序は影響しません。

フラグメントを削除するトランザクションの実行時に addToBackStack() を呼び出さない場合、トランザクションの実行時にそのフラグメントは破棄され、ユーザーは操作を元に戻すことができなくなります。 一方で、フラグメントの削除時に addToBackStack() を呼び出した場合、フラグメントは停止状態になり、ユーザーが元に戻した時に再開します。

ヒント: それぞれのフラグメントのトランザクションでは、コミットする前に setTransition() と呼ばれる遷移のアニメーションを適用できます。

commit() を呼び出してもトランザクションはすぐには実行されません。 具体的には、アクティビティの UI スレッド(「メイン」スレッド)で実行可能になったときにすぐに実行されるようスケジュールされます。 ただし、必要であれば UI スレッドから executePendingTransactions() を呼び出して、commit() から送信されたトランザクションをすぐに実行することもできます。 通常、トランザクションが他のスレッドのジョブに依存していない限り、この操作は必要ありません。

警告: commit() を使用したトランザクションをコミットできるのは、アクティビティが状態を保存する前(ユーザーがアクティビティを離れるとき)にのみに限定されます。 それ以降にコミットしようとすると、例外がスローされます。 これは、アクティビティの復元が必要な場合に、コミット後のステータスが失われてしまう可能性があるためです。 コミットが失われてもよい場合は、commitAllowingStateLoss() を使用します。

アクティビティと通信する

FragmentActivity から独立したフラグメントとして実行されていて、複数のアクティビティ内で使用可能であっても、フラグメントの特定のインスタンスは、それが含まれるアクティビティに直接結び付いています。

具体的には、フラグメントは getActivity() を使用して Activity インスタンスにアクセスでき、アクティビティのレイアウトでビューを見つけるなどのタスクを簡単に実行できます。

View listView = getActivity().findViewById(R.id.list);

同様に、アクティビティが findFragmentById()findFragmentByTag() を使って FragmentManager から Fragment への参照を取得することで、フラグメント内のメソッドを呼び出すこともできます。次に例を示します。

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

アクティビティへのイベント コールバックを作成する

アクティビティとイベントを共有するフラグメントが必要になる場面もあります。これを実現するには、フラグメント内でコールバック インターフェースを定義して、ホスト アクティビティがそれを実装するよう要求します。 アクティビティがインターフェースを介してコールバックを受け取ると、必要に応じてレイアウト内の他のフラグメントと情報を共有します。

たとえば、新しいアプリケーションでアクティビティ内に、記事のリストを表示するフラグメント(フラグメント A)と、記事を表示するフラグメント(フラグメント B)の 2 つのフラグメントがある場合、リストのアイテムが選択されたときに、フラグメント B に記事を表示するよう伝えるため、フラグメント A から選択されたことをアクティビティに伝える必要があります。 ここでは、OnArticleSelectedListener インターフェースがフラグメント A で宣言されています。

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

次に、フラグメントのホストであるアクティビティが OnArticleSelectedListener インターフェースを実装して onArticleSelected() をオーバーライドし、フラグメント B にフラグメント A からのイベントを通知します。ホスト アクティビティがこのインターフェースを確実に実装するようにするため、フラグメント A の onAttach() コールバック メソッド(フラグメントをアクティビティに追加するときにシステムが呼び出すメソッド)が、onAttach() に渡される Activity をキャストして OnArticleSelectedListener のインスタンスを登録します。

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

アクティビティがインターフェースを実装していない場合は、フラグメントは ClassCastException をスローします。成功すると、mListener メンバーがアクティビティの OnArticleSelectedListener の実装への参照を保持でき、フラグメント A が OnArticleSelectedListener インターフェースで定義されたコールバック メソッドを呼び出すことでアクティビティとイベントを共有できるようになります。 たとえば、フラグメント A は ListFragment の拡張で、ユーザーがリストアイテムをクリックするたびに、システムがフラグメントの onListItemClick() を呼び出し、それが onArticleSelected() を呼び出してアクティビティとイベントを共有します。

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}

onListItemClick() に渡される id パラメータはクリックされたアイテムの行 ID で、アクティビティ(または他のフラグメント)がアプリの ContentProvider から記事を取得する際に使用します。

コンテンツ プロバイダの使用に関する詳細については、コンテンツ プロバイダのドキュメントをご覧ください。

アイテムをアプリバーに追加する

フラグメントは、onCreateOptionsMenu() を実装することでアクティビティのオプション メニュー(とそのアプリバー)にメニュー アイテムを提供することができます。 ただし、このメソッドが呼び出しを受け取るには、onCreate() の間に setHasOptionsMenu() を呼び出して、フラグメントがオプション メニューにアイテムを追加することを示す必要があります(これを行わない場合、フラグメントは onCreateOptionsMenu() への呼び出しを受け取りません)。

フラグメントから追加するアイテムはすべて、既存のメニュー アイテムに追加されます。 また、メニュー アイテムが選択されたとき、フラグメントは onOptionsItemSelected() へのコールバックも受け取ります。

また、registerForContextMenu() を呼び出して、フラグメントのレイアウトにビューを登録してコンテキスト メニューを提供することもできます。ユーザーがコンテキスト メニューを開くと、フラグメントは onCreateContextMenu() への呼び出しを受け取ります。 ユーザーがアイテムを選択すると、フラグメントは onContextItemSelected() への呼び出しを受け取ります。

注: 追加するメニュー アイテムごとにフラグメントは on-item-selected コールバックを受け取りますが、ユーザーがメニュー アイテムを選択したときに最初にそれぞれのコールバックを受け取るのはアクティビティになります。 アクティビティの on-item-selected コールバックの実装で、選択されたアイテムが処理されなかった場合、イベントはフラグメントのコールバックに渡されます。 この挙動は、オプション メニューとコンテキスト メニューの両方に適用されます。

メニューの詳細については、デベロッパー ガイドのメニューとトレーニング クラスのアプリバーをご覧ください。

フラグメントのライフサイクルを処理する

図 3 フラグメントのライフサイクルに対するアクティビティのライフサイクルの影響

フラグメントのライフサイクルの管理は、アクティビティのライフサイクルの管理によく似ています。アクティビティと同様に、フラグメントには次の 3 つの状態があります。

再開状態
フラグメントが実行中のアクティビティで表示されている。
一時停止状態
他のアクティビティがフォアグラウンドにあり、フォーカスがあるが、このアクティビティも表示されている(フォアグラウンドにある別のアクティビティは半透明になっているか、全画面をカバーしていない)。
停止状態
フラグメントは表示されていない。ホスト アクティビティが停止状態か、フラグメントがアクティビティから削除されたが、バックスタックに追加されている。 停止状態のフラグメントはまだ存続しています(すべての状態とメンバー情報がシステムで保持されています)。 ただし、アクティビティが破棄されるとユーザーには表示されなくなり、フラグメントも破棄されます。

さらに、アクティビティと同様に、アクティビティのプロセスが破棄されて、アクティビティが再作成されたときにフラグメントの状態を復元する必要がある場合は、Bundle を使用してフラグメントの状態を保持できます。 フラグメントの onSaveInstanceState() コールバックの間に状態を保存して、onCreate()onCreateView()onActivityCreated() の間にそれを復元できます。 状態の保存の詳細については、Activities のドキュメントをご覧ください。

アクティビティとフラグメントのライフサイクルの最も重要な違いは、バックスタックでのそれぞれの保存方法です。 アクティビティは、デフォルトで停止状態の時にシステムが管理するアクティビティのバックスタックに置かれますが(タスクとバックスタックで説明したようにユーザーが 「戻る」ボタンで前に戻ることができるように)、フラグメントの場合は、フラグメントを削除するトランザクションの間に addToBackStack() を呼び出してインスタンスを保存するよう明示的にリクエストした場合のみ、ホスト アクティビティが管理するバックスタックに置かれます。

これ以外については、フラグメントのライフサイクルの管理は、アクティビティのライフサイクルの管理とほとんど同じです。 そのため、アクティビティのライフサイクルの管理におけるベスト プラクティスがフラグメントにも適用されます。 もう 1 つ理解しておくべき事項は、アクティビティの寿命がどのようにフラグメントの寿命に影響を与えるかという点です。

警告: Fragment 内に Context オブジェクトが必要な場合は、getActivity() を呼び出すことができます。ただし、getActivity() はフラグメントがアクティビティにアタッチされている場合にのみ呼び出すようにしてください。 フラグメントがまだアタッチされていない場合や、ライフサイクルの終了時にデタッチされた場合は、getActivity() は null を返します。

アクティビティのライフサイクルと連携する

フラグメントが含まれるアクティビティのライフサイクルは、フラグメントのライフサイクルに直接影響し、アクティビティに対する各ライフサイクル コールバックは、各フラグメントに対して同様のコールバックをもたらします。 たとえば、アクティビティが onPause() を受け取ると、アクティビティ内の各フラグメントが onPause() を受け取ります。

ただし、フラグメントにはフラグメントの UI をビルドしたり破棄したりするアクションを実行する際のアクティビティとの独自のやり取りを処理する追加のライフサイクル コールバックがいくつかあります。 これらの追加のコールバック メソッドは次のとおりです。

onAttach()
フラグメントがアクティビティと関連付けられたときに呼び出されます(ここで Activity が渡されます)。
onCreateView()
フラグメントに関連付けられたビュー階層を作成する際に呼び出されます。
onActivityCreated()
アクティビティの onCreate() メソッドが戻ったときに呼び出されます。
onDestroyView()
フラグメントに関連付けられたビュー階層が削除されたときに呼び出されます。
onDetach()
フラグメントとアクティビティとの関連付けが解除されたときに呼び出されます。

図 3 は、ホスト アクティビティの影響を受けたフラグメントのライフサイクルのフローを表しています。この図から、アクティビティの一連の状態によって、フラグメントが受け取るコールバック メソッドがどのように決められているかがわかります。 たとえば、アクティビティが onCreate() コールバックを受け取ると、アクティビティ内のフラグメントは onActivityCreated() コールバックまでを受け取ります。

アクティビティが再開状態になると、アクティビティでのフラグメントの追加や削除を自由に行えます。 つまり、アクティビティが再開された状態が、フラグメントのライフサイクルを独立して変更できる唯一の期間になります。

ただし、アクティビティが再開状態でなくなると、フラグメントはアクティビティによって再度ライフサイクル間を通過することになります。

このドキュメントで解説した内容をすべて網羅するため、2 つのフラグメントを使用して 2 つのペインのレイアウトを作成するアクティビティの例を紹介します。 次のアクティビティには、シェイクスピア劇のタイトル リストを表示するフラグメントと、リストから劇が選択されたときに劇のあらすじを表示するフラグメントがあります。 また、ここでは画面構成によって異なるフラグメント構成を提供する方法も示しています。

注: このアクティビティのすべてのソースコードは、FragmentLayout.java で入手できます。

メイン アクティビティでは、通常の方法で onCreate() の間にレイアウトを適用します。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.fragment_layout);
}

適用されるレイアウトは fragment_layout.xml です。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent" />

    <FrameLayout android:id="@+id/details" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent"
            android:background="?android:attr/detailsElementBackground" />

</LinearLayout>

このレイアウトを使って、アクティビティがレイアウトを読み込むとすぐにシステムが TitlesFragment(これが劇のタイトルをリストします)のインスタンスを作成します。このとき、FrameLayout(劇のあらすじを表示するフラグメントの配置場所)が画面右側を使用し、残りは空白の状態です。 ご覧のように、フラグメントが FrameLayout に置かれるのはユーザーがアイテムを選択してからになります。

ただし、画面構成によっては、劇の一覧とあらすじを横並びに表示するほどの幅がない場合もあります。 そのため、上記のレイアウトを res/layout-land/fragment_layout.xml に保存して、横方向の画面構成の場合のみ使用されるようにします。

画面が縦方向の場合、システムは res/layout/fragment_layout.xml に保存された次のレイアウトを適用します。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles"
            android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

このレイアウトには、TitlesFragment のみが含まれます。つまり、端末が縦方向の場合、劇のタイトルのみが表示されます。 そのため、この構成時にユーザーがリストのアイテムをクリックしたとき、アプリケーションは 2 つ目のフラグメントをロードする代わりに新しいアクティビティを開始してあらすじを表示します。

次に、フラグメントのクラスでこれをどう実現するのかを見ていきます。まず、TitlesFragment はシェイクスピア劇のタイトル リストを表示します。このフラグメントは ListFragment を拡張し、リスト ビューの動作の処理のほとんどを委ねます。

このコードをよく見ると、ユーザーがリストのアイテムをクリックしたときの挙動が 2 つ考えられます。2 つのレイアウトのどちらがアクティブになっているかによって、新しいフラグメントを作成して表示することで同じアクティビティで詳細を表示するか(FrameLayout にフラグメントを追加する)、新しいアクティビティを(フラグメントを表示できる場所に)開始するかのいずれかになります。

public static class TitlesFragment extends ListFragment {
    boolean mDualPane;
    int mCurCheckPosition = 0;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Populate list with our static array of titles.
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

        // Check to see if we have a frame in which to embed the details
        // fragment directly in the containing UI.
        View detailsFrame = getActivity().findViewById(R.id.details);
        mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;

        if (savedInstanceState != null) {
            // Restore last state for checked position.
            mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
        }

        if (mDualPane) {
            // In dual-pane mode, the list view highlights the selected item.
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            // Make sure our UI is in the correct state.
            showDetails(mCurCheckPosition);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("curChoice", mCurCheckPosition);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails(position);
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    void showDetails(int index) {
        mCurCheckPosition = index;

        if (mDualPane) {
            // We can display everything in-place with fragments, so update
            // the list to highlight the selected item and show the data.
            getListView().setItemChecked(index, true);

            // Check what fragment is currently shown, replace if needed.
            DetailsFragment details = (DetailsFragment)
                    getFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index);

                // Execute a transaction, replacing any existing fragment
                // with this one inside the frame.
                FragmentTransaction ft = getFragmentManager().beginTransaction();
                if (index == 0) {
                    ft.replace(R.id.details, details);
                } else {
                    ft.replace(R.id.a_item, details);
                }
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft.commit();
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            Intent intent = new Intent();
            intent.setClass(getActivity(), DetailsActivity.class);
            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}

2 つ目のフラグメントである DetailsFragment は、TitlesFragment のリストで選択された劇のあらすじを表示します。

public static class DetailsFragment extends Fragment {
    /**
     * Create a new instance of DetailsFragment, initialized to
     * show the text at 'index'.
     */
    public static DetailsFragment newInstance(int index) {
        DetailsFragment f = new DetailsFragment();

        // Supply index input as an argument.
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);

        return f;
    }

    public int getShownIndex() {
        return getArguments().getInt("index", 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            // We have different layouts, and in one of them this
            // fragment's containing frame doesn't exist.  The fragment
            // may still be created from its saved state, but there is
            // no reason to try to create its view hierarchy because it
            // won't be displayed.  Note this is not needed -- we could
            // just run the code below, where we would create and return
            // the view hierarchy; it would just never be used.
            return null;
        }

        ScrollView scroller = new ScrollView(getActivity());
        TextView text = new TextView(getActivity());
        int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                4, getActivity().getResources().getDisplayMetrics());
        text.setPadding(padding, padding, padding, padding);
        scroller.addView(text);
        text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
        return scroller;
    }
}

TitlesFragment クラスで説明したように、ユーザーがリストアイテムをクリックしたときに、現在のレイアウトに R.id.details ビュー(DetailsFragment が属する場所)が含まれていない場合、アプリは DetailsActivity のアクティビティを開始してアイテムのコンテンツを表示します。

次の DetailsActivity では、画面が縦方向のときに単純に DetailsFragment を埋め込んで選択された劇のあらすじを表示します。

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}

構成が横方向の場合はアクティビティは自ら終了するため、メイン アクティビティが引き継いで DetailsFragmentTitlesFragment の横に表示できます。これは、ユーザーが縦方向のときに DetailsActivity を開始し、その後横方向に回転した(ここで現在のアクティビティが再開される)ときに起こることがあります。

フラグメントを使用したその他のサンプルとこの例の完全なソース ファイルについては、ApiDemosSDK コンポーネントのサンプルから入手可能)にある API デモサンプルをご覧ください。

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)