Android invalidate all views

Русские Блоги

Android basic invalidate и процесс RequestLayout

В этой статье разбирается простейший случай, процесс вызова недействительного в дочернем представлении (не ViewGroup) и игнорирование кеша чертежей, то есть mLayerType == LAYER_TYPE_NONE 。

Пожалуйста, поправьте меня, если есть ошибки.

Программный рисунок

Известно из кода:

  1. Если View не отображается && без анимации, ViewGroup не отображается && без анимации && анимация перехода не выполняется.
  2. Отрисовка программного обеспечения вызовет все представления (точки оптимизации аппаратного ускорения), которые пересекаются с грязной областью
  3. Согласно первому пункту ViewGroup # invalidate инициирует перерисовку своего собственного и всех дочерних представлений.
  4. Когда аннулирование одного и того же представления постоянно вызывается в одной и той же последовательности отрисовки, оно будет заблокировано флажком и больше не исчезнет.
  5. В той же последовательности отрисовки, когда разные вызовы Views становятся недействительными, он будет настроен только на один и не будет выполняться повторно.

Аппаратное ускорение

  1. Если View не отображается && без анимации, ViewGroup не отображается && без анимации && анимация перехода не выполняется.
  2. Чертеж оборудования вызовет только draw () или dispatchDraw () представления, отмеченного PFLAG_INVALIDATED
  3. Когда View # invalidate (не ViewGroup), dispatchDraw не будет вызываться, потому что он передается dispatchGetDisplayList
  4. Когда аннулирование одного и того же представления постоянно вызывается в одной и той же последовательности отрисовки, оно будет заблокировано флажком и больше не исчезнет.
  5. В той же последовательности отрисовки, когда разные вызовы Views становятся недействительными, он будет настроен только на один и не будет выполняться повторно.

requestLayout

  1. Очистить кэш меры, пометить PFLAG_FORCE_LAYOUT, вызвать рекурсивно, будет отмечен весь путь, весь путь будет сильно заплачен за измерение и макет
  2. Draw () может быть запущен снова, недействительность будет запущена после изменения размера во время макета
  3. Когда requestLayout одного и того же представления постоянно вызывается в той же последовательности макета, он будет заблокирован isLayoutRequested и больше не будет расти.
  4. Когда разные представления вызывают requestLayout в одной и той же последовательности макета, он будет настроен только на одно, а не на повторное выполнение

Соответствующий код выглядит следующим образом:

Источник

Рисование собственных представлений (View) в Android

Получите полный контроль над представлением и оптимизируйте его производительность

В преддверии старта курса «Android Developer. Professional» приглашаем всех желающих принять участие в открытом вебинаре на тему «Пишем gradle plugin».

А пока делимся переводом полезного материала.

Введение

Разработчики постоянно проектируют различные виды пользовательских интерфейсов с помощью XML, но в дополнение к этому можно довольно легко освоить создание собственных представлений, которые открывают новые преимущества и позволяют избежать повторного использования шаблонного кода.

В Android доступен широкий набор готовых виджетов и макетов для создания пользовательского интерфейса, однако они не могут удовлетворить все требования наших приложений. И здесь на помощь приходит возможность создания собственных представлений. Создав собственный подкласс представления, можно получить максимально полный контроль за внешним видом и функционалом экранного элемента.

Прежде чем приниматься за работу с собственными представлениями, полезно изучить жизненный цикл представления.

Зачем создавать собственные представления?

Чтобы реализовать собственное представление, в большинстве случаев понадобится больше времени, чем если использовать обычные представления. Создавать собственные представления стоит лишь в том случае, если нет другого, более простого способа реализовать нужную вам возможность или если у вас есть какие-либо из указанных ниже проблем, которые можно устранить за счет создания собственного представления.

Производительность: в вашем макете много представлений и вы хотите оптимизировать их, нарисовав одно, более легкое собственное представление.

Имеется сложная иерархия представлений, которую трудно использовать и поддерживать.

Необходимо создать специализированное представление, требующее рисования вручную.

