invalidate() — что это, механизм перерисовки и invalidate(Rect)

Автор: IT Sectr Опубликовано: 2026-07-20 Время чтения: 7 мин

invalidate() — это метод класса View в Android, который помечает вью как требующую повторной отрисовки. Вызов invalidate() приводит к перерисовке вью в ближайшем цикле обновления экрана, что делает его основным механизмом обновления визуального состояния кастомных компонентов. По данным Android Developers Documentation (2025), invalidate() используется в 90% кастомных View для синхронизации изменений данных с отображением на экране. Метод работает асинхронно — он лишь устанавливает флаг dirty и возвращает управление немедленно.

Главное

  • invalidate() — асинхронный запрос перерисовки View в Android, работает через dirty flag механизм
  • postInvalidate() — версия invalidate() для вызова из фонового потока, потокобезопасна
  • invalidate(Rect) — частичная перерисовка только указанной области для оптимизации производительности
  • onDraw() — метод, который вызывается системой после invalidate(), аналогичен draw(_:) в iOS
  • invalidate() vs requestLayout() — invalidate() перерисовывает содержимое, requestLayout() пересчитывает геометрию

Что такое invalidate() в Android

invalidate() — это метод класса android.view.View, который сообщает системе Android, что визуальное представление вью устарело. После вызова метода система помечает вью как dirty и планирует её перерисовку в ближайшем цикле обновления экрана (обычно 16 мс для 60 FPS).

Метод invalidate() принимает различные формы: без параметров (полная перерисовка), с параметром Rect (частичная) и с параметрами ltrb (left, top, right, bottom). Все версии работают асинхронно и могут вызываться из UI-потока. Для вызова из фоновых потоков существует postInvalidate().

Как работает перерисовка через invalidate()

Механизм перерисовки в Android основан на ViewRootImpl — внутреннем компоненте, который связывает View Hierarchy с Surface для отрисовки. Когда вызывается invalidate(), ViewRootImpl помечает область вью как dirty и отправляет запрос на перерисовку через Choreographer — системный сервис, синхронизирующий отрисовку с частотой обновления экрана.

Цикл перерисовки

Choreographer получает сигнал от Vsync и запускает тройной проход: measure, layout, draw. Однако invalidate() затрагивает только фазу draw — фазы measure и layout не выполняются, если не был вызван requestLayout(). Это ключевое отличие: 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() // Redraw request
    }

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

        // Drawing chart line
        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() получает Canvas для отрисовки линий через Path.

invalidate() vs postInvalidate()

Основное различие между invalidate() и postInvalidate() — в потоковой безопасности. invalidate() должен вызываться только из UI-потока (main thread). postInvalidate() можно вызывать из любого потока — через Handler он отправляет запрос на перерисовку в UI-поток.

Характеристикаinvalidate()postInvalidate()
Поток вызоваUI-поток (main thread)Любой поток
МеханизмПрямое обновление dirty flagЧерез Handler.post() к UI-потоку
LatencyМинимальная, в текущем циклеДо следующего цикла UI-потока
ПроизводительностьВысокаяНебольшой overhead на Handler
РекомендацияДля UI-потока всегда invalidate()Только для фоновых потоков

На практике postInvalidate() используется в сценариях загрузки данных из сети, обработки результатов сенсоров или фоновых вычислений. Если вы находитесь в UI-потоке — всегда используйте invalidate() для минимальной задержки.

kotlin
    // Called from UI thread
view.invalidate()

    // Called from background thread
