Изучение библиотеки LVGL. Cоздание красивых интерфейсов с помощью стилей
Рассмотрим различные доступные варианты стилей в LVGL, а также способы настройки и создания собственных стилей.
Изучение библиотеки LVGL. Cоздание красивых интерфейсов с помощью стилей
29.02.2024 в 12:43   1284 0
Версия для печати

Изучение библиотеки LVGL. Создание красивых интерфейсов с помощью стилей

Введение

Эта статья является частью цикла материалов о библиотеке графического интерфейса пользователя LVGL. Ранее мы изучили базовые свойства объектов в LVGL, а в данной статье мы сосредоточимся на изучении стилей. Стили являются ключевым аспектом создания удобного и привлекательного пользовательского интерфейса, и мы рассмотрим различные доступные варианты стилей в LVGL, а также способы настройки и создания собственных стилей.

Теоретическая часть

В этой теоретической части статьи мы разберемся что представляют из себя стили в библиотеке LVGL и немного поговорим об особенностях их применения. 

Краткая информация о стилях LVGL

Стили в библиотека LVGL во многом напоминают CSS. Это означает, что вам нужно создать объект стиля, задать его параметры, такие как текст, граница, линия, размеры тени, цвета и т.д. После создания объекта стиля вы должны применить его к одному или нескольким объектам (меткам, кнопкам, переключателям и т.д.). Общая концепция заключается в следующем:
  • Стиль - это lv_style_t переменная, которая может содержать такие свойства, как ширина границы, цвет текста и так далее. Это похоже на class в CSS.
  • Объектам могут быть назначены стили для изменения их внешнего вида. При назначении может быть указана целевая часть (псевдоэлемент в CSS) и целевое состояние (псевдокласс). Например, можно добавить style_blue к ручке ползунка, когда она находится в нажатом состоянии.
  • Один и тот же стиль может использоваться любым количеством объектов.
  • Стили могут быть каскадными, что означает, что объекту может быть присвоено несколько стилей, и каждый стиль может иметь разные свойства. Следовательно, не все свойства должны быть указаны в стиле. LVGL будет искать свойство до тех пор, пока его не определит стиль, или использовать значение по умолчанию, если оно не указано ни в одном из стилей. Например, style_btn может привести к серой кнопке по умолчанию и style_btn_red может добавить только background-color=red, чтобы перезаписать цвет фона.
  • Самый последний добавленный стиль имеет более высокий приоритет. Это означает, что если свойство указано в двух стилях, то будет использоваться самый новый стиль в объекте.
  • Некоторые свойства (например, цвет текста) могут быть унаследованы от родительского элемента (ов), если он не указан в объекте.
  • Объекты также могут иметь локальные стили с более высоким приоритетом, чем "обычные" стили.
  • В отличие от CSS (где псевдоклассы описывают разные состояния, например :focus), в LVGL свойство присваивается заданному состоянию.
  • Переходы могут применяться, когда объект меняет состояние.

Состояния

Любой объект в LVGL в каждый определенный момент времени может находится в одном или сразу нескольких состояниях:
  • LV_STATE_DEFAULT (0x0000) Нормальное состояние
  • LV_STATE_CHECKED (0x0001) Переключенное состояние. Актуально для переключателей (вкл/выкл)
  • LV_STATE_FOCUSED (0x0002) В фокусе. Выделен с помощью клавиатуры, энкодера или нажатия с помощью сенсорной панели / мыши
  • LV_STATE_FOCUS_KEY (0x0004) В фокусе. Выделен с помощью клавиатуры или энкодера, но без помощм сенсорной панели / мыши
  • LV_STATE_EDITED (0x0008) Значение изменяется с помощью энкодера
  • LV_STATE_PRESSED (0x0020) Нажат
  • LV_STATE_SCROLLED (0x0040) Прокручивается
  • LV_STATE_DISABLED (0x0080) Отключенное состояние
  • LV_STATE_USER_1 (0x1000) Пользовательское состояние
  • LV_STATE_USER_2 (0x2000) Пользовательское состояние
  • LV_STATE_USER_3 (0x4000) Пользовательское состояние
  • LV_STATE_USER_4 (0x8000) Пользовательское состояние