Общий подход

Чтобы приступить к созданию компонентов для реализации собственных представлений, необходимо выполнить следующие основные шаги.

Создать класс, расширяющий базовый класс или подкласс представления.

Реализовать конструкторы, использующие атрибуты из XML-файла.

Переопределить некоторые методы родительского класса (onDraw(), onMeasure() и т. д.) в соответствии с нашими требованиями.

После выполнения этих шагов созданный расширяющий класс можно использовать вместо представления, на основе которого он был создан.

Пример

В одном из моих проектов мне нужно было создать круглый виджет TextView для отображения количества уведомлений. Чтобы достичь этой цели, нужно создать подкласс TextView.

Шаг 1. Создадим класс с именем CircularTextView .

Шаг 2. Расширим класс виджета TextView. Здесь под TextView в IDE выдается ошибка, в которой сообщается, что у этого типа есть конструктор и он должен быть инициализирован.

Читайте также:  Дистанционное управление компом с андроида

Шаг 3. Добавим конструкторы в класс.

Это можно сделать двумя способами.

Первый способ добавления конструкторов в класс показан ниже.

Другой способ заключается в добавлении аннотации @JvmOverloads к вызову конструктора, как показано ниже.

Часто нас сбивает с толку то, что у представления есть несколько разных типов конструкторов.

View(Context context)

Простой конструктор для динамического создания представления из программного кода. Здесь параметр context — это контекст, в котором работает представление и через который можно получить доступ к текущей теме, ресурсам и т. д.

View(Context context, @Nullable AttributeSet attrs)

Конструктор, который вызывается при формировании представления из XML-файла. Он вызывается, когда представление создается из XML-файла, содержащего атрибуты представления. В этом варианте конструктора используется стиль по умолчанию (0), поэтому применяются только те значения атрибутов, которые есть в теме контекста и заданном наборе AttributeSet .

Шаг 4. Самый важный шаг в отрисовке собственного представления — это переопределение метода onDraw() и реализация необходимой логики отрисовки внутри этого метода.

Метод OnDraw (canvas: Canvas?) имеет параметр Canvas (холст), с помощью которого компонент представления может отрисовывать себя. Для рисования на холсте необходимо создать объект Paint.

Как правило, процесс рисования определяется двумя аспектами:

что рисовать (определяется объектом Canvas);

как рисовать (определяется объектом Paint).

Например, Canvas предоставляет метод для рисования линии, а Paint предоставляет методы для определения цвета этой линии. В нашем случае объект Canvas в классе CircularTextView предоставляет метод для рисования окружности, а объект Paint заполняет ее цветом. Проще говоря, Canvas определяет, какие фигуры можно нарисовать на экране, а Paint определяет свойства нарисованных фигур — цвет, стиль, шрифт и т. д.

Давайте займемся кодом. Мы создаем объект Paint и присваиваем ему некоторые свойства, а затем рисуем фигуру на холсте (объект Canvas), используя наш объект Paint. Метод onDraw() будет выглядеть так:

IDE показывает предупреждение о том, что следует избегать выделения объектов во время операций отрисовки или операций с макетом. Это предупреждение возникает потому, что метод onDraw() много раз вызывается при отрисовке представления, в котором каждый раз создаются ненужные объекты. Поэтому, чтобы избежать ненужного создания объектов, мы вынесем соответствующую часть кода за пределы метода onDraw() , как показано ниже.

При выполнении отрисовки всегда помните о том, что следует повторно использовать объекты вместо создания новых. Ваша IDE может указать на потенциальные проблемы, но полагаться на нее не стоит. Например, она не сможет отследить случай, когда объекты создаются внутри методов, вызываемых из метода onDraw() . Поэтому лучше проверять все самостоятельно.

Шаг 5. Мы закончили с рисованием. Теперь давайте внесем этот класс представления в XML.

Добавьте этот XML-макет в вашу активность (Activity) и запустите приложение. Вот что будет на экране.

