invalidate() — その概要、再描画メカニズム、invalidate(Rect)

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

invalidate() はAndroidのViewクラスのメソッドで、ビューを再描画が必要な状態としてマークします。invalidate()を呼び出すと、次の画面更新サイクルでビューの再描画がトリガーされ、カスタムコンポーネントの視覚的状態を更新する主要なメカニズムとなります。Android Developers Documentation(2025)によると、invalidate()は90%のカスタムViewでデータ変更を画面表示と同期するために使用されています。このメソッドは非同期で動作し、dirtyフラグを設定するだけで即座に制御を返します。

重要なポイント

  • invalidate() — AndroidでViewの再描画を要求する非同期リクエスト、dirtyフラグメカニズムを通じて動作
  • postInvalidate() — バックグラウンドスレッドから呼び出すためのinvalidate()のバージョン、スレッドセーフ
  • invalidate(Rect) — パフォーマンス最適化のための指定領域のみの部分再描画
  • onDraw() — invalidate()後にシステムによって呼び出されるメソッド、iOSのdraw(_:)に類似
  • invalidate() vs requestLayout() — invalidate()はコンテンツを再描画、requestLayout()はジオメトリを再計算

Androidにおけるinvalidate()とは

invalidate() はandroid.view.Viewクラスのメソッドで、ビューの視覚的表現が古くなったことをAndroidシステムに通知します。メソッドを呼び出した後、システムはビューをdirtyとしてマークし、次の画面更新サイクル(通常60 FPSで16 ms)で再描画をスケジュールします。

invalidate()メソッドにはさまざまな形式があります:パラメータなし(完全再描画)、Rectパラメータ付き(部分)、ltrbパラメータ(left, top, right, bottom)付き。すべてのバージョンは非同期で動作し、UIスレッドから呼び出す必要があります。バックグラウンドスレッドからの呼び出しにはpostInvalidate()があります。

invalidate()による再描画の仕組み

Androidの再描画メカニズムはViewRootImplに基づいています — View Hierarchyを描画用のSurfaceに接続する内部コンポーネントです。invalidate()が呼び出されると、ViewRootImplはビュー領域をdirtyとしてマークし、Choreographerを介して再描画リクエストを送信します — 画面リフレッシュレートに描画を同期するシステムサービスです。

再描画サイクル

ChoreographerはVsyncから信号を受け取り、measurelayoutdrawの3段階のプロセスを開始します。ただし、invalidate()はdrawフェーズのみに影響し、requestLayout()が呼び出されない限りmeasureおよびlayoutフェーズは実行されません。これが重要な違いです:invalidate()はジオメトリを再計算しないため、requestLayout()よりも軽量です。

kotlin
class CustomChartView(context: Context, attrs: AttributeSet?)
    : View(context, attrs) {

    private var dataPoints: List<Float> = emptyList()
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    fun updateData(newPoints: List<Float>) {
        dataPoints = newPoints
        invalidate() // 再描画リクエスト
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.color = Color.BLUE
        paint.strokeWidth = 4f
        paint.style = Paint.Style.STROKE

        // グラフの線を描画中
        val path = Path()
        dataPoints.forEachIndexed { index, value ->
            val x = index * width / max(dataPoints.size - 1, 1)
            val y = height - value * height
            if (index == 0) path.moveTo(x, y)
            else path.lineTo(x, y)
        }
        canvas.drawPath(path, paint)
    }
}

この例では、グラフを描画するカスタムViewがデータ更新時にinvalidate()を呼び出します。システムは階層内の他の要素に影響を与えずにこのViewのみを再描画します。onDraw()はPathを介して線を描画するためのCanvasを受け取ります。

invalidate() vs postInvalidate()

invalidate()とpostInvalidate()の主な違いはスレッドセーフティにあります。invalidate()はUIスレッド(メインスレッド)からのみ呼び出す必要があります。postInvalidate()は任意のスレッドから呼び出すことができ、Handlerを介してUIスレッドに再描画リクエストを送信します。

特性invalidate()postInvalidate()
呼び出しスレッドUIスレッド(メインスレッド)任意のスレッド
メカニズムdirtyフラグの直接更新Handler.post()を介してUIスレッドへ
レイテンシ最小、現在のサイクル内次のUIスレッドサイクルまで
パフォーマンス高いHandlerのわずかなオーバーヘッド
推奨UIスレッドでは常にinvalidate()バックグラウンドスレッドのみ

実際には、postInvalidate()はネットワークデータの読み込み、センサー結果の処理、バックグラウンド計算などのシナリオで使用されます。UIスレッドにいる場合は、最小レイテンシのために常にinvalidate()を使用してください。

kotlin
    // UIスレッドから呼び出されました
view.invalidate()

    // バックグラウンドスレッドから呼び出されました
Thread {
    // 重い計算
    val result = performHeavyCalculation()
    runOnUiThread {
        updateUi(result)
    }
}.start()

invalidate(Rect)による部分再描画

invalidate(Rect)invalidate(int l, int t, int r, int b) は再描画領域を制限することを可能にします。これはパフォーマンスにとって重要です:Viewの一部のみが変更される場合(カーソルの移動、インジケータの変更など)、ビュー全体を再描画する必要はありません。

システムは指定されたdirty矩形をonDraw()canvas.clipBoundsを介して渡します。onDraw()内でclipBoundsを確認し、その領域内でのみ描画できます。Android Canvasはdirty矩形の外側の描画を自動的にクリップします。

kotlin
    // 部分更新:カーソル領域のみ
private val cursorRect = Rect()

fun moveCursorTo(newX: Int, newY: Int) {
    // 古い位置を無効化
    invalidate(cursorRect)

    cursorRect.set(newX - 5, newY - 5,
                  newX + 5, newY + 5)

    // 新しい位置を無効化
    invalidate(cursorRect)
}