Объект может находиться в комбинации состояний. Например одновременная фокусировка и нажатие. Записать это можно так LV_STATE_FOCUSED | LV_STATE_PRESSED.
Каждое состояние имеет собственный приоритет. В списке выше он указан в скобках. При изменении состояния объекта будут применены свойства состояния с большим приоритетом. Например укажем цвет фона для 3 состояний объекта:
  • LV_STATE_DEFAULT: белый
  • LV_STATE_PRESSED: серый
  • LV_STATE_FOCUSED: красный

В обычном состоянии LV_STATE_DEFAULT с приоритетом 0x0000 цвет фона объекта будет белым. При нажатии его состояние изменится на LV_STATE_PRESSED. Его приоритет 0х0020 выше чем приоритет состояния по умолчанию 0х0000. Поэтому цвет фона изменится на серый. При отпускании нажатия объект все еще остается в фокусе и его состояние переключится в LV_STATE_FOCUSED, имеющее приоритет 0x0002. Он выше значения по умолчанию. Поэтому цвет фона объекта изменится на красный.

Пойдем немного дальше. Зададим цвет для комбинации состояний:

LV_STATE_PRESSED | LV_STATE_FOCUSED: зеленый

Теперь при нажатии и удержании на объекте он будет находится сразу в 2 состояниях LV_STATE_PRESSED и LV_STATE_FOCUSED. Приоритет будет равен сумме приоритетов этих двух состояний. То есть 0х0020 + 0х0002 = 0х0022. Он выше чем приоритет нажатого состояния 0х0020, поэтому цвет фона изменится на зеленый.

Каскадные стили

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

Это очень похоже на CSS, когда используемые классы перечислены следующим образом <div class=".btn .btn-red">.

Стили, добавленные позже, имеют приоритет над ранее установленными. Итак, в примере с серой / красной кнопкой, приведенном выше, в первую очередь следует добавить обычный стиль кнопки, а во вторую - красный. Тем не менее, приоритет состояний все еще принимается во внимание. Итак, давайте рассмотрим следующий случай:

  • основной стиль кнопки определяет темно-серый цвет для состояния по умолчанию и светло-серый цвет для нажатого состояния

  • стиль красной кнопки определяет цвет фона как красный только в состоянии по умолчанию

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

Наследование

Некоторые свойства (обычно относящиеся к тексту) могут быть унаследованы от стилей родительского объекта. Наследование применяется, только если данное свойство не задано в стилях объекта (даже в состоянии по умолчанию). В этом случае, если свойство является наследуемым, поиск значения свойства в родительском объекте будет идти до тех пор, пока объект не укажет значение для него. Родители будут использовать свое собственное состояние для определения значения. Например если есть объект Экран с установленным значением Цвет текста зеленый, то создав на нем дочерний объект Кнопка с ее дочерним объектом Надпись, цвет текста для надписи будет унаследован от родительского объекта Экран и будет зеленым.

Принудительное наследование значений / значение по умолчанию

Иногда может потребоваться заставить дочерний объект использовать родительское значение для данного свойства стиля. Для этого вы можете использовать одну из следующих функций (в зависимости от того, какой тип стиля вы используете):


/* обычный стиль */
lv_style_set_prop_meta(&style, LV_STYLE_TEXT_COLOR, LV_STYLE_PROP_META_INHERIT);
/* локальный стиль */
lv_obj_set_local_style_prop_meta(child,LV_STYLE_PROP_META_INHERIT, LV_PART_MAIN);

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

Вы также можете захотеть принудительно использовать значение свойства по умолчанию, без необходимости жестко кодировать его в вашем приложении. Для этого вы можете использовать тот же API, но с LV_STYLE_PROP_META_INITIAL вместо этого. 

Части объекта