Выглядит неплохо, правда? Теперь сделаем так, чтобы значение динамическому свойству цвета в circlePaint назначалось из активности, а также добавим контур к кружку. Для этого в классе CircularTextView необходимо создать несколько методов-сеттеров, чтобы можно было вызывать эти методы и устанавливать свойства динамически.

Для начала давайте реализуем настройку цвета отрисовки. Для этого создадим сеттер, как показано ниже.

Теперь мы можем устанавливать цвет из нашей активности динамически, вызывая этот метод.

Неплохо, правда? Теперь давайте добавим контур к кружку. Контур будет задаваться двумя входными параметрами: шириной линии контура и ее цветом. Чтобы задать цвет линии контура, нам нужно создать объект Paint точно так же, как мы это делали для кружка. Чтобы задать ширину линии контура, мы создадим переменную, установим для нее нужное значение и используем его в методе onDraw() . Полный код будет выглядеть так:

Теперь в активности можно динамически настраивать эти атрибуты нужным образом.

Далее давайте запустим приложение, устанавливая различные цвета для нашего виджета.

Итак, теперь стало ясно, как динамически устанавливать свойства из активности, но возникает вопрос о том, как устанавливать атрибуты из XML. Продолжим наше исследование.

Для начала создадим файл с именем attrs.xml в папке values. Этот файл будет содержать все атрибуты для различных представлений, которые мы создаем сами. В приведенном ниже примере у нашего представления под названием CircularTextView имеется атрибут ct_circle_fill_color , который принимает значение цвета. Аналогичным образом мы можем добавить и другие атрибуты.

Затем нам нужно будет прочитать эти свойства в классе, который мы создали для реализации собственного представления. В блоке инициализации мы считываем набор атрибутов, как показано ниже.

Теперь просто переходим к XML-макету и устанавливаем значение свойства, соответствующее нужному цвету, после чего запускаем приложение. На выходе мы увидим нужный результат.

В моем случае результат был таким:

Примечание. При рисовании не задавайте жестко размер вашего представления, так как им могут воспользоваться другие разработчики с применением других размеров. Рисуйте представление в соответствии с его текущим размером.

Читайте также:  Mysql android studio примеры

Обновление представления

Итак, мы задали собственное представление. Если мы хотим обновлять представление при изменении какого-нибудь свойства или по какой-то другой причине, этого можно добиться двумя основными способами.

invalidate()

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

requestLayout()

Если в какой-то момент происходит изменение состояния представления, то метод requestLayout() сообщает системе представлений, что необходимо сделать перерасчет фаз «измерение» (Measure) и «макет» (Layout) для данного представления (измерение → макет → рисование). Проще говоря, метод requestLayout() следует вызывать в случае, когда требуется изменение границ представления.

Теперь, я надеюсь, вы знаете в общих чертах, как создавать собственные представления. Чтобы они демонстрировали отличную производительность, необходимо освоить все описанные здесь методы.

Источник

Реализация Custom View-компонента в Android

Каждый день мы пользуемся разными приложениями и несмотря на их различные цели, большинство из них очень похожи между собой с точки зрения дизайна. Исходя из этого, многие заказчики просят специфичные, индивидуальные макеты внешний вид, который не воплотило ещё ни одно приложение, чтобы сделать своё Android приложение уникальным и отличающимся от других.

Если какая-то специфичная особенность, из тех которые просит заказчик, требует особые функциональные возможности, которые невозможно сделать с помощью встроенных в Android View-компонентов, тогда нужно реализовывать собственный View-компонент (Custom View). Это не значит, что нужно всё взять и бросить, просто потребуется некоторое время на его реализацию, к тому же это довольно интересный и увлекательный процесс.

Я недавно попал в похожую ситуацию: мне нужно было создать индикатор страниц для Android ViewPager. В отличи от iOS, Android не предоставляет такой View-компонент, поэтому мне пришлось делать его самому.

Я потратил довольно много времени на его реализацию. К счастью, этот Custom View-компонент можно использовать и в других проектах, поэтому чтобы сэкономить личное время и время других разработчиков, я решил оформить всё это дело в виде библиотеки. Если вам нужен похожий функционал и не хватает времени на его реализацию собственными силами, можете взять его с этого GitHub репозитория.