部分再描画がない場合、カーソルが移動するたびにView全体が再描画され、大きなグラフでは数十ピクセルではなく数千ピクセルの再描画が必要になります。invalidate(Rect)はエディタ、描画キャンバス、アニメーションコンポーネントにとって必須のテクニックです。

invalidate() vs requestLayout(): 違いは何か

よくある間違いは、invalidate()で十分な場所でrequestLayout()を呼び出すこと、またはその逆です。違いは根本的です:invalidate()はdrawフェーズのみに影響しますが、requestLayout()は完全なmeasure → layout → drawサイクルをトリガーします。

側面invalidate()requestLayout()
サイクルのフェーズdrawのみmeasure + layout + draw
使用時レンダリングのみ変更(色、テキスト、グラフィック)Viewのサイズまたは位置が変更
パフォーマンス軽量 — 再描画のみ重量 — 階層の再計算
階層への影響現在のViewのみ親コンテナに影響する可能性あり

TextViewでテキストを変更する場合、Viewのサイズが変わらないためinvalidate()で十分です。テキストが改行されて高さが増える可能性がある場合は、requestLayout()が必要です。Android Lintはパフォーマンスルールを通じてこのようなエラーの追跡を支援します。

invalidate()のパフォーマンス最適化

過剰なinvalidate()呼び出しは、AndroidのカスタムViewにおけるパフォーマンス低下の主な原因の1つです。最適化テクニックを見てみましょう。

呼び出し頻度を最小化する

データが高頻度(センサー、アニメーション、ビデオ)で更新される場合、変更のたびにinvalidate()を呼び出さないでください。画面リフレッシュレートに同期するには、ValueAnimatorまたはChoreographer.FrameCallbackを使用してください。これにより、invalidate()が1フレームに1回以上呼び出されないことが保証されます。

ハードウェアアクセラレーションを使用する

API 14以降、AndroidはGPUを介したハードウェアアクセラレーションをサポートしています。カスタムViewがCanvas API(drawRect、drawCircle、drawPath)のみを使用する場合、アクセラレーションは透過的に機能します。DisplayList互換の操作では、invalidate()の処理が大幅に高速化されます。

kotlin
// Vsync同期にChoreographerを使用
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
    updateAnimation(frameTimeNanos)
    invalidate()
    Choreographer.getInstance().postFrameCallback(this)
}

fun startAnimation() {
    Choreographer.getInstance().postFrameCallback(frameCallback)
}

ターゲットを絞った更新にはinvalidate(Rect)を使用し、onDraw()からinvalidate()を呼び出すことを避け(無限ループ)、常にデバイス上でGPU Profile Renderingを使用してプロファイリングしてください。これにより各フレームの正確なレンダリング時間が表示され、問題のある領域を特定するのに役立ちます。

よくある質問

onDraw()からinvalidate()を呼び出せますか?

いいえ、onDraw()内でinvalidate()を呼び出すと、無限の再描画ループが発生します:onDraw()がinvalidate()を呼び出し、それが再びonDraw()をトリガーします。これによりCPU使用率が100%になり、フレームドロップが発生します。ValueAnimatorまたはChoreographerを介してアニメーションを使用してください。

invalidate()とpostInvalidate()の違いは何ですか?

invalidate()はUIスレッドでのみ動作し、dirtyフラグを即座に更新します。postInvalidate()はHandlerを介してUIスレッドにリクエストを送信し、任意のバックグラウンドスレッドから呼び出すことができます。UIスレッドにいる場合は、最小レイテンシのためにinvalidate()を使用してください。

TextViewのsetText()は自動的にinvalidate()を呼び出しますか?

はい、TextViewのsetText()メソッドは内部的にテキスト更新後にinvalidate()を呼び出します。テキストがViewの寸法を変更する場合は、requestLayout()も呼び出されます。標準ウィジェットを使用する場合、開発者が手動でinvalidate()を呼び出す必要はありません。

60 FPSでのinvalidate()はパフォーマンスにどのように影響しますか?

各invalidate()呼び出しは、次のVsync(16 msごと)で再描画をスケジュールします。onDraw()に16 ms以上かかる場合、フレームドロップが発生します。onDraw()を最適化してください — Bitmapをキャッシュし、アロケーションを避け、GPUレンダリングにハードウェアアクセラレーションを使用してください。

Paintのプロパティ変更後にinvalidate()を呼び出す必要がありますか?

はい、Paintのプロパティ(色、太さ、スタイル)を変更した後は、invalidate()を呼び出す必要があります。ViewはPaintオブジェクトの変更を自動的に追跡しないためです。システムはPaintが変更されたことを認識せず、明示的なリクエストなしではonDraw()を呼び出しません。

まとめ

  • invalidate() — AndroidでView再描画を要求する主要メカニズム、dirtyフラグを介して非同期に動作
  • postInvalidate() — バックグラウンドスレッドから呼び出すためのスレッドセーフなバージョン、Handlerを使用
  • invalidate(Rect) — 指定領域のみの部分再描画、ターゲットを絞った変更のパフォーマンスに重要
  • requestLayout() — 完全なmeasure + layout + drawサイクルをトリガー、invalidate()より大幅に重い
  • Choreographer — Vsync同期のためのシステムサービス、invalidate()を使用したアニメーションに推奨
  • Hardware Acceleration — API 14から利用可能なGPUアクセラレーション、Canvas APIのinvalidate()処理を高速化
  • GPU Profile Rendering — レンダリング時間を測定し、遅いonDraw()メソッドを特定するプロファイリングツール

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

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

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

こちらもお読みください