В LVGL существуют следующие предопределенные частей объекта:

  • LV_PART_MAIN Основная часть объекта. Или фон представляющий собой прямоугольник
  • LV_PART_SCROLLBAR Полоса (полосы) прокрутки
  • LV_PART_INDICATOR Индикатор, например, для ползунка, панели, переключателя или флажка чекбокса
  • LV_PART_KNOB Перемещаемая часть ползунка, или так называемая, ручка за которую перетаскивают, чтобы изменить значение
  • LV_PART_SELECTED Выбранный в данный момент параметр или раздел в списках
  • LV_PART_ITEMS Используется, если виджет содержит несколько похожих элементов (например, ячейки таблицы)
  • LV_PART_TICKS Отметки на шкалах, например, для диаграммы или счетчика
  • LV_PART_CURSOR Курсор текстовой области или диаграммы
  • LV_PART_CUSTOM_FIRST Пользовательские части могут быть добавлены здесь

Например, объект Слайдер состоит из трех частей:

  • LV_PART_MAIN - Фон
  • LV_PART_INDICATOR - Индикатор
  • LV_PART_KNOB - Ручка

Программная часть

Немного разобравшись с теорией, самое время узнать как использовать стили LVGL в ваших проектах.

Инициализация стилей и установка / получение свойств

Стили хранятся в lv_style_t переменных. Переменные стиля должны быть static, глобальными или динамически распределяемыми. Другими словами, они не могут быть локальными переменными в функциях, которые уничтожаются при завершении функции. Перед использованием стиля его следует инициализировать с помощью lv_style_init(&my_style). После инициализации стиля, можно добавлять или изменять его свойства

Функция установки свойства в общем виде выглядит так:

lv_style_set_<property_name>(&style, <value>);

Создадим, например, пару стилей для кнопок и добавим им несколько свойств:


static lv_style_t style_btn; //объявляем переменную стиля style_btn
lv_style_init(&style_btn); //Инициализируем стиль
lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588)); //устанавливаем цвет фона введя его значение в HEX 0x115588
lv_style_set_bg_opa(&style_btn, LV_OPA_50); //устанавливаем значение прозрачности фона равным 50%
lv_style_set_border_width(&style_btn, 2); //устанавливаем ширину рамки кнопки равной 2 пикселя
lv_style_set_border_color(&style_btn, lv_color_black()); //устанавливаем цвет рамки кнопки черным с помощью предустановленного значения цвета библиотеки
static lv_style_t style_btn_red; //объявляем новую переменную стиля style_btn_red
lv_style_init(&style_btn_red); //инициализируем её
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED)); //устанавливаем цвет фона красный с помощью выбора предустановленного цвета из палитры LVGL
lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER); //устанавливаем полностью непрозрачный цвет фона

Чтобы удалить свойство, используйте:


lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);

Чтобы получить значение свойства из стиля:

lv_style_value_t v;
lv_res_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
if(res == LV_RES_OK) { /*Found*/
 do_something(v.color);
}

lv_style_value_t содержит 3 поля:

  • num для свойств целых чисел, логических значений и непрозрачности
  • color для свойств цвета
  • ptr для свойств указателя

Чтобы сбросить стиль (удалить все его данные), используйте:

lv_style_reset(&style);

Для экономии памяти стили могут быть объявлены как константы:

const lv_style_const_prop_t style1_props[] = {
 LV_STYLE_CONST_WIDTH(50),
 LV_STYLE_CONST_HEIGHT(50),
 LV_STYLE_CONST_PROPS_END
};

LV_STYLE_CONST_INIT(style1, style1_props);

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

Добавление и удаления стиля объекта

Чтобы использовать стиль его нужно добавить к объекту. Для этого используется функция:

lv_obj_add_style(obj, &style, <selector>). 

selector - это упорядоченное значение частей и состояния, к которым должен быть добавлен стиль. Например:

  • LV_PART_MAIN | LV_STATE_DEFAULT - Основная часть объекта в состоянии по умолчанию
  • LV_STATE_PRESSED: Основная часть объекта в нажатом состоянии. LV_PART_MAIN может быть опущен
  • LV_PART_SCROLLBAR: Полоса прокрутки в состоянии по умолчанию. LV_STATE_DEFAULT может быть опущен.
  • LV_PART_SCROLLBAR | LV_STATE_SCROLLED: Полоса прокрутки при прокрутке объекта
  • 0 То же, что LV_PART_MAIN | LV_STATE_DEFAULT.
  • LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED Часть индикатора, когда объект одновременно нажимается и включен (например стоит галка в чекбоксе).

