FrameLayoutのキーコンセプトを学びましょう — Android SDKで最もシンプルなViewGroupで、通常は単一の子要素をホストするか、複数の要素を重ね合わせるために設計されています。FrameLayoutは、プレースホルダーコンテナ、フラグメント、ローディングインジケーター、フォアグラウンド要素に便利です。子要素の配置は管理されず、後続の各要素は前の要素の上に描画され、その位置はlayout_gravity(left, top, right, bottom, center)によって決定されます。基本的なシナリオはFrameLayout API Referenceに記載されています。
重要なポイント
FrameLayoutはAndroid SDKで最もシンプルなViewGroupであり、画面領域をブロックして単一の子ビュー(または複数のオーバーレイ)を表示するために設計されています。LinearLayout(順次配置)やRelativeLayout(相対配置)とは異なり、FrameLayoutは子の位置を変更しません — 新しい子はデフォルトで左上隅(0,0)に配置され、前の子の上に描画されます。
FrameLayoutはAPI Level 1で導入され、現在も最も軽量なAndroidコンテナです。複雑なロジックでonMeasureをオーバーライドせず、最小限のレイアウト操作を実行します。Android Performance Blogによると、FrameLayoutはonLayoutを1パスで実行し、Viewを直接配置する場合と比較してオーバーヘッドがほとんどありません。そのため、速度が重要なコンテナ(RecyclerViewのアイテムレイアウト、Fragmentコンテナ、オーバーレイレイヤー)に最適です。
FrameLayoutのサイズはデフォルトで最も大きな子要素によって決定されます(match_parentが設定されていない場合)。子が指定されていない場合、FrameLayoutは(0,0)に縮小します。android:measureAllChildren属性(下記のセクション)はこの動作を変更します。
FrameLayoutはViewGroupを継承し、多くの特殊化されたコンテナの直接の親です:FragmentContainerView(フラグメント)、CardView(影付きカード)、ScrollView(単一の子)、NestedScrollView。フラグメントナビゲーションでアクティビティを作成する際、標準のAndroid StudioテンプレートはフラグメントのルートコンテナとしてFrameLayout(またはFragmentContainerView)を使用します。
android:foreground — すべての子要素の上に描画されるdrawableを指定するFrameLayout属性です。background(子要素の下)とは異なり、foregroundはコンテンツの上に表示され、透明にすることができます。使用用途:押下時のオーバーレイエフェクト(?attr/selectableItemBackgroundによるリップル)、画像のマスキング、ステータスインジケーターの表示(画像の上の選択チェックマーク)。
Foregroundは標準のdrawableリソースをサポートします:ColorDrawable、ShapeDrawable、RippleDrawable、LayerDrawable。API 23+以降、フォアグラウンドの配置にandroid:foregroundGravityが利用可能です(fill, center, top, bottom)。フィルモードでは、foregroundはFrameLayout全体に拡大されます。センターモードでは、中央に描画されます。
android:measureAllChildren — FrameLayoutのサイズを計算する際にすべての子要素を測定するかどうかを決定するブール属性です(デフォルトはtrue)。true(デフォルト)の場合、FrameLayoutはGONE(サイズ0)を含むすべての子のサイズを考慮します。falseの場合、FrameLayoutはVISIBLEとINVISIBLEの子のみを測定し、GONE要素は計算から除外されます。Google I/O 2019によると、多くのGONE要素(可視性を切り替えるリストなど)を含むコンテナでmeasureAllChildrenを無効にすると、初期描画が20〜60%高速化されます。
android:layout_gravity — FrameLayout(および他のViewGroup)の子要素の属性で、コンテナ内での位置を定義します。FrameLayoutでは、layout_gravityが子要素の位置を制御する唯一の方法です。FrameLayoutは独自の配置ルール(RelativeLayoutのような)や方向(LinearLayoutのような)を提供しないためです。
指定可能な値:top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical。|で組み合わせます:android:layout_gravity="bottom|center_horizontal" — 要素は下端に固定され、水平方向に中央揃えされます。FrameLayoutより小さい要素の場合、layout_gravityによって空きスペースでの位置が決まります。
layout_gravityが設定されていない場合、要素はデフォルトで左上隅(top|left)に配置されます。複数の子を含むFrameLayoutでは、各子が独自のlayout_gravityを持つことができます — ある要素は左上隅、別の要素は右下隅、3つ目の要素は中央に配置できます。これにより、シンプルなオーバーレイ(画像の上に閉じるアイコンなど)を作成できます。
android:gravity(親属性)はFrameLayout内のコンテンツを配置します — 例えば、TextView内のテキスト。android:layout_gravity(子属性)は要素自体をFrameLayout内で配置します。FrameLayoutのコンテキストでは、gravityは子がデフォルトでどのように配置されるかを定義します(すべての子に対して一度にlayout_gravityを設定するのと同様)。ただし、各要素のlayout_gravityは親の値を上書きします。
右下隅にテキストラベルがある画像。FrameLayoutには、画面全体を埋めるImageViewと、画像の上に配置するためのlayout_gravity="bottom|end"を持つTextViewが含まれます。
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/product_photo"
android:scaleType="centerCrop" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="8dp"
android:background="@drawable/badge_background"
android:elevation="2dp"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp"
android:text="-30%"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
</FrameLayout>
ImageViewはFrameLayout全体(高さ200dp)を埋めます。layout_gravity="bottom|end"のTextViewは画像の上、右下隅に配置されます。elevation=2dpはラベルの下に影を追加し、視覚的に画像から分離します。この最小限の例は、LinearLayoutではネストされたコンテナやカスタムコードが必要だったでしょう。
読み込み中に中央に表示されるプログレスバーがある画面。FrameLayoutには、コンテンツとvisibility="gone"のProgressBar(コードで切り替え)の2つの要素が含まれます。
<FrameLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="読み込まれたコンテンツ"
android:textSize="18sp" />
<ProgressBar
android:id="@+id/loading_spinner"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
ProgressBarはデフォルトで非表示(gone)です。読み込みが開始されると、findViewById(R.id.loading_spinner).visibility = View.VISIBLEが呼び出され、スピナーがコンテンツの上に中央表示されます。読み込み後は.visibility = View.GONE。FrameLayoutはコンテンツの位置を移動せずにオーバーレイを提供します — ProgressBarが上に描画されるため、スピナーが表示されてもテキストは移動しません。
FragmentTransactionの標準コンテナとしてのFrameLayout。アクティビティはナビゲーションに応じてこのコンテナ内のフラグメントを置き換えます。
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
import androidx.fragment.app.FragmentTransaction
val fragmentContainer = R.id.fragment_container
fun navigateTo(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(fragmentContainer, fragment)
.addToBackStack(null)
.commit()
}
フラグメントコンテナとしてのFrameLayoutは、フラグメントナビゲーションをサポートする最も軽量な方法です。Navigation Component 2.4+ではFragmentContainerView(FrameLayoutのサブクラス)が推奨されますが、単純なFrameLayoutでも手動のFragmentTransactionには有効です。主な利点は、FragmentTransaction.replace()がアクティビティに影響を与えずにコンテンツを完全に置き換えることです。
FrameLayoutは4つのシナリオに最適です:フラグメントコンテナ(FragmentContainerViewまたはFrameLayout)、ローディングオーバーレイ(コンテンツの上にProgressBar)、カードラベル(画像の上のラベル)、ViewStubのプレースホルダーコンテナ(遅延読み込み)。
適さない場合:複数要素の複雑な配置(ConstraintLayoutを使用)、順次リスト(LinearLayoutまたはRecyclerView)、要素の順序が変わる動的レイアウト。
画面のルート要素としてのFrameLayout — 画面に2〜3個以上の要素がある場合は悪い習慣です。ルートコンテナにはConstraintLayoutを使用してください — パフォーマンスを損なうことなく配置をより細かく制御できます。FrameLayoutは、オーバーレイやプレースホルダーが必要な子ブロックにのみ使用してください。
よくある質問
FrameLayout — 配置システムを持たないミニマルなコンテナ(layout_gravityのみ)、要素の重ね合わせ用に設計されています。ConstraintLayout — 制約、チェーン、バリア、パーセント寸法、MotionLayoutサポートを備えた本格的なレイアウトシステム。FrameLayoutは単純なケース(子が1つまたはオーバーレイ)では高速ですが、複雑なレイアウトにはConstraintLayoutが必要です。選択基準:ローディングオーバーレイとフラグメントコンテナにはFrameLayout、それ以外にはConstraintLayout。
目的の軸でFrameLayoutのサイズをwrap_contentに設定します(android:layout_width="wrap_content"やandroid:layout_height="wrap_content")。FrameLayoutは最大の子要素のサイズに調整されます。すべての子がFrameLayoutより小さい場合、余分なスペースは空のままになります。正確に制御するには、固定のpaddingとともにmatch_parentを使用してください。
これはFrameLayoutの意図された動作です — 子を空間に分散させるのではなく、順次重ねて描画します。要素を重ねたくない場合は、別のコンテナ(LinearLayout、ConstraintLayout)を使用してください。描画順序を制御しながら部分的な重ね合わせを行うには、オフセットにlayout_gravity、Zオーダーにelevationを使用してください。
FrameLayoutの方が高速です、最も単純なケース(子要素が1つ、複雑な配置なし)の場合。FrameLayoutは制約を計算せずに最小限の時間でonMeasureを実行します。ただし、その差が顕著になるのは何百回もの繰り返し(数万アイテムのRecyclerView)の場合のみです。一般的な画面(1〜3個のFrameLayout)では、差はマイクロ秒であり、UXに影響しません。ConstraintLayoutは95%のタスクに万能な選択肢です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。