onLayout():概要、レイアウトアルゴリズムとメソッドパラメータ

著者: IT Sectr 公開日: 2026-07-22 読了時間: 9 分

onLayout() はViewGroupクラスのメソッドで、親コンテナの座標平面上で子Viewの位置とサイズを決定します。Androidシステムは測定フェーズ(onMeasure)の後、各子Viewの測定された幅と高さがすでにわかった時点でonLayoutを呼び出します。Android Developers Documentation(2026)によると、標準のViewGroup実装は子の自動位置決めを行わないため、onLayoutはカスタムViewGroupでオーバーライドする必須メソッドです。

重要ポイント

  • onLayout(boolean changed, int l, int t, int r, int b) — 親座標内の子要素の位置を決定するViewGroupメソッド
  • child.layout(l, t, r, b) — 各子Viewの呼び出しで、最終的な境界を設定
  • レイアウトフェーズはViewライフサイクルにおいてmeasureフェーズの後、drawフェーズの前に続く
  • getWidth()とgetHeight()はonLayoutの実行後にのみ利用可能になり、onMeasure後に利用可能なgetMeasuredWidthとは異なる
  • requestLayout() — 位置決めに影響するデータが変更されたときにonMeasureとonLayoutの再呼び出しを開始するメソッド

onLayout()とは?

onLayout(boolean changed, int l, int t, int r, int b) はViewGroupクラスのprotectedメソッドで、親コンテナ内に子Viewを配置するためにシステムによって呼び出されます。開発者は、カスケード、グリッド、千鳥配置、任意の座標など、標準的でない要素配置のカスタムViewGroupを作成するときにこのメソッドをオーバーライドします。各子Viewはchild.layout()の呼び出しを通じて最終的な境界を受け取ります。

changedパラメータは、最後のレイアウト以降にViewGroup自体の位置またはサイズが変更されたかどうかを示します。changedがtrueの場合、すべての子要素もおそらく再配置が必要です。パラメータl、t、r、bは、親の座標系におけるViewGroupの左上隅と右下隅の座標です。onLayout内で、開発者はこれらの値を子の配置のための開始座標として使用します。

ViewGroupはonLayoutをオーバーライドする唯一のクラスです。通常のView(ViewGroupではない)には子要素がなく、onLayoutを必要としません。その位置決めは親コンテナによって処理されます。通常のViewがonLayoutをオーバーライドしても、システムはそれを呼び出しません。これは、任意のViewに対して呼び出されるonMeasureとの基本的な違いです。

レイアウトフェーズの流れ

レイアウトフェーズは、ルートView上のパブリックメソッドlayout(int l, int t, int r, int b)の呼び出しから始まります。このメソッドはView自体の最終座標を設定し、ViewがViewGroupの場合はonLayoutを呼び出します。次にonLayoutは各子要素に対してchild.layout()を再帰的に呼び出し、このプロセスが階層を下って繰り返されます。このようにして、レイアウトはルートからリーフへと伝播します。

onLayoutを呼び出す前に、システムは前のサイクルと比較してViewの寸法が変更されたかどうかを確認します。寸法が変更されておらず、requestLayoutが呼び出されていない場合、onLayoutは呼び出されないことがあります。システムは前のレイアウトの結果を使用します。これは、寸法ではなく内容のみが変更されるアニメーションやスクロール中に、不要な位置の再計算を防ぐ最適化です。

requestLayout()は、Viewのレイアウトが古くなり再計算が必要であることをシステムに通知するViewメソッドです。requestLayoutを呼び出すと完全なサイクルがトリガーされます:最初にonMeasureが呼び出され、次にonLayout、次にonDrawが呼び出されます。再描画のみをトリガーするinvalidateとは異なり、requestLayoutは寸法と位置の完全な再計算をトリガーします。requestLayoutの過剰な呼び出しは、パフォーマンス問題の一般的な原因です。

onLayoutパラメータ:l、t、r、b

l(left) — 親の座標系におけるViewGroupの左端のX座標。t(top) — 上端のY座標。r(right) — 右端のX座標。b(bottom) — 下端のY座標。ViewGroupの幅はr - l、高さはb - tとして計算されます。これらの座標には、ViewGroup自体のすべてのパディングがすでに含まれています。