Например:

lv_obj_add_style(btn, &style_btn, 0); // Стиль кнопки по умолчанию 
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); // стиль кнопки при нажатии на нее
 

Удаление стиля объекта

Чтобы удалить все стили из объекта, используйте lv_obj_remove_style_all(obj).

Для удаления определенных стилей используйте lv_obj_remove_style(obj, style, selector). Эта функция удалит, style только если selector совпадет с selector используемым в lv_obj_add_style

Вместо значения style можно использовать NULL и проверить только selector, удалив при этом все совпадающие стили. 

В качестве selector можно использовать значения LV_STATE_ANY и LV_PART_ANY для удаления стиля из любого состояния или части.

Уведомление об изменении стиля

Если стиль, который уже присвоен объекту, изменяется (то есть добавляется или изменяется свойство), объекты, использующие этот стиль, должны быть уведомлены. Для этого есть 3 варианта:

  • Если вы знаете, что измененные свойства могут быть применены простым перерисовыванием (например, изменением цвета или непрозрачности), просто вызовите lv_obj_invalidate(obj) или lv_obj_invalidate(lv_scr_act()).
  • Если были изменены или добавлены более сложные свойства стиля, и вы знаете, на какие объекты влияет этот вызов стиля lv_obj_refresh_style(obj, part, property). Чтобы обновить все части и свойства, используйте lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY).

     

  • Чтобы заставить LVGL проверять все объекты на предмет использования ими стиля и обновлять их при необходимости, вызовите lv_obj_report_style_change(&style). Если style это NULL. При этом все объекты будут уведомлены об изменении стиля.

Получить значение свойства для объекта

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

lv_obj_get_style_<property_name>(obj, <part>)
.

Эта функции используют текущее состояние объекта, и если лучшего кандидата не существует, они возвращают значение по умолчанию. Например:

lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);

Локальные стили

В дополнение к обычным стилям, объекты также могут использовать локальные стили. Эта концепция похожа на встроенные стили в CSS (например, <div style="color:red">) с некоторыми изменениями.

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

В отличие от CSS, локальные стили LVGL могут быть назначены состояниям (псевдоклассам) и частям (псевдоэлементам).

Чтобы задать локальное свойство, используются такие функции, как lv_obj_set_style_<property_name>(obj, <value>, <selector>); Например:

lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);

Переходы

По умолчанию, когда объект меняет состояние (например, он нажат), новые свойства из нового состояния устанавливаются немедленно. Однако с помощью переходов можно воспроизводить анимацию при изменении состояния. Например, при нажатии кнопки ее фоновый цвет может быть анимирован в соответствии с нажатым цветом в течение 300 мс.

Параметры переходов хранятся в стилях. Можно установить

  • Время перехода
  • Задержка перед началом перехода
  • Путь анимации (также известный как функция синхронизации или смягчения)
  • Свойства для анимации

Свойства перехода могут быть определены для каждого состояния. Например, установка времени перехода в состояние DEFAULT в 500 мс означает, что когда объект переходит в состояние DEFAULT, анимация времени перехода будет занимать 500 мс. Установка времени перехода в  состоянии PRESSED  100 мс означает, что переход в нажатое состояние будет занимать 100 мс. В итоге при нажатии на объект он быстро покажет анимацию перехода в нажатое состояние, потом при отпускании кнопки он с плавной анимацией вернется в свое обычное состояние. 

Для описания перехода lv_transition_dsc_t переменную необходимо инициализировать и добавить к стилю:


/* Сохраняется только его указатель, поэтому он должен быть статическим, глобальным или динамически распределяемым */
static const lv_style_prop_t trans_props[] = {LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,0}; //0 - конечный маркер

static lv_style_transition_dsc_t trans1;
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);

