← Назад к вопросам

Что такое invalidate в View?

2.0 Middle🔥 162 комментариев
#Android компоненты#UI и вёрстка

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основное назначение invalidate()

invalidate() — это ключевой метод в Android View-системе, который сообщает фреймворку, что текущее состояние визуального представления View устарело и требует перерисовки (redraw) в следующем цикле отрисовки UI-потока. При вызове этого метода View помечается как "грязная" (dirty), и система планирует для неё вызов метода onDraw(), где и происходит фактическое рисование с актуальными данными.

Как работает механизм invalidate()

Процесс можно разделить на несколько этапов:

  1. Вызов invalidate() на View или её подклассе.
  2. Система добавляет View в очередь недействительных областей (invalidation queue) и вычисляет область экрана, требующую обновления.
  3. В следующем цикле отрисовки (в рамках выполнения performTraversals() у ViewRootImpl) система проверяет очередь и определяет, какие View нуждаются в перерисовке.
  4. Для каждой "грязной" View последовательно вызываются:
    • onDraw() — основной метод для кастомной отрисовки.
    • dispatchDraw() — для отрисовки дочерних View (актуально для ViewGroup).

Важно: invalidate() не вызывает onDraw() немедленно, а только планирует перерисовку. Фактическое обновление происходит в UI-потоке, когда система обрабатывает очередь сообщений.

Отличие от postInvalidate()

Основное различие между invalidate() и postInvalidate() заключается в потоке выполнения:

  • invalidate() можно вызывать только из UI-потока (главного потока). Вызов из фонового потока приведёт к исключению CalledFromWrongThreadException.
  • postInvalidate() — потокобезопасный метод, который можно вызывать из любого потока. Он работает, помещая задачу на перерисовку в очередь сообщений UI-потока.
// Пример использования в фоновом потоке
new Thread(new Runnable() {
    @Override
    public void run() {
        // Нельзя: someView.invalidate(); // Вызовет исключение!
        
        // Можно:
        someView.postInvalidate();
        
        // Или через Handler:
        someView.post(new Runnable() {
            @Override
            public void run() {
                someView.invalidate();
            }
        });
    }
}).start();

Методы семейства invalidate()

Помимо базового invalidate(), существуют вариации для оптимизации перерисовки:

  • invalidate(Rect dirty) и invalidate(int l, int t, int r, int b) — позволяют указать конкретную прямоугольную область View, которая требует обновления. Это полезно для оптимизации, когда нужно перерисовать только часть View, а не всю целиком.
// Пример оптимизированной перерисовки только части View
Rect damagedArea = new Rect(10, 10, 50, 50);
customView.invalidate(damagedArea);

// Или с координатами
customView.invalidate(10, 10, 50, 50);
  • invalidateDrawable(Drawable drawable) — используется, когда нужно перерисовать View из-за изменения состояния Drawable (например, анимации).

Оптимизации перерисовки

Система Android старается минимизировать перерисовку, объединяя "грязные" области:

  1. Объединение областей — если несколько вызовов invalidate() происходят до следующего цикла отрисовки, система объединяет их в одну область.
  2. Отсечение невидимых частей — перерисовываются только видимые части View.
  3. Аппаратное ускорение — начиная с Android 3.0, система использует аппаратное ускорение для более эффективной перерисовки.

Практическое применение

invalidate() наиболее часто используется в кастомных View, когда нужно обновить их отображение в ответ на:

  • Изменение данных
  • Пользовательский ввод (касания, жесты)
  • Анимации
  • Изменение состояния
class CustomCircleView(context: Context) : View(context) {
    private var radius: Float = 50f
    
    fun increaseRadius() {
        radius += 10f
        // Сообщаем системе, что View нужно перерисовать
        invalidate()
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val paint = Paint().apply {
            color = Color.RED
            style = Paint.Style.FILL
        }
        // Рисуем круг с текущим радиусом
        canvas.drawCircle(width / 2f, height / 2f, radius, paint)
    }
}

Важные нюансы

  1. Частые вызовы invalidate() могут привести к потере кадров (jank), если View не успевает перерисовываться с частотой 60 FPS.
  2. requestLayout() vs invalidate() — если изменились размеры или положение View, нужно вызвать requestLayout(), который запускает полный layout-пасс, а не только перерисовку.
  3. Внутри onDraw() нельзя вызывать invalidate() без условий — это создаст бесконечный цикл перерисовок.

Метод invalidate() — фундаментальная часть системы отрисовки Android, обеспечивающая эффективное обновление пользовательского интерфейса в ответ на изменение состояния приложения.