Рисуем!

Так как в большинстве случаев разработка Custom View-комопонента занимает больше времени, чем работа с обычными View-компонентами, создавать их целесообразно только тогда, когда нет более простого способа реализовать специфичную особенность, или когда у вас есть ниже перечисленные проблемы, которые Custom View-компонент может решить:

  • Производительность. Если у вас есть много View-компонентов в одном layout-файле и вы хотите оптимизировать это с помощью создания единственного Custom View-компонента;
  • Большая иерархия View-компонентов, которая сложна в эксплуатации и поддержке;
  • Полностью настраиваемый View-компонент, которому нужна ручная отрисовка;

Если вы ещё не пробовали разрабатывать Custom View, то эта статья — отличная возможность окунуться в эту тему. Здесь будет показана общая структура View-компонента, как реализовывать специфичные вещи, как избежать распространённые ошибки и даже как анимировать ваш View-компонент!

Первая вещь, которую нам нужно сделать, это погрузиться в жизненный цикл View. По какой-то причине Google не предоставляет официальную диаграмму жизненного цикла View-компонента, это довольно распространённое среди разработчиков непонимание, поэтому давайте рассмотрим её.

Constructor

Каждый View-компонент начинается с Constructor’а. И это даёт нам отличную возможность подготовить его, делая различные вычисления, устанавливая значения по умолчанию, ну или вообще всё что нам нужно.

Но для того чтобы сделать наш View-компонент простым в использовании и установке, существует полезный интерфейс AttributeSet. Его довольно просто реализовать и определённо стоит потратить на это время, потому что он поможет вам (и вашей команде) в настройке вашего View-компонента с помощью некоторых статических параметров на последующих экранах. Во-первых, создайте новый файл и назовите его «attrs.xml». Этот файл может содержать все атрибуты для различных Custom View-компонентов. Как вы можете видеть в этом примере есть View-компонент названный PageIndicatorView и один атрибут piv_count.

Во-вторых, в конструкторе вашего View-компонента, вам нужно получить атрибуты и использовать их как показано ниже.

  • При создании кастомных атрибутов, добавьте простой префикс к их имени, чтобы избежать конфликтов имён с другими View-компонентами. Обычно добавляют аббревиатуру от названия View-компонента, поэтому у нас префикс «piv_»;
  • Если вы используете Android Studio, то Lint будет советовать вам использовать метод recycle() до тех пор пока вы сделаете это с вашими атрибутами. Причина заключается в том, что вы можете избавиться от неэффективно связанных данных, которые не будут использоваться снова;

onAttachedToWindow

После того как родительский View-компонент вызовет метод addView(View), этот View-компонент будет прикреплён к окну. На этой стадии наш View-компонент будет знать о других View-компонентах, которые его окружают. Если ваш View-компонент работает с View-компонентами пользователя, расположенными в том же самом «layout.xml» файле, то это хорошее место найти их по идентификатору (который вы можете установить с помощью атрибутов) и сохранить их в качестве глобальной ссылки (если нужно).

Читайте также:  What is port forwarding in android

onMeasure

Этот метод означает, что наш Custom View-компонент находится на стадии определения собственного размера. Это очень важный метод, так как в большинстве случаев вам нужно определить специфичный размер для вашего View-компонента, чтобы поместиться на вашем макете.

При переопределении этого метода, всё что вам нужно сделать, это установить setMeasuredDimension(int width, int height).

При настройке размера Custom View-компонента вы должны обработать случай, когда у View-компонента может быть определённый размер, который пользователь (прим. переводчика: программист работающий с вашим View-компонентом) будет устанавливать в файле layout.xml или программно. Для вычисления этого свойства, нужно проделать несколько шагов:

  • Рассчитать размер необходимый для содержимого вашего View-компонента (ширину и высоту);
  • Получить MeasureSpec вашего View-компонента (ширину и высоту) для размера и режима;
  • Проверить MeasureSpec режим, который пользователь устанавливает и регулирует (для ширины и высоты);