lv_style_set_transition(&style1, &trans1);

Прозрачность, режимы наложения и преобразования

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

Эти свойства оказывают такое влияние только на MAIN часть виджета.

Созданный снимок называется "промежуточный слой" или просто "layer". Если для параметра только opa  и/или blend_mode установлено значение, отличное от значения по умолчанию, LVGL может создавать слой из меньших фрагментов. Размер этих фрагментов может быть настроен с помощью следующих свойств в lv_conf.h:

  • LV_LAYER_SIMPLE_BUF_SIZE: [байты] оптимальный размер целевого буфера. LVGL попытается выделить этот объем памяти.

  • LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [байты], используемые, если LV_LAYER_SIMPLE_BUF_SIZE не удалось выделить.

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

Если виджет может полностью покрывать область для перерисовки, LVGL создает слой RGB (который быстрее визуализируется и использует меньше памяти). В противном случае необходимо использовать рендеринг ARGB. Виджет может не охватывать свою область, если он имеет радиус, bg_opa != 255, имеет тень, контур и т.д.

Область клика виджета также соответствующим образом преобразовывается.

Темы

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

У каждого дисплея может быть своя тема. Например, у вас может быть цветная тема на TFT-дисплее и монохромная тема на дополнительном монохромном дисплее.

Чтобы задать тему для отображения, требуется выполнить два шага:

  1. Инициализировать тему
  2. Назначить экрану инициализированную тему.

Функции инициализации темы могут иметь разные прототипы. В этом примере показано, как установить тему "по умолчанию":


lv_theme_t * th = lv_theme_default_init(display, /* Использовать DPI, размер и т.д. этого дисплея */
 LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN, / * Основная и дополнительная палитры * /
 false, / * Светлый (false) или темный (true) режим */
 & lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Мелкий, обычный, крупный шрифты */

lv_disp_set_theme(display, th); /*Назначить тему для дисплея */

Встроенные темы активированы в lv_conf.h. Если тема по умолчанию включена LV_USE_THEME_DEFAULT 1, LVGL автоматически инициализирует и устанавливает ее при создании дисплея.

Расширение тем

Встроенные темы могут быть расширены. Если создается пользовательская тема, можно выбрать родительскую тему. Стили родительской темы будут добавлены перед стилями пользовательской темы. Таким образом можно объединить любое количество тем. Например. тема по умолчанию -> пользовательская тема -> темная тема.

lv_theme_set_parent(new_theme, base_theme) расширяет base_theme с помощью new_theme .

Практическая часть

Настало время перейти к практической части. Возьмем пример из статьи Начало работы с библиотекой LVGL. Первые шаги в создании графических интерфейсов и внесем в него некоторые изменения.

Создадим новые стили для кнопки и надписи. Для этого добавим следующий код:

static lv_style_t style1; //объявляем переменную стиля
lv_style_init(&style1); //инициализируем стиль
lv_style_set_text_color(&style1, lv_palette_main(LV_PALETTE_GREEN)); //цвет текста зеленый
lv_style_set_border_color(&style1, lv_palette_main(LV_PALETTE_RED)); //цвет границы красный
lv_style_set_border_width(&style1, 2); //ширина границы 2 пикселя
lv_style_set_bg_color(&style1, lv_palette_main(LV_PALETTE_YELLOW)); //цвет фона желтый
lv_style_set_bg_opa(&style1, 255); //непрозрачность максимальная
lv_style_set_radius(&style1, 1); //радиус закругления углов 1

static lv_style_t bigStyle; //объявляем новую переменную стиля
lv_style_init(&bigStyle); //инициализируем ее
lv_style_set_text_font(&bigStyle, &lv_font_montserrat_48); //устанавливаем новый шрифт

Мы создали два новых стиля style1 и bigStyle и задали для них некоторые параметры. В стиле style1 мы добавляем цвет границы (красный) и устанавливаем ее ширину в 2 пикселя, устанавливаем желтый цвет фона и максимальную непрозрачность. Наконец, мы меняем радиус границы на 1.

В стиле bigStyle мы просто изменим шрифт по умолчанию на другой чуть большего размера.

Теперь добавим созданные стили к имеющимся объектам btn1 и label

lv_obj_add_style(label, &bigStyle,0);
lv_obj_add_style(btn1, &style1,0);

В данном случае мы просто воспользовались упрощенной записью функции для добавления стиля к основной части объекта для состояния по умолчанию. Напомню что до применения стилей объекты на экране выглядели так:

старый вид

После применения стилей вид элементов стал таким:

применение стилей

Таким образом вы можете изменить внешний вид любого элемента интерфейса.

И, наконец, установим темную тему экрана. Для этого объявим переменную дисплея disp типа lv_disp_t и зарегистрируем драйвер дисплея для работы с ним:

 lv_disp_t * disp = lv_disp_drv_register( &disp_drv ); //регистрируем драйвер дисплея и сохраним его настройки

Теперь инициализируем стандартную тему и назначим её дисплею:

lv_theme_t * th = lv_theme_default_init( disp,lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_CYAN),true,&lv_font_montserrat_14); //инициализация стандартной темы в темном режиме

