フラグメント Part of Android Jetpack.
Fragment
は、FragmentActivity
でのユーザー インターフェースの挙動や部位を表すものです。1 つのアクティビティに複数のフラグメントを組み合わせてマルチペイン UI をビルドしたり、複数のアクティビティでフラグメントを再利用したりできます。フラグメントとは、アクティビティのモジュラー セクションのようなもので、独自のライフサイクルを持ち、独自の入力イベントを受信します。フラグメントはアクティビティの実行中に追加したり削除したりできます(別のアクティビティで再利用できる「サブ アクティビティ」のようなものです)。
フラグメントは常にアクティビティでホストされている必要があり、フラグメントのライフサイクルはホストのアクティビティのライフサイクルの影響を直接受けます。たとえば、アクティビティが一時停止しているとき、その中のすべてのフラグメントも一時停止し、アクティビティが破棄されると、すべてのフラグメントも破棄されます。ただし、アクティビティの実行中(ライフサイクルで再開された状態)は、追加や削除といった操作を各フラグメントで独立して行うことができます。このようなフラグメントのトランザクションを実行するとき、アクティビティで管理されているバックスタックにそれを追加することもできます。アクティビティの各バックスタック エントリは、発生したフラグメントのトランザクションの記録です。バックスタックでは、[戻る] ボタンを押すとフラグメントのトランザクションを戻す(前に戻る)ことができます。
アクティビティ レイアウトの一部としてフラグメントを追加すると、それはアクティビティのビュー階層内の ViewGroup
に配置され、そのフラグメントによって独自のビュー レイアウトが定義されます。フラグメントをアクティビティ レイアウトに挿入するには、アクティビティのレイアウト ファイルで <fragment>
要素としてフラグメントを宣言するか、またはアプリケーション コードから既存の ViewGroup
にフラグメントを追加します。
このドキュメントでは、アクティビティのバックスタックに追加したときにフラグメントの状態を維持する方法、アクティビティ内でアクティビティや他のフラグメントとイベントを共有する方法、アクティビティのアプリバーへの影響など、フラグメントを使用してアプリケーションをビルドする方法について説明します。
ライフサイクルの処理に関する情報は、ベスト プラクティスに関するガイダンスも含めて、以下のリソースをご覧ください。
デザインの指針
Android では、タブレットなどの大画面でのよりダイナミックで柔軟な UI デザインに対応するため、Android 3.0(API レベル 11)でフラグメントが採用されました。タブレットの画面はハンドセットよりもかなり大きいため、UI コンポーネントを組み合わせたり入れ替えたりできる領域が広くなります。フラグメントでは、ビュー階層に複雑な変更を加えることなく、そのようなデザインを実現できます。アクティビティのレイアウトをフラグメントに分割することで、アクティビティの外観を実行時に変更でき、それらの変更をアクティビティが管理するバックスタックに保持できます。それらのフラグメントは、フラグメント サポート ライブラリを使用して広く利用できるようになりました。
たとえばニュース アプリケーションでは、1 つのフラグメントを使って左側に記事の一覧を表示し、別のフラグメントを使って右側に記事の内容を表示できます。両方のフラグメントが 1 つのアクティビティに横並びに表示され、それぞれのフラグメントには自身のライフサイクル コールバック メソッドがあり、それぞれのユーザー入力イベントを処理します。つまり、1 つのアクティビティで記事を選択して、別のアクティビティで記事を閲覧するのではなく、図 1 のタブレットのレイアウトのように 1 つのアクティビティ内で記事を選択して閲覧できるようになります。
各フラグメントはモジュール型の、再利用可能なアクティビティ コンポーネントとしてデザインする必要があります。各フラグメントは独自のライフサイクル コールバックを使用して自身のレイアウトと挙動とを定義するため、1 つのフラグメントを複数のアクティビティに含めることができます。このことから、再利用可能なデザインを用いることに加えて、1 つのフラグメントを他のフラグメントから直接操作しないようにする必要があります。これは、モジュール型のフラグメントでは画面のサイズごとにフラグメントの組み合わせを変更できる点からも、特に重要です。タブレットとハンドセットの両方に対応したアプリケーションをデザインする場合、異なるレイアウト構成でフラグメントを再利用することで、利用可能な画面の領域に応じて最適な使い心地を実現できます。たとえばハンドセットの場合、同一のアクティビティ内に 2 つ以上のフラグメントが収まりきらないときは、フラグメントを分割してシングルペインの UI を提示する必要があることもあります。

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