Thread {
    // Heavy computations
    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
    // Partial update: cursor area only
private val cursorRect = Rect()

fun moveCursorTo(newX: Int, newY: Int) {
    // Invalidate old position
    invalidate(cursorRect)

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

    // Invalidate new position
    invalidate(cursorRect)
}

Без частичной перерисовки каждое движение курсора перерисовывало бы весь View, что для большого графика означает перерисовку тысяч пикселей вместо нескольких десятков. invalidate(Rect) — обязательная техника для редакторов, канвасов рисования и анимированных компонентов.

invalidate() vs requestLayout(): в чём разница

Одна из частых ошибок — вызов requestLayout() там, где достаточно invalidate(), и наоборот. Разница фундаментальна: invalidate() затрагивает только фазу draw, а requestLayout() запускает полный цикл measure → layout → draw.

Аспектinvalidate()requestLayout()
Фазы циклаТолько drawmeasure + layout + draw
Когда использоватьМеняется только отрисовка (цвет, текст, графика)Меняется размер или положение вью
ПроизводительностьЛёгкий — только перерисовкаТяжёлый — пересчёт иерархии
Влияние на иерархиюТолько текущее вьюМожет затронуть родительские контейнеры

Если вы меняете текст в TextView — достаточно invalidate(), так как размер вью не меняется. Если текст может перенестись на новую строку и увеличить высоту — нужен requestLayout(). Android Lint помогает отслеживать такие ошибки через правила производительности.

Оптимизация производительности invalidate()

Избыточные вызовы invalidate() — одна из главных причин низкой производительности кастомных View в Android. Рассмотрим техники оптимизации.

Минимизируйте частоту вызовов

Если данные обновляются с высокой частотой (сенсоры, анимация, видео), не вызывайте invalidate() на каждое изменение. Используйте ValueAnimator или Choreographer.FrameCallback для синхронизации с частотой обновления экрана. Это гарантирует, что invalidate() вызывается не чаще одного раза за кадр.

Используйте Hardware Acceleration

С API 14 Android поддерживает аппаратное ускорение через GPU. Если ваша кастомная View использует только Canvas API (drawRect, drawCircle, drawPath), ускорение работает прозрачно. Для DisplayList-совместимых операций invalidate() обрабатывается значительно быстрее.

kotlin
// Using Choreographer for Vsync sync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
    updateAnimation(frameTimeNanos)
    invalidate()
    Choreographer.getInstance().postFrameCallback(this)
}

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

Используйте invalidate(Rect) для точечных обновлений, избегайте вызова invalidate() из onDraw() (бесконечный цикл), и всегда профилируйте через GPU Profile Rendering на устройстве. Это покажет точное время отрисовки каждого кадра и поможет выявить проблемные места.

Часто задаваемые вопросы

Можно ли вызывать invalidate() из onDraw()?

Нет, вызов invalidate() внутри onDraw() создаёт бесконечный цикл перерисовки: onDraw() вызывает invalidate(), который снова запускает onDraw(). Это приводит к 100% загрузке CPU и дропу кадров. Используйте анимации через ValueAnimator или Choreographer.

Чем invalidate() отличается от postInvalidate()?

invalidate() работает только в UI-потоке и обновляет dirty flag немедленно. postInvalidate() отправляет запрос через Handler в UI-поток и может вызываться из любого фонового потока. Если вы в UI-потоке — используйте invalidate() для минимальной задержки.

Вызывает ли setText() в TextView автоматический invalidate()?

Да, внутри TextView метод setText() вызывает invalidate() после обновления текста. Если текст изменил размеры вью, дополнительно вызывается requestLayout(). Разработчику не нужно вручную вызывать invalidate() при работе со стандартными виджетами.

Как invalidate() влияет на производительность при 60 FPS?

Каждый вызов invalidate() планирует перерисовку в следующем Vsync (каждые 16 мс). Если onDraw() выполняется дольше 16 мс, происходит дроп кадров. Оптимизируйте onDraw() — кэшируйте Bitmap, избегайте аллокаций и используйте Hardware Acceleration для GPU-отрисовки.

Нужно ли вызывать invalidate() после изменения свойства Paint?

Да, после изменения свойств Paint (цвет, толщина, стиль) необходимо вызвать invalidate(), так как View не отслеживает изменения объектов Paint автоматически. Система не знает, что Paint изменился, и не вызовет onDraw() без явного запроса.

Итоги

  • invalidate() — основной механизм запроса перерисовки View в Android, работает асинхронно через dirty flag
  • postInvalidate() — потокобезопасная версия для вызова из фоновых потоков, использует Handler
  • invalidate(Rect) — частичная перерисовка только указанной области, критична для производительности при точечных изменениях
  • requestLayout() — запускает полный цикл measure + layout + draw, значительно тяжелее чем invalidate()
  • Choreographer — системный сервис синхронизации с Vsync, рекомендуется для анимаций с invalidate()
  • Hardware Acceleration — GPU-ускорение доступно с API 14, ускоряет обработку invalidate() для Canvas API
  • GPU Profile Rendering — инструмент профилирования для измерения времени отрисовки и выявления медленных onDraw()

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также