lv_disp_set_theme(disp, th); //Назначить тему для дисплея 

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

темная тема

Полный текст программы представлен ниже

#include <lvgl.h>
#include <TFT_eSPI.h>
#include "touch.h"

static const uint16_t screenWidth = 480; //ширина экрана
static const uint16_t screenHeight = 320; //высота экрана

//Объявление служебных переменных для LVGL
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 6];
lv_obj_t * btn1;
lv_obj_t * screenMain;
lv_obj_t * label;


TFT_eSPI tft = TFT_eSPI(); //инициализация библиотеки TFT_eSPI 

//Здесь напишем функцию для вывода содержимого буфера на экран
void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p )
{
 uint32_t w = ( area->x2 - area->x1 + 1 );
 uint32_t h = ( area->y2 - area->y1 + 1 );

 tft.startWrite();
 tft.setAddrWindow( area->x1, area->y1, w, h );
 tft.pushColors( ( uint16_t * )&color_p->full, w * h, true );
 tft.endWrite();

 lv_disp_flush_ready( disp );
}
// Вычисление координат касания
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
 if (touch_has_signal()) //если есть касание
 {
 if (touch_touched())
 {
 data->state = LV_INDEV_STATE_PR; //сообщаем библиотеке, что есть касание и удерживается

 //Отправка координат
 data->point.x = touch_last_x; //координата касания по X
 data->point.y = touch_last_y; //координата касания по Y
 }
 else if (touch_released())
 {
 data->state = LV_INDEV_STATE_REL; //сообщаем библиотеке, что касания больше нет
 }
 }
 else
 {
 data->state = LV_INDEV_STATE_REL;
 }
}
//Функция обработки нажатия экранной кнопки
static void event_handler_btn(lv_event_t * event){
 static uint32_t cnt = 1;
 lv_obj_t * btn = lv_event_get_target(event);
 lv_obj_t * label = lv_obj_get_child(btn, 0);
 lv_label_set_text_fmt(label, "%"LV_PRIu32, cnt);
 cnt++;
 }


void setup()
{ 
 Serial.begin( 115200 ); //открытие серийного порта
 touch_init(); //иницилизация тача 
 lv_init();//инициализация LVGL
 
 //инициализация дисплея в библиотеке TFT_ESPi и изменение его ориентации на альбомную
 tft.begin(); 
 tft.setRotation(1); 
 //Далее идут функции настройки LVGL 
 lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * screenHeight / 6 ); //создаем буфер для вывода информации на экран
 
 //далее идет настройка параметров экрана
 static lv_disp_drv_t disp_drv; //объявляем переменную для хранения драйвера дисплея
 lv_disp_drv_init( &disp_drv ); //базовая инициализация драйвера
 disp_drv.hor_res = screenWidth; //ширина экрана
 disp_drv.ver_res = screenHeight; //высота экрана
 disp_drv.flush_cb = my_disp_flush; //функция которая выводит содержимое буфера в заданное место экрана. Указываем имя функции которую мы написали выше
 disp_drv.draw_buf = &draw_buf; //объявляем библиотеке, что содержимое буфера экрана находится в переменной draw_buf
 lv_disp_t * disp = lv_disp_drv_register( &disp_drv ); //регистрируем драйвер дисплея и сохраняем его настройки
 // Инициализируем драйвер тачскрина
 static lv_indev_drv_t indev_drv; //объявляем переменные для хранения драйвера тачскрина
 lv_indev_drv_init( &indev_drv ); // базовая инициализация драйвера
 indev_drv.type = LV_INDEV_TYPE_POINTER; //указываем тип драйвера. В данном случае это тачскрин
 indev_drv.read_cb = my_touchpad_read; //указываем имя функции обработчика нажатий на тачскрин, которую мы создали
 lv_indev_drv_register( &indev_drv ); //регистрация драйвера тачскрина и сохранение его настроек

lv_theme_t * th = lv_theme_default_init( disp,lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_CYAN),true,&lv_font_montserrat_14); //инициализация стандартной темы в темном режиме

lv_disp_set_theme(disp, th); //Назначить тему для дисплея 

static lv_style_t style1; //объявляем переменную стиля
lv_style_init(&style1); //инициализируем стиль
lv_style_set_text_color(&style1, lv_palette_main(LV_PALETTE_GREEN)); //цвет текста зеленый
lv_style_set_border_color(&style1, lv_palette_main(LV_PALETTE_RED)); //цвет границы красный
lv_style_set_border_width(&style1, 2); //ширина границы 2 пикселя
lv_style_set_bg_color(&style1, lv_palette_main(LV_PALETTE_YELLOW)); //цвет фона желтый
lv_style_set_bg_opa(&style1, 255); //непрозрачность максимальная
lv_style_set_radius(&style1, 1); //радиус закругления углов 1

static lv_style_t bigStyle; //объявляем новую переменную стиля
lv_style_init(&bigStyle); //инициализируем ее
lv_style_set_text_font(&bigStyle, &lv_font_montserrat_48); //устанавливаем новый шрифт


 //Создаем экранные объекты
 lv_obj_t * screenMain = lv_obj_create(NULL); //создаем экранный объект, который будет содержать все другие объекты
//Создадим объект надпись и опишем его свойства
 label = lv_label_create(screenMain); //создаем объект Надпись как дочерний объект screenMain
 lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); //текст можно переносить по строкам если не вмещается
 lv_label_set_text(label, "Press the button!"); //сам текст для надписи
 lv_obj_set_size(label, 460, 80); //размеры надписи
 lv_obj_set_pos(label, 0, 15); //положение на экране
 lv_obj_add_style(label, &bigStyle,0);
//создадим объект Кнопка
 btn1 = lv_btn_create(screenMain); //создаем объект Кнопка как дочерний объект screenMain
 lv_obj_add_event_cb(btn1, event_handler_btn, LV_EVENT_CLICKED, NULL); //функция, которая вызывается при нажатии на кнопку
 lv_obj_set_width(btn1, 120); //ширина
 lv_obj_set_height(btn1, 30); //высота
 lv_obj_set_pos(btn1,10, 80); //положение
 //далее создадим надпись на кнопке
 lv_obj_t * label1 = lv_label_create(btn1); //создаем объект Надпись как дочерний объект созданной ранее кнопки
 lv_label_set_text(label1, "Press me!"); //Надпись на кнопке
// далее выводим все на экран
lv_obj_add_style(btn1, &style1,0);
 lv_scr_load(screenMain);

}


void loop()
{
 //функция обновления экрана и параметров LVGL 
 lv_timer_handler(); 
 delay( 10 );
}

Заключение

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

Материал также доступен на моем канале: Яндекс Дзен и в группе ВК
Категория: LVGL (Light and Versatile Graphics Library) | Добавил: :, (24.04.2023)
Просмотров: 1284 | Теги: LVGL, TFT_eSPI, ESP8266, ESP32, Arduino IDE | Рейтинг: 0.0/0
Поделиться:
Всего комментариев: 0
avatar