onLayout内で、開発者は各子Viewに対してchild.layout(int childLeft, int childTop, int childRight, int childBottom)を呼び出します。child.layoutに渡される座標は、親ViewGroupの座標系でなければなりません。通常、childLeftとchildTopは親のパディングを考慮して計算されます:childLeft = l + paddingLeft + offsetXchildTop = t + paddingTop + offsetY

パラメータ説明一般的な使用法
l(left)親内のViewGroupの左端の座標子要素のX軸の開始点
t(top)親内のViewGroupの上端の座標子要素のY軸の開始点
r(right)親内のViewGroupの右端の座標幅の上限、r - l = getWidth()
b(bottom)親内のViewGroupの下端の座標高さの上限、b - t = getHeight()

子要素の座標計算

子座標は次の式で計算されます:childLeft = l + paddingLeft +(marginLeftがある場合)childRight = childLeft + child.getMeasuredWidth()。垂直軸についても同様です:childTop = t + paddingTop +(marginTop)childBottom = childTop + child.getMeasuredHeight()。これら4つの値を計算した後、child.layout(childLeft, childTop, childRight, childBottom)が呼び出されます。

KotlinでのonLayoutを使用したカスタムViewGroupの例

FlowLayoutを作成しましょう。これは、子Viewを行に配置し、現在の行がいっぱいになると要素を新しい行に折り返すカスタムViewGroupです。これは1平面でのFlexboxのラップ相当品です。onLayoutはすべての子Viewを反復処理し、それぞれの位置を計算して、正しい境界でchild.layout()を呼び出します。

kotlin
class FlowLayout(context: Context)
    : ViewGroup(context) {

    private val horizontalSpacing = 12
    private val verticalSpacing = 8

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        val parentWidth =
            MeasureSpec.getSize(widthMeasureSpec)
        var rowX = paddingLeft
        var rowY = paddingTop
        var maxRowHeight = 0

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            measureChildWithMargins(child,
                widthMeasureSpec, 0,
                heightMeasureSpec, 0)

            if (rowX + child.measuredWidth >
                parentWidth - paddingRight) {
                rowX = paddingLeft
                rowY += maxRowHeight + verticalSpacing
                maxRowHeight = 0
            }

            rowX += child.measuredWidth +
                horizontalSpacing
            maxRowHeight = maxOf(maxRowHeight,
                child.measuredHeight)
        }

        val totalHeight = rowY + maxRowHeight +
            paddingBottom
        setMeasuredDimension(
            resolveSize(parentWidth, widthMeasureSpec),
            resolveSize(totalHeight, heightMeasureSpec))
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        val parentWidth = r - l
        var rowX = paddingLeft
        var rowY = paddingTop
        var maxRowHeight = 0

        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            val ch = child.measuredHeight

            if (rowX + cw >
                parentWidth - paddingRight) {
                rowX = paddingLeft
                rowY += maxRowHeight + verticalSpacing
                maxRowHeight = 0
            }

            child.layout(rowX, rowY,
                rowX + cw, rowY + ch)
            rowX += cw + horizontalSpacing
            maxRowHeight =
                maxOf(maxRowHeight, ch)
        }
    }

    override fun generateLayoutParams(attrs: AttributeSet?)
        : LayoutParams =
        MarginLayoutParams(context, attrs)
}

onLayoutとonMeasureの違い

onMeasureonLayoutは、Viewライフサイクルの2つの連続したフェーズで、根本的に異なるタスクを実行します。onMeasureはViewの望ましい(測定された)寸法を決定し、onLayoutは実際の(最終的な)座標と寸法を設定します。主な違い:onMeasureでは寸法は中間的であり、後で親によって調整される可能性がありますが、onLayoutでは各子Viewの最終位置が固定されます。

onMeasureは、リーフView(TextView、ImageView、Button)を含むすべてのViewに対して呼び出されます。onLayoutはViewGroupに対してのみ呼び出されます。これは、位置決めがView自体ではなく親コンテナの責任であるためです。リーフViewは、親のonLayoutから呼び出されたlayout()を通じて位置を受け取ります。