図 2 フラグメントのライフサイクル(アクティビティの実行中)
フラグメントを作成するには、Fragment
(またはその既存のサブクラス)のサブクラスを作成する必要があります。Fragment
クラスには、Activity
に非常に似ているコードがあります。これには、onCreate()
、onStart()
、onPause()
、onStop()
のようなアクティビティと同様のコールバック メソッドが含まれています。実際に、既存の Android アプリケーションをフラグメントを使用するように変換するには、アクティビティのコールバック メソッドから、フラグメントの各コールバック メソッドにコードを移動させるだけで済みます。
通常は、少なくとも次のライフサイクル メソッドを実装する必要があります。
onCreate()
- フラグメントの作成時にシステムが呼び出します。実装内で、フラグメントが一時停止、停止して、再開されたときに保持するフラグメントの必須コンポーネントを初期化する必要があります。
onCreateView()
- フラグメントが初めてユーザー インターフェースを描画するタイミングでシステムがこれを呼び出します。フラグメントの UI を描画するには、このメソッドからフラグメントのレイアウトのルートとなっている
View
を返す必要があります。null を返せば、フラグメントが UI を提示しないようにできます。 onPause()
- システムは、ユーザーがフラグメントから離れたことを初めて示すものとして、このメソッドを呼び出します(フラグメントが破棄されようとしていない場合も含む)。通常はここで、現在のユーザー セッション後も維持する必要のある変更点を保存しておきます(ユーザーが戻ってこない可能性があるため)。
ほとんどのアプリケーションでは、各フラグメントで少なくともこれら 3 つのメソッドを実装する必要がありますが、フラグメントのライフサイクルのさまざまなステージを処理する際に使用する他のコールバック メソッドもいくつかあります。すべてのライフサイクル コールバック メソッドについては、フラグメントのライフサイクルを処理するのセクションで説明します。
なお、従属コンポーネントのライフサイクル アクションを実装するコードは、フラグメントのコールバック実装の中に直接配置するのではなく、コンポーネントそのものの中に置く必要があることにご注意ください。従属コンポーネントをライフサイクル対応にする方法については、ライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。
基本の Fragment
クラスの代わりに拡張できるサブクラスもいくつかあります。
DialogFragment
- フローティング ダイアログを表示します。
Activity
でダイアログ ヘルパー メソッドの代わりにこのクラスを使用してダイアログを作成すると、アクティビティで管理されるフラグメントのバックスタックにフラグメント ダイアログを組み込むことができるため、ユーザーは終了したフラグメントに戻ることが可能になります。 ListFragment
ListActivity
と同様に、アダプタ(SimpleCursorAdapter
など)で管理されるアイテム リストを表示します。クリック イベントを処理するためのonListItemClick()
コールバックなど、リストビューを管理するメソッドがいくつか提供されます。(リストを表示する方法としては、ListView ではなく RecyclerView を使用することをお勧めします。この場合は、レイアウトに RecyclerView を含めたフラグメントを作成する必要があります。その方法については、RecyclerView によるリストの作成をご覧ください。)PreferenceFragmentCompat
Preference
オブジェクトの階層をリストとして表示します。これは、アプリケーションの設定画面を作成するために使用します。
ユーザー インターフェースを追加する
通常、フラグメントはアクティビティのユーザー インターフェースの一部であり、独自のレイアウトをアクティビティで使用できるようにします。
フラグメントのレイアウトを提供するには、onCreateView()
コールバック メソッドを実装する必要があります。これは、フラグメントがレイアウトを描画するタイミングで Android システムが呼び出します。このメソッドの実装では、フラグメントのレイアウトのルートである View
を返す必要があります。
注:フラグメントが ListFragment
のサブクラスの場合、デフォルトの実装で ListView
がonCreateView()
から返されるため、これを実装する必要はありません。
onCreateView()
からレイアウトを返すには、XML で定義したレイアウト リソースからインフレートできます。これを行うため、onCreateView()
から LayoutInflater
オブジェクトが提供されます。
たとえば、次の例では Fragment
のサブクラスが example_fragment.xml
ファイルからレイアウトを読み込みます。
Kotlin
class ExampleFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false) } }
Java
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); } }
注:前の例では、R.layout.example_fragment
はアプリリソースに保存されている example_fragment.xml
という名前のレイアウト リソースへの参照です。XML でレイアウトを作成する方法の詳細については、ユーザー インターフェースのドキュメントをご覧ください。
onCreateView()
に渡される container
パラメータは、フラグメントのレイアウトが(アクティビティのレイアウトから)挿入される親 ViewGroup
です。savedInstanceState
パラメータは、フラグメントが再開された場合にフラグメントの前のインスタンスに関する情報を提供する Bundle
です(状態の復元の詳細については、フラグメントのライフサイクルを処理するで説明します)。
inflate()
メソッドは、次の 3 つの引数を受け取ります。
- インフレートするレイアウトのリソース ID。
- インフレートされたレイアウトの親となる
ViewGroup
。システムがインフレートされたレイアウトのルートビュー(親ビューが指定)にレイアウト パラメータ適用するには、container
を渡すことが重要です。 - インフレート中に、インフレートされたレイアウトを
ViewGroup
(2 つ目のパラメータ)にアタッチすべきかどうかを示すブール値(この場合、システムがインフレートされたレイアウトをcontainer
に既に挿入しているため、false にします。true を渡すと、最終レイアウトに余分なビューグループが作成されます)。
ここまで、レイアウトを提供するフラグメントの作成方法について説明しました。次は、フラグメントをアクティビティに追加する必要があります。
フラグメントをアクティビティに追加する
通常、フラグメントはホスト アクティビティに UI の一部を提供し、アクティビティの全体的なビュー階層の一部として埋め込まれます。アクティビティのレイアウトにフラグメントを追加する方法は 2 つあります。
- アクティビティのレイアウト ファイル内でフラグメントを宣言する
この場合、フラグメントがビューであるかのようにレイアウト プロパティを指定できます。以下は、2 つのフラグメントを持つアクティビティのレイアウト ファイルです。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
<fragment>
のandroid:name
属性では、レイアウト内でインスタンス化するFragment
クラスを指定します。システムはこのアクティビティ レイアウトを作成するとき、レイアウトで指定された各フラグメントをインスタンス化し、それぞれの
onCreateView()
メソッドを呼び出して、各フラグメントのレイアウトを取得します。システムはフラグメントから返されたView
を<fragment>
要素の代わりに直接挿入します。注:各フラグメントには、アクティビティの再開時にフラグメントを復元するためにシステムが使用できる(そしてフラグメントをキャプチャして削除などのトランザクションを実行する際に使用できる)一意の識別子が必要です。フラグメントの ID を提供するには、次の 2 つの方法があります。
android:id
属性に一意の ID を提供する。android:tag
属性に一意の文字列を提供する。
- または、既存の
ViewGroup
にプログラムを使用してフラグメントを追加します。アクティビティの実行中は、いつでもフラグメントをアクティビティ レイアウトに追加できます。必要なのは、フラグメントを配置する場所に
ViewGroup
を指定することだけです。アクティビティ内でフラグメントのトランザクション(フラグメントの追加、削除、置換など)を実行するには、
FragmentTransaction
から API を使用する必要があります。FragmentTransaction
のインスタンスは、FragmentActivity
から次のようにして取得できます。Kotlin
val fragmentManager = supportFragmentManager val fragmentTransaction = fragmentManager.beginTransaction()
Java
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
次に、
add()
メソッドを使用して、追加するフラグメントと挿入するビューを指定してフラグメントを追加できます。次に例を示します。Kotlin
val fragment = ExampleFragment() fragmentTransaction.add(R.id.fragment_container, fragment) fragmentTransaction.commit()
Java
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
add()
に渡される最初の引数はフラグメントを配置するViewGroup
で、リソース ID で指定されており、2 つ目のパラメータは追加するフラグメントです。FragmentTransaction
で変更を加えたら、commit()
を呼び出して変更を適用する必要があります。
フラグメントを管理する
アクティビティのフラグメントを管理するには、FragmentManager
を使用する必要があります。それを取得するには、アクティビティから getSupportFragmentManager()
を呼び出します。
FragmentManager
では、次の操作が可能です。
findFragmentById()
(アクティビティ レイアウトで UI を提供するフラグメントの場合)やfindFragmentByTag()
(UI を提供する、または提供しないフラグメントの場合)を使用して、アクティビティにあるフラグメントを取得する。popBackStack()
を使用してフラグメントをバックスタックから取り出す(ユーザーによる「戻る」コマンドをシミュレートする)。addOnBackStackChangedListener()
を使用して、バックスタックの変更に対するリスナーを登録する。
これらのメソッドや他のメソッドの詳細については、FragmentManager
クラスのドキュメントをご覧ください。
前のセクションで説明したように、FragmentManager
を使用して FragmentTransaction
を開くことで、フラグメントの追加や削除といったトランザクションを実行することもできます。
フラグメントのトランザクションを実行する
アクティビティでフラグメントを使用すると、ユーザー操作への応答として、フラグメントを追加、削除、置換したり、フラグメントに対して他のアクションを実行したりできます。アクティビティに加える変更のことをそれぞれトランザクションといいます。トランザクションは、FragmentTransaction
の API を使用して実行できます。各トランザクションはアクティビティが管理するバックスタックに保存でき、ユーザーはフラグメントの変更を元に戻すことができます(アクティビティ間で元に戻す動作と似ています)。
FragmentTransaction
のインスタンスは、次のように FragmentManager
から取得できます。
Kotlin
val fragmentManager = supportFragmentManager val fragmentTransaction = fragmentManager.beginTransaction()
Java
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
各トランザクションは、同時に実行する一連の変更点です。add()
、remove()
、replace()
などのメソッドを使って、特定のトランザクションで実行するすべての変更点をセットアップできます。次に、トランザクションをアクティビティに適用するために、commit()
を呼び出す必要があります。
ただし、commit()
を呼び出す前に、addToBackStack()
を呼び出して、フラグメントのトランザクションのバックスタックにトランザクションを追加することもできます。このバックスタックはアクティビティによって管理され、ユーザーが [戻る] ボタンを選択して前のフラグメントの状態に戻れるようにします。
例として、フラグメントを別のフラグメントに置き換えて、バックスタックに前の状態を保持する方法を次に示します。
Kotlin
val newFragment = ExampleFragment() val transaction = supportFragmentManager.beginTransaction() transaction.replace(R.id.fragment_container, newFragment) transaction.addToBackStack(null) transaction.commit()
Java
// Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getSupportFragmentManager().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()
を呼び出すと、置き換えのトランザクションがバックスタックに保存されるため、ユーザーは [戻る] ボタンを押すことで、トランザクションを元に戻して前のフラグメントに戻れるようになります。
すると、FragmentActivity
が onBackPressed()
を使用してバックスタックからフラグメントを自動的に取得します。
トランザクションに複数の変更を加えて(別の add()
や remove()
など)から addToBackStack()
を呼び出した場合、commit()
を呼び出す前に適用されたすべての変更点が 1 つのトランザクションとしてバックスタックに追加され、[戻る] ボタンを押すとすべてが同時に元に戻るようになります。
次の点を除き、FragmentTransaction
に加える変更の順序は影響しません。
commit()
は最後に呼び出す必要があります。- 複数のフラグメントを同じコンテナに追加する場合、追加する順序がビュー階層に表示される順序になります。
フラグメントを削除するトランザクションの実行時に addToBackStack()
を呼び出さない場合、トランザクションの実行時にそのフラグメントは破棄され、ユーザーはそのフラグメントに戻ることができなくなります。それに対して、フラグメントの削除時に addToBackStack()
を呼び出した場合、フラグメントは停止状態になり、ユーザーが元に戻したときに再開します。
ヒント: フラグメントの各トランザクションでは、コミットする前に setTransition()
を呼び出して、遷移のアニメーションを適用できます。
commit()
を呼び出しても、トランザクションはすぐには実行されません。具体的には、アクティビティの UI スレッド(「メイン」スレッド)で実行可能になったときにすぐに実行されるようスケジュールされます。ただし、必要であれば UI スレッドから executePendingTransactions()
を呼び出して、commit()
から送信されたトランザクションをすぐに実行することもできます。通常、トランザクションが他のスレッドのジョブに依存していない限り、この操作は必要ありません。
注意:commit()
を使用してトランザクションをコミットできるのは、(ユーザーがアクティビティを離れるときに)アクティビティが状態を保存する前だけです。それより後にコミットしようとすると、例外がスローされます。これは、アクティビティの復元が必要な場合に、コミット後のステータスが失われてしまう可能性があるためです。コミットが失われてもよい場合は、commitAllowingStateLoss()
を使用します。
アクティビティと通信する
Fragment
は FragmentActivity
から独立したオブジェクトとして実装され、複数のアクティビティ内で使用可能ですが、フラグメントの特定のインスタンスは、ホストされているアクティビティに直接結び付いています。
具体的には、フラグメントは getActivity()
を使用して FragmentActivity
インスタンスにアクセスでき、アクティビティのレイアウトでビューを見つけるなどのタスクを簡単に実行できます。
Kotlin
val listView: View? = activity?.findViewById
(R.id.list)
Java
View listView = getActivity().findViewById
(R.id.list);
同様に、アクティビティが findFragmentById()
や findFragmentByTag()
を使って FragmentManager
からFragment
への参照を取得することで、フラグメント内のメソッドを呼び出すこともできます。次に例を示します。
Kotlin
val fragment = supportFragmentManager.findFragmentById(R.id.example_fragment) as ExampleFragment
Java
ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.example_fragment);
アクティビティへのイベント コールバックを作成する
場合によっては、フラグメントで、アクティビティやアクティビティによってホストされた他のフラグメントとイベントまたはデータを共有する必要が生じることがあります。データを共有するには、ViewModel ガイドにあるフラグメント間でのデータ共有のセクションで概説されているように、共有された ViewModel を作成します。ViewModel では処理できないイベントを伝播する必要がある場合は、代わりにフラグメント内にコールバック インターフェースを定義することができますが、それを実装するホスト アクティビティが必要になります。アクティビティがインターフェースを介してコールバックを受け取ると、必要に応じてレイアウト内の他のフラグメントと情報を共有します。
たとえば、新しいアプリケーションでアクティビティ内に、記事のリストを表示するフラグメント(フラグメント A)と、記事を表示するフラグメント(フラグメント B)の 2 つのフラグメントがある場合、リストのアイテムが選択されたときに、フラグメント B に記事を表示するよう伝えるため、選択されたことをフラグメント A からアクティビティに伝える必要があります。ここでは、OnArticleSelectedListener
インターフェースがフラグメント A で宣言されています。
Kotlin
public class FragmentA : ListFragment() { ... // Container Activity must implement this interface interface OnArticleSelectedListener { fun onArticleSelected(articleUri: Uri) } ... }
Java
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
をインスタンス化します。
Kotlin
public class FragmentA : ListFragment() { var listener: OnArticleSelectedListener? = null ... override fun onAttach(context: Context) { super.onAttach(context) listener = context as? OnArticleSelectedListener if (listener == null) { throw ClassCastException("$context must implement OnArticleSelectedListener") } } ... }
Java
public static class FragmentA extends ListFragment { OnArticleSelectedListener listener; ... @Override public void onAttach(Context context) { super.onAttach(context); try { listener = (OnArticleSelectedListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener"); } } ... }
アクティビティがインターフェースを実装していない場合、フラグメントは ClassCastException
をスローします。成功すると、mListener
メンバーがアクティビティの OnArticleSelectedListener
の実装への参照を保持し、フラグメント A が OnArticleSelectedListener
インターフェースで定義されたコールバック メソッドを呼び出すことでイベントをアクティビティと共有できるようになります。たとえば、フラグメント A が ListFragment
を拡張したものである場合、ユーザーがリストアイテムをクリックするたびにシステムはフラグメントの onListItemClick()
を呼び出し、それが onArticleSelected()
を呼び出してイベントをアクティビティと共有します。
Kotlin
public class FragmentA : ListFragment() {
var listener: OnArticleSelectedListener? = null
...
override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
// Append the clicked item's row ID with the content provider Uri
val noteUri: Uri = ContentUris.withAppendedId
(ArticleColumns.CONTENT_URI, id)
// Send the event and Uri to the host activity
listener?.onArticleSelected(noteUri)
}
...
}
Java
public static class FragmentA extends ListFragment {
OnArticleSelectedListener listener;
...
@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
listener.onArticleSelected(noteUri);
}
...
}
onListItemClick()
に渡される id
パラメータはクリックされたアイテムの行 ID で、アクティビティ(または他のフラグメント)がアプリの ContentProvider
から記事を取得する際に使用します。
コンテンツ プロバイダの使用に関する詳細については、コンテンツ プロバイダのドキュメントをご覧ください。
アイテムをアプリバーに追加する
フラグメントは、onCreateOptionsMenu()
を実装することでアクティビティのオプション メニュー(とそのアプリバー)にメニュー アイテムを提供することができます。ただし、このメソッドが呼び出しを受け取るには、onCreate()
の間に setHasOptionsMenu()
を呼び出して、フラグメントがオプション メニューにアイテムを追加することを示す必要があります。これを行わない場合、フラグメントは onCreateOptionsMenu()
への呼び出しを受け取りません。
フラグメントからオプション メニューに追加する項目はすべて、既存のメニュー アイテムに追加されます。また、メニュー アイテムが選択されたとき、フラグメントは onOptionsItemSelected()
へのコールバックも受け取ります。
また、registerForContextMenu()
を呼び出して、フラグメントのレイアウトにビューを登録してコンテキスト メニューを提供することもできます。ユーザーがコンテキスト メニューを開くと、フラグメントは onCreateContextMenu()
への呼び出しを受け取ります。ユーザーが項目を選択すると、フラグメントは onContextItemSelected()
への呼び出しを受け取ります。
注:追加するメニュー アイテムごとにフラグメントは on-item-selected コールバックを受け取りますが、アクティビティが最初にそれぞれのコールバックを受け取るのはユーザーがメニュー アイテムを選択したときになります。アクティビティの on-item-selected コールバックの実装で、選択されたアイテムが処理されなかった場合、イベントはフラグメントのコールバックに渡されます。この挙動は、オプション メニューとコンテキスト メニューの両方に適用されます。
メニューの詳細については、デベロッパー ガイドのメニューとトレーニング クラスのアプリバーをご覧ください。
フラグメントのライフサイクルを処理する

図 3 フラグメントのライフサイクルに対するアクティビティのライフサイクルの影響
フラグメントのライフサイクルの管理は、アクティビティのライフサイクルの管理によく似ています。アクティビティと同様に、フラグメントには次の 3 つの状態があります。
- 再開状態
- フラグメントが実行中のアクティビティで表示されている。
- 一時停止状態
- 他のアクティビティがフォアグラウンドにあってフォーカスがあるが、このアクティビティも表示されている(フォアグラウンドにある別のアクティビティは半透明になっているか、全画面をカバーしていない)。
- 停止状態
- フラグメントは表示されていない。ホスト アクティビティが停止状態か、フラグメントがアクティビティから削除されたが、バックスタックに追加されている。停止状態のフラグメントはまだ存続しています(すべての状態とメンバー情報がシステムで保持されています)。ただし、ユーザーには表示されなくなっており、アクティビティが破棄されるとフラグメントも破棄されます。
また、アクティビティと同様に、onSaveInstanceState(Bundle)、ViewModel、および永続的なローカル ストレージの組み合わせを使用して、構成の変更とプロセスの終了をまたいでフラグメントの UI の状態を保持することができます。UI の状態に保持に関する詳細は、UI の状態を保存するをご覧ください。
アクティビティとフラグメントのライフサイクルの最も重要な違いは、バックスタックでのそれぞれの保存方法です。デフォルトではアクティビティは、停止した時点で、システムによって管理されているアクティビティのバックスタックに置かれます(これにより、タスクとバックスタックで説明されているように、ユーザーは [戻る] ボタンで元に戻ることができます)。それに対して、フラグメントは、フラグメントを削除するトランザクション中に addToBackStack()
を呼び出してインスタンスを保存することを明示的にリクエストした場合にのみ、ホスト アクティビティによって管理されているバックスタックに置かれます。
これ以外については、フラグメントのライフサイクルの管理は、アクティビティのライフサイクルの管理とほとんど同じで、同じ習慣が適用されます。アクティビティのライフサイクルとそれを管理する方法の詳細については、アクティビティのライフサイクルガイドとライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。
注意:Fragment
内の Context
オブジェクトが必要な場合は、getContext()
を呼び出すことができます。ただし、getContext()
は、フラグメントがアクティビティにアタッチされている場合にのみ呼び出すようにしてください。フラグメントがまだアタッチされていない場合、またはライフサイクルの終わりにデタッチされた場合は、getContext()
は null を返します。
アクティビティのライフサイクルと連携する
フラグメントが含まれるアクティビティのライフサイクルは、フラグメントのライフサイクルに直接影響し、アクティビティに対する各ライフサイクル コールバックは、各フラグメントに対して同様のコールバックをもたらします。たとえば、アクティビティが onPause()
を受け取ると、アクティビティ内の各フラグメントが onPause()
を受け取ります。
ただし、フラグメントには、フラグメントの UI をビルドしたり破棄したりするアクションを実行する際のアクティビティとの独自のやり取りを処理する追加のライフサイクル コールバックがいくつかあります。これらの追加のコールバック メソッドは次のとおりです。
onAttach()
- フラグメントがアクティビティと関連付けられたときに呼び出されます(ここで
Activity
が渡されます)。 onCreateView()
- フラグメントに関連付けられたビュー階層を作成する際に呼び出されます。
onActivityCreated()
- アクティビティの
onCreate()
メソッドから戻ったときに呼び出されます。 onDestroyView()
- フラグメントに関連付けられたビュー階層が削除されたときに呼び出されます。
onDetach()
- フラグメントとアクティビティとの関連付けが解除されたときに呼び出されます。
図 3 は、ホスト アクティビティに対応するフラグメントのライフサイクルのフローを表しています。この図から、アクティビティの一連の状態によって、フラグメントが受け取るコールバック メソッドがどのように決まるかがわかります。たとえば、アクティビティが onCreate()
コールバックを受け取ると、アクティビティ内のフラグメントは onActivityCreated()
コールバックまでを受け取ります。
アクティビティが再開状態になると、アクティビティでのフラグメントの追加や削除を自由に行えます。つまり、アクティビティが再開された状態は、フラグメントのライフサイクルを独立して変更できる唯一の期間です。
ただし、アクティビティが再開状態でなくなると、フラグメントはアクティビティによって再度ライフサイクル間を通過することになります。
例
このドキュメントで解説した内容をすべて網羅するため、2 つのフラグメントを使用して 2 つのペインのレイアウトを作成するアクティビティの例を紹介します。次のアクティビティには、シェイクスピア劇のタイトル リストを表示するフラグメントと、リストから劇が選択されたときに劇のあらすじを表示するフラグメントがあります。また、ここでは画面構成によって異なるフラグメント構成を提供する方法も示しています。
注:このアクティビティの完全なソースコードは、FragmentLayout
クラスの使用例を示したサンプルアプリで入手可能です。
メイン アクティビティでは、通常の方法で onCreate()
の間にレイアウトを適用します。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fragment_layout) }
Java
@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
にフラグメントを追加する)、新しいアクティビティ(フラグメントを表示する場所)を開始するかのいずれかになります。
Kotlin
class TitlesFragment : ListFragment() { private var dualPane: Boolean = false private var curCheckPosition = 0 override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) // Populate list with our static array of titles. listAdapter = ArrayAdapter<String>( activity, 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. val detailsFrame: View? = activity?.findViewById(R.id.details) dualPane = detailsFrame?.visibility == View.VISIBLE curCheckPosition = savedInstanceState?.getInt("curChoice", 0) ?: 0 if (dualPane) { // In dual-pane mode, the list view highlights the selected item. listView.choiceMode = ListView.CHOICE_MODE_SINGLE // Make sure our UI is in the correct state. showDetails(curCheckPosition) } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt("curChoice", curCheckPosition) } override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) { 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. */ private fun showDetails(index: Int) { curCheckPosition = index if (dualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. listView.setItemChecked(index, true) // Check what fragment is currently shown, replace if needed. var details = fragmentManager?.findFragmentById(R.id.details) as? DetailsFragment if (details?.shownIndex != 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. fragmentManager?.beginTransaction()?.apply { if (index == 0) { replace(R.id.details, details) } else { replace(R.id.a_item, details) } setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) commit() } } } else { // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. val intent = Intent().apply { setClass(activity, DetailsActivity::class.java) putExtra("index", index) } startActivity(intent) } } }
Java
public static class TitlesFragment extends ListFragment { boolean dualPane; int curCheckPosition = 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); dualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // Restore last state for checked position. curCheckPosition = savedInstanceState.getInt("curChoice", 0); } if (dualPane) { // 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(curCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", curCheckPosition); } @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) { curCheckPosition = index; if (dualPane) { // 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) getSupportFragmentManager().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 = getSupportFragmentManager().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
のリストで選択された劇のあらすじを表示します。
Kotlin
class DetailsFragment : Fragment() { val shownIndex: Int by lazy { arguments?.getInt("index", 0) ?: 0 } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { 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 // isn't displayed. Note this isn't 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 } val text = TextView(activity).apply { val padding: Int = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 4f, activity?.resources?.displayMetrics ).toInt() setPadding(padding, padding, padding, padding) text = Shakespeare.DIALOGUE[shownIndex] } return ScrollView(activity).apply { addView(text) } } companion object { /** * Create a new instance of DetailsFragment, initialized to * show the text at 'index'. */ fun newInstance(index: Int): DetailsFragment { val f = DetailsFragment() // Supply index input as an argument. val args = Bundle() args.putInt("index", index) f.arguments = args return f } } } }
Java
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 // isn't displayed. Note this isn't 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
を埋め込んで選択された劇のあらすじを表示します。
Kotlin
class DetailsActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (resources.configuration.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. val details = DetailsFragment().apply { arguments = intent.extras } supportFragmentManager.beginTransaction() .add(android.R.id.content, details) .commit() } } }
Java
public static class DetailsActivity extends FragmentActivity { @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()); getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } } }
構成が横向きの場合、このアクティビティそのものは終了し、メイン アクティビティが引き継いで TitlesFragment
の横に DetailsFragment
を表示します。この動作は、画面が縦向きのときにユーザーが DetailsActivity
を開始し、その後で横向きに回転した場合(現在のアクティビティが再始動される)にも発生します。
参考資料
Fragment
は Sunflower デモアプリで使用されています。