Посмотрите на значения MeasureSpec:

  1. MeasureSpec.EXACTLY означает, что пользователь жёстко задал значения размера, независимо от размера вашего View-компонента, вы должны установить определённую ширину и высоту;
  2. MeasureSpec.AT_MOST используется для создания вашего View-компонента в соответствии с размером родителя, поэтому он может быть настолько большим, насколько это возможно;
  3. MeasureSpec.UNSPECIFIED — на самом деле размер обёртки View-компонента. Таким образом, с этим параметром вы можете использовать желаемый размер, который вы расчитали выше.

Перед установкой окончательных значений в setMeasuredDimension, на всякий случай проверьте эти значения на отрицательность Это позволит избежать любых проблем в предпросмотре макета.

onLayout

Этот метод позволяет присваивать размер и позицию дочерним View-компонентам. У нас нет дочерних View-компонентов, поэтому нет смысла переопределять этот метод.

onDraw

Вот здесь происходит магия. Два объекта, Canvas и Paint, позволяют вам нарисовать всё что вам нужно. Экземпляр объекта Canvas приходит в качестве параметра для метода onDraw, и по существу отвечает за рисование различных фигур, в то время как объект Paint отвечает за цвет этой фигуры. Простыми словами, Canvas отвечает за рисование объекта, а Paint за его стилизацию. И используется он в основном везде, где будет линия, круг или прямоугольник.

Создавая Custom View-компонент, всегда учитывайте, что вызов onDraw занимает довольно много времени. При каких-то изменениях, сроллинге, свайпе вы будете перерисовывать. Поэтому Andorid Studio рекомендует избегать выделение объекта во время выполнения onDraw, вместо этого создайте его один раз и используйте в дальнейшем.

  • При отрисовке, имейте в виду переиспользование объектов вместо создания новых. Не полагайтесь на вашу IDE, которая должна подсветить потенциальную проблему, а сделайте это самостоятельно, потому что IDE может не увидеть этого, если вы создаёте объекты внутри методов вызываемых в onDraw;
  • Во время отрисовки, не задавайте размер прямо в коде. Обрабатывайте случай, когда у других разработчиков может быть тот же самый View-компонент, но с другим размером, поэтому делайте ваш View-компонент зависимым от того размера, который ему присвоен;

View Update

Из диаграммы жизненного цикла View-компонента, вы можете заметить что существует два метода, которые заставляют View-компонент перерисовываться. Методы invalidate() и requestLayout() могут помочь вам сделать ваш Custom View-компонент интерактивным, что собственно поможет изменять его внешний вид во время выполнения. Но почему их два?

Метод invalidate() используется когда просто нужно перерисовать View-компонент. Например, когда ваш View-компонент обновляет свой текст, цвет или обрабатывает прикосновение. Это значит, что View-компонент будет вызывать только метод onDraw, чтобы обновить своё состояние.

Метод requestLayout(), как вы можете заметить будет производить обновление View-компонента через его жизненный цикл, только из метода onMeasure(). А это означает, что сразу после обновления View-компонента вам нужно его измерить, чтобы отрисовать его в соответствии с новыми размерами.

Animation

Анимации в Custom View-компонентах, это по кадровый процесс. Это означает, что если вы например захотите сделать анимированным процесс изменения радиуса круга от маленького к большому, то вам нужно увеличивать его последовательно и после каждого шага вызывать метод invalidate для отрисовки.

Ваш лучший друг в анимации Custom View-компонентов — это ValueAnimator. Этот класс будет помогать вам анимировать любые значения от начала до конца и даже обеспечит поддержку Interpolator (если нужно).

Не забывайте вызывать метод Invalidate каждый раз, когда изменяется значение анимации.

Надеюсь, что эта статья поможет вам сделать свой первый Custom View-компонент. Если вы захотите получить больше информации по этой теме, то может посмотреть хорошее видео.

Источник

Оцените статью