getMeasuredWidth()getMeasuredHeight()はonMeasureの後に利用可能になりますが、getWidth()getHeight()はonLayoutの後にのみ利用可能になります。onMeasure内でgetWidth()にアクセスすると、前のサイクルの値またはゼロが返されます。したがって、onMeasureで寸法を計算するには、MeasureSpecと子を順次使用する必要があります。

onLayout使用時のよくある間違い

パディングを考慮しない位置決め — onLayoutを実装するときの最初の間違い。開発者は子Viewの初期座標に親のpaddingLeftpaddingTopを追加するのを忘れることがよくあります。その結果、子はsetPadding()またはXMLマークアップで設定されたパディングを無視して、ViewGroupの端に表示されます。正しい計算:childLeft = paddingLeft + offsetX

非表示の子に対するレイアウト呼び出し — 2番目の一般的な問題。ViewGroupに可視性がGONEの子Viewが含まれている場合、それらを配置する必要はありません。スペースを占有しません。ただし、onLayoutはこのケースを正しく処理し、GONEの子をスキップする必要があります。INVISIBLEの子については、レイアウトを呼び出す必要があります。表示されていなくてもスペースを保持します。

changedパラメータの無視 — 3番目の間違い。changedパラメータは、ViewGroupの寸法または位置が変更されたかどうかを示します。changed == falseの場合、すべての子要素のレイアウトを再計算せずにキャッシュされた座標を使用できます。ただし、完全なレイアウトキャッシュは複雑なタスクであり、ほとんどの実装ではonLayoutは毎回単純にすべての要素を再計算します。これは子の数が少ない場合に許容されます。

よくある質問

ViewGroupでonLayoutをオーバーライドしないことは可能ですか?

はい、可能です。ViewGroupが標準のLayoutParamsを使用し、カスタム位置決めロジックを追加しない場合。ただし、ViewGroupの標準のonLayout実装は何もアクションを実行しません。子要素は配置されません。実際には、すべてのViewGroup(LinearLayout、RelativeLayout、FrameLayout)がonLayoutをオーバーライドします。

layoutとonLayoutの違いは何ですか?

layout()はViewのpublic finalメソッドで、システムまたは親ViewGroupによって呼び出されます。View自体の座標を設定し、ViewがViewGroupの場合はonLayoutを呼び出します。onLayout()はprotectedメソッドで、開発者が子要素のカスタム配置のためにオーバーライドします。

onLayoutはrequestLayoutを呼び出せますか?

技術的には — はい、呼び出せます。しかし、これは強く推奨されません。無限再帰(requestLayout → onMeasure → onLayout → requestLayout)につながるためです。onLayout内でrequestLayoutが呼び出されると、システムはStackOverflowError例外をスローします。すべての寸法変更はonLayoutの前に実行される必要があります。

onLayoutはアニメーションとどのように連携しますか?

レイアウトアニメーション(LayoutTransition)は子Viewの位置の変更をインターセプトし、トランジションアニメーションを適用します。LayoutTransitionが有効な場合、onLayoutは最初に最終位置を設定し、次にLayoutTransitionが古い位置から新しい位置への移動をアニメーション化します。これには、適切な最終座標を使用した正しいonLayout実装が必要です。

invalidateの後になぜonLayoutが呼び出されないのですか?

invalidate()はdrawフェーズ(再描画)のみをトリガーし、measureとlayoutには影響しません。onLayoutをトリガーするには、requestLayout()を呼び出す必要があります。これにより完全なサイクル(measure → layout → draw)が開始されます。invalidateは、寸法と位置が変更されない場合に外観を更新するためにより効率的です。

まとめ

  • onLayout() — 測定フェーズ完了後に子Viewの最終位置を決定するViewGroupメソッド
  • child.layout(l, t, r, b) — 各子要素の座標を設定する主要なメカニズム
  • パラメータl、t、r、b — 親システムにおけるViewGroupの端の座標、幅 = r - l、高さ = b - t
  • レイアウトフェーズはルートViewから子要素へ再帰的に伝播し、各ViewGroupでonLayoutを呼び出す
  • requestLayout()は寸法と位置の完全な再計算サイクルをトリガーする。invalidateは再描画のみをトリガーする
  • onLayoutでのパディング考慮は必須 — 子の初期座標には親のpaddingLeftとpaddingTopを含める必要がある

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください