Изучение библиотеки LVGL. Базовые свойства объектов
Введение
В данной статье мы продолжим изучение библиотеки LVGL, которая является мощным инструментом для создания пользовательских интерфейсов на микроконтроллерах. В предыдущей статье мы рассмотрели основы настройки и использования библиотеки, а также разобрали простой проект с использованием LVGL.
Теперь мы глубже погрузимся в изучение библиотеки и рассмотрим такой её важный элемент как объекты. Объекты - это основные элементы пользовательского интерфейса, такие как кнопки, полосы прокрутки, метки и т.д. В данной статье мы сфокусируемся на базовых свойствах всех типов объектов. Узнаем, как их создавать , изменять размеры, параметры и свойства. Понимание этих основных свойств объектов позволит нам эффективно использовать LVGL для создания красивых и функциональных пользовательских интерфейсов на микроконтроллерах.
Общие сведения по объектам
Объекты в LVGL представляют собой основные элементы пользовательского интерфейса, которые используются для создания интерактивных приложений. Они могут быть различных типов, таких как кнопки, полосы прокрутки, метки, изображения и многое другое. Объекты создаются в графическом контексте, который управляется библиотекой LVGL.
lv_obj_set_align(obj, LV_ALIGN_CENTER);
lv_obj_set_pos(obj, 10, 20);
// Или в одной функции
lv_obj_align(obj, LV_ALIGN_CENTER, 10, 20);
Каждый объект имеет уникальный идентификатор, который используется для обращения к нему из программного кода. Объекты могут быть созданы как на верхнем уровне экрана, так и вложены друг в друга для создания более сложных интерфейсов.
Каждый объект имеет свой набор свойств, которые могут быть настроены для изменения его внешнего вида и поведения. Например, свойства объекта могут быть использованы для задания его размера, цвета, шрифта и т.д. Кроме того, объекты могут быть связаны с обработчиками событий, такими как нажатие на кнопку или изменение значения ползунка.
Типы объектов в LVGL
LVGL предоставляет множество типов объектов или, так называемых виджетов, для создания пользовательских интерфейсов. Некоторые из наиболее распространенных типов объектов в LVGL:
- Кнопка (lv_btn) - элемент интерфейса, который может быть нажат пользователем.
- Метка (lv_label) - объект, который отображает текст на экране.
- Изображение (lv_img) - объект, который отображает изображение на экране.
- Список (lv_list) - объект, который позволяет выбирать один или несколько элементов из списка.
- Ползунок (lv_slider) - объект, который позволяет пользователю выбирать значение из диапазона.
- Поле ввода (lv_textarea) - объект, который позволяет пользователю вводить текст.
- Таблица (lv_table) - объект, который представляет собой таблицу с ячейками.
Каждый из этих объектов имеет ряд свойств:
- Базовые свойства. Размер, положение, видимость и т.п. Они есть у всех объектов.
- Специальные свойства. Шрифт, формат текста, действие при нажатии, рисунок и т.п. Они зависят от конкретного объекта. У других виджетов их может и не быть.
В рамках данной статьи мы рассмотрим только базовые свойства присущие всем объектам LVGL.
Объявление объекта
Ссылки на все объекты выполняются с использованием lv_obj_t
указателя в качестве дескриптора. Этот указатель впоследствии может быть использован для установки или получения атрибутов объекта.
lv_obj_t * btn1;
В дальнейшем обращаться к данному объекту мы сможем по имени указателя btn1.
Создание и удаление объекта
У каждого виджета есть своя собственная функция create с прототипом, подобным этому:
lv_obj_t * lv_<виджет>_create(lv_obj_t * родитель, <другие параметры );,,,
Обычно функции создания имеют только параметр родитель, указывающий им, на каком объекте создавать новый виджет.
Возвращаемое значение представляет собой указатель на созданный объект с lv_obj_t *
тип.
Например, для создания кнопки используется следующий код:
lv_obj_t * btn1 = lv_btn_create(screenMain);
В данном случаем, мы создаем объект типа lv_btn, родителем котором является объект screenMain и присваиваем ссылку на него указателю btn1.
В дальнейшем все действия с объектом можно выполнять с использованием его указателя. Например для изменения ширины кнопки можно использовать функцию:
lv_obj_set_width(btn1, 120); //ширина
Здесь мы для объекта на который ссылается указатель btn1, устанавливаем ширину 120 пикселей.
Также важно понимать, что объекты созданные таким образом, доступны лишь в пределах функции в которой они были объявлены. То есть, если вы, например, в функции void setup() создадите объект btn1, то в дальнейшем при попытке обратиться к нему из другой функции вы получите ошибку. Чтобы этого избежать, можно объявить глобальную ссылку на объект. Как, например, в этом примере:
#include "touch.h"
lv_obj_t * btn1;
lv_obj_t * screenMain;
...
...
...
void setup()
{ ...
...
screenMain = lv_obj_create(NULL);
btn1 = lv_btn_create(screenMain);
...
...
}
В результате, мы создадим глобальные ссылки screenMain и btn1 и, после создания объектов, можем обращаться к ним из любого места программы. Но данный подход имеет существенный минус. Объекты постоянно будут висеть в оперативной памяти устройства и отбирать драгоценные килобайты.
Для немедленного удаления объекта используется функция lv_obj_del(lv_obj_t * obj). Она удаляет объект и все его дочерние элементы. Например, для удаления btn1 из предыдущего примера достаточно написать:
lv_obj_del(btn1);
Если по какой-либо причине вы не можете удалить объект немедленно, вы можете использовать lv_obj_del_async(obj)
который выполнит удаление при следующем вызове lv_timer_handler()
. Это полезно, например, если вы хотите удалить родительский объект в LV_EVENT_DELETE
обработчике дочернего объекта.
Вы можете удалить все дочерние элементы объекта (но не сам объект) с помощью lv_obj_clean(obj)
.
Вы можете использовать lv_obj_del_delayed(obj, 1000)
для удаления объекта через некоторое время. Задержка выражается в миллисекундах.
Родительско-дочерняя структура
При создании любого объекта, кроме экрана, обязательно указывается его родитель. Родительский объект может рассматриваться как контейнер для его дочерних элементов. У каждого объекта есть ровно один родительский объект (кроме экранов), но у родителя может быть любое количество дочерних элементов. Нет ограничений на тип родительского объекта, но есть объекты, которые обычно являются родительскими (например, кнопка) или дочерними (например, метка).
Если позиция родительского элемента изменяется, дочерние элементы будут перемещаться вместе с ним. Следовательно, все координаты указываются относительно родительского объекта. Это иллюстрирует следующий код:
lv_obj_t * parent = lv_obj_create(lv_scr_act()); /*Создадим родительский объект на текущем экране*/
lv_obj_set_size(parent, 100, 80); /*Установим размер родительского объекта*/
lv_obj_t * obj1 = lv_obj_create(parent); /*Создадим объект на предварительно созданном родительском объекте*/
lv_obj_set_pos(obj1, 10, 10); /*Установить позицию дочернего объекта относительно родительского*/
Схематичный результат выполнения программы вы можете увидеть на картинке ниже:
Теперь попробуем изменить позицию родительского объекта:
lv_obj_set_pos(parent, 50, 50); /*Перемещаем родителя. Дочерний элемент переместится вместе с ним*/
Дочерний элемент также изменит свое положение вместе с родительским:
Если дочерний элемент частично или полностью находится за пределами своего родительского элемента, то внешние части не будут видны. Например переместим дочерний элемент немного влево:
lv_obj_set_x(obj1, -30); /* Немного отодвинем дочерний элемент влево*/
На экране вы увидите следующее:
Часть дочернего элемента за пределами родительского невидима. Это поведение может быть изменено с помощью lv_obj_add_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE);
. Тогда картинка на экране будет выглядеть несколько иначе:
Дисплей и экраны
На самом высоком уровне иерархии объектов LVGL находится дисплей, который представляет драйвер для устройства отображения (физического дисплея или симулятора). С дисплеем может быть связан один или несколько экранов. Каждый экран содержит иерархию объектов для графических виджетов, представляющих макет, который охватывает весь дисплей.
Когда вы создадите подобный экран lv_obj_t * screen = lv_obj_create(NULL)
, вы можете сделать его активным с помощью lv_scr_load(screen)
.
Функция lv_scr_act()
дает вам указатель на активный экран.
Если у вас несколько дисплеев, важно знать, что функции экрана работают на самом последнем созданном дисплее или на том, который явно выбран с помощью lv_disp_set_default
.
Чтобы узнать экран к которому относится объект, используйте функцию lv_obj_get_screen(obj)
.
Размер и положение объекта
Размер объекта могут быть изменены по отдельным осям с помощью
lv_obj_set_width(obj, new_width)
и lv_obj_set_height(obj, new_height)
Размер по обеим осям может быть изменён одновременно с помощью
lv_obj_set_size(obj, new_width, new_height)
. Например:
lv_obj_set_width(btn1, 100); //устанавливаем ширину 100 пикселей для объекта btn1
lv_obj_set_height(btn1, 60); //Устанавливаем высоту 60 пикселей для объекта btn1
lv_obj_set_size(btn1, 100, 60); //Устанавливаем для объекта btn1 ширину 100 и высоту 60 пикселей
Вы можете задать положение относительно родительского объекта с помощью
lv_obj_set_x(obj, new_x)
и lv_obj_set_y(obj, new_y)
Или для обеих осей одновременно с помощью
lv_obj_set_pos(obj, new_x, new_y)
. Например:
lv_obj_set_x(btn1, 50); //устанавливаем для объекта btn1 положение по оси Х равным 50 пикселям
lv_obj_set_y(btn1, 60); //устанавливаем для объекта btn1 положение по оси У равным 60 пикселям
lv_obj_set_pos(btn1, 50, 60); //устанавливаем координаты для объекта btn1 по оси X=50, а по оси Y=60
Выравнивание
Вы можете выровнять объект по его родительскому элементу с помощью lv_obj_set_align(obj, LV_ALIGN_...)
. После этого все настройки x и y будут соответствовать установленному режиму выравнивания. Например, это приведет к смещению объекта на 10 и 20 пикселей от центра его родительского объекта:
lv_obj_set_align(obj, LV_ALIGN_CENTER);
lv_obj_set_pos(obj, 10, 20);
// Или в одной функции
lv_obj_align(obj, LV_ALIGN_CENTER, 10, 20);
Для выравнивания одного объекта относительно другого используется функция: lv_obj_align_to(obj_to_align, obj_reference, LV_ALIGN_..., x, y)
Например, чтобы выровнять текст под изображением:
lv_obj_align_to(text, image, LV_ALIGN_OUT_BOTTOM_MID, 0, 10)
Существуют следующие типы выравнивания:
Дополнительные атрибуты объектов
Есть некоторые атрибуты, которые могут быть включены / отключены с помощью lv_obj_add/clear_flag(obj, LV_OBJ_FLAG_...)
:
LV_OBJ_FLAG_HIDDEN
Делает объект скрытым. (Как будто его там вообще не было)LV_OBJ_FLAG_CLICKABLE
Делает объект интерактивным, то есть реагирующим на касание или нажатие кнопки мышиLV_OBJ_FLAG_CLICK_FOCUSABLE
При нажатии на объект добавляется сфокусированное состояниеLV_OBJ_FLAG_CHECKABLE
При нажатии на объект переключается его состояние. По логике похоже на чекбокс.LV_OBJ_FLAG_SCROLLABLE
включить/выключить прокрутку объекта если он не вмещается на экранLV_OBJ_FLAG_SCROLL_ELASTIC
Плавная прокрутка, но с меньшей скоростьюLV_OBJ_FLAG_SCROLL_MOMENTUM
Прокрутка обладает инерциейLV_OBJ_FLAG_SCROLL_ONE
Разрешить прокрутку только одного дочернего объекта с возможностью привязкиLV_OBJ_FLAG_SCROLL_CHAIN_HOR
Разрешить распространение горизонтальной прокрутки на родительский объектLV_OBJ_FLAG_SCROLL_CHAIN_VER
Разрешить распространение вертикальной прокрутки на родительский объектLV_OBJ_FLAG_SCROLL_CHAIN
объединяет в себе действие этих двух флагов (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER
)LV_OBJ_FLAG_SCROLL_ON_FOCUS
Автоматически прокручивать объект, чтобы сделать его видимым при фокусировкеLV_OBJ_FLAG_SCROLL_WITH_ARROW
Разрешить прокрутку сфокусированного объекта с помощью клавиш со стрелкамиLV_OBJ_FLAG_SNAPPABLE
Если привязка прокрутки включена на родительском объекте, он может привязываться к этому объектуLV_OBJ_FLAG_PRESS_LOCK
Сохранять нажатое состояние объекта, даже если курсор или точка касания вышли за пределы объектаLV_OBJ_FLAG_EVENT_BUBBLE
Распространять события и на родительский объектLV_OBJ_FLAG_GESTURE_BUBBLE
Распространить жесты на родительский объектLV_OBJ_FLAG_ADV_HITTEST
Более точное определение координат при касании объекта. Например для объектов сложной формыLV_OBJ_FLAG_IGNORE_LAYOUT
Игнорировать слои расположения объектов. То есть не позволять перекрывать его другими объектамиLV_OBJ_FLAG_FLOATING
Не прокручивать объект, когда прокручивается родительский объект и разместить его поверх всех объектов.LV_OBJ_FLAG_OVERFLOW_VISIBLE
Не обрезать дочерний объект если он выходит за границы родительскогоLV_OBJ_FLAG_LAYOUT_1
Пользовательский флаг, свободно используемый макетамиLV_OBJ_FLAG_LAYOUT_2
Пользовательский флаг, свободно используемый макетамиLV_OBJ_FLAG_WIDGET_1
Пользовательский флаг, свободно используемый виджетомLV_OBJ_FLAG_WIDGET_2
Пользовательский флаг, свободно используемый виджетомLV_OBJ_FLAG_USER_1
Пользовательский флаг, свободно используемый пользователемLV_OBJ_FLAG_USER_2
Пользовательский флаг, свободно используемый пользователемLV_OBJ_FLAG_USER_3
Пользовательский флаг, свободно используемый пользователемLV_OBJ_FLAG_USER_4
Пользовательский флаг, свободно используемый пользователем
Небольшой пример:
/*Спрячем объект*/
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
/*Сделаем объект неинтерактивным*/
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
Группировка объектов
Объекты добавляются в группу с помощью lv_group_add_obj(group, obj)
, и вы можете использовать lv_obj_get_group(obj)
, чтобы увидеть, к какой группе принадлежит объект.
lv_obj_is_focused(obj)
возвращает, сфокусирован ли объект в данный момент на своей группе или нет. Если объект не добавлен в группу, функция вернет false
.
Расширение области нажатия
По умолчанию на объекты можно нажимать только в пределах их ограниченной области. Однако её можно расширить с помощью lv_obj_set_ext_click_area(obj, size)
.