В этом уроке мы разберемся с виджетом индексации, научимся его создавать и использовать. Мы рассмотрим только стиль "circle" для самого виджета индексации в котором элементы расположены по кругу экрана.
Важно:
В эмуляторе с версией Tizen 2.3.2 и меньше виджет индексации со стилем "circle" отображается некорректно, поэтому мы использовали эмулятор с версией Tizen 3.0.0 в этом уроке для демонстрации. Эта проблема связана лишь с темой эмуляторов. На реальных устройствах индекс-виджет отображается корректно.
Виджет индексации используется для индикации страниц. Для носимых устройств с круглым дисплеем он позволяет отобразить от 1 до 20 индикаторов. При этом подсвечивая один, который является активным.
Изначально этот виджет задумывался как элемент для быстрого доступа к элементам в больших списках. Например, в списке контактов для мобильных устройств сбоку есть индекс-виджет на котором присутствуют элементы для быстрого поиска по первым буквам контактов. В мобильных устройствах он интерактивный, то есть вы можете нажимать на элементы и вы будете переходить в соответствующую этому индексу секцию списка.
Но на носимых устройствах с круглым дисплеем на данный момент виджет индексации в основном используется только для отображения активной страницы.
Как говорилось виджет может иметь до 20 элементов.
Для каждого из элементов необходимо ставить отдельный стиль соответствующий его порядковому номеру, и четному или нечетному количеству элементов.
Давайте разберем стили элементов поподробнее.
При наполнении индекса первый очень важный момент, это знать четное или нечетное количество элементов мы добавим.
Максимальное нечетное количество элементов в индексе равно 19.
Для нечетного количества элементов используется стиль с приставкой "item/odd_" в конце ставится порядковый номер. Ниже перечислены все возможные стили для использования при нечетном количестве элементов.
"item/odd_1"
"item/odd_2"
...
"item/odd_19"
Максимальное четное количество элементов в индексе равно 20.
Для четного количества элементов используется стиль с приставкой "item/even_" в конце также ставится порядковый номер. Ниже перечислены все возможные стили для использования при четном количестве элементов.
"item/even_1"
"item/even_2"
...
"item/even_20"
Второй момент - выбор стиля для центрального элемента.
Для нечетного количества это стиль "item/odd_10", он всегда должен устанавливаться среднему элементу. Например, при трех элементах, мы должны добавить 3 элемента и установить им стили в следующем порядке:
"item/odd_9", "item/odd_10", "item/odd_11".
Для 5-ти:
"item/odd_8", "item/odd_9", "item/odd_10", "item/odd_11", "item/odd_12".
И так далее.
Для четного количества центральных стиля два: "item/even_10" и "item/even_11". Так для двух элементов добавятся только эти два стиля, а для 4-х элементов:
"item/odd_9", "item/odd_10", "item/odd_11", "item/odd_12".
Ну и последнее. Как вы могли заметить, соседние элементы должны иметь суффиксы стиля отличающиеся ровно на 1.
Со стилями для элементов нужно быть внимательными поскольку если неправильно подобрать стиль то у вас может получиться что то дикое, как на картинкe ниже.
Здесь были допущены следующие ошибки при выборе стилей:
1. Выбран набор стилей для нечетного количества элементов, хотя количество элементов в данном случае четное.
2. Перепутаны порядковые номера стилей, соседние стили должны отличаться на 1 порядковый номер.
3. Неправильно выбранный стиль для центрального элемента.
4. Использование четных и нечетных элементов одновременно.
Правильное использование стилей показано ниже на картинке.
Здесь мы использовали стили "item/even_#" для четного количества элементов, нумерация [7, 8, 9, 10, 11, 12, 13, 14].
Расположение элементов должно быть по контуру круга это касается только стиля "circle" самого виджета-индексации, не его элементов.
Как вы могли заметить виджет индексации со стилем circle приспособлен находится в верхней части экрана при этом на самом верхнем слое виджетов вашего приложения.
Для его добавления в наше демо-приложение необходимо предусмотреть специальную область.
Давайте добавим макет с двумя областями. Одна (фоновая) будет использоваться для помещения скроллера с его содержимым, а вторая (передняя) будет использоваться индекс-виджетом.
Для нас отлично подойдет макет со стандартной темой elm/layout/application/default. Мы разбирали этот макет в 12-м уроке. Он имеет следующий вид:
Перейдем к коду.
Для начала, поскольку мы будем использовать индикатор страниц, то можно отключить видимость полос прокрутки скроллера.
static void
_scroller_create(UIData *ui)
{
...
elm_scroller_policy_set(ui->scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);
...
}
Теперь добавим указатель на макет в структуру с главными виджетами.
Evas_Object *layout;
Получим следующее содержимое нашей структуры с графическими компонентами.
typedef struct _UIData {
Evas_Object *win;
Evas_Object *conform;
Evas_Object *layout;
Evas_Object *scroller;
Evas_Object *box;
Evas_Object *padding_start;
Evas_Object *padding_end;
} UIData;
Для создания макета создадим отдельную функцию. И перенесем в нее создание скроллера и помещение его в нужную область, в нашем случае фоновая область называется elm.swallow.bg.
static void
_layout_create(UIData *ui)
{
ui->layout = elm_layout_add(ui->conform);
elm_layout_theme_set(ui->layout, "layout", "application", "default");
_scroller_create(ui);
elm_object_part_content_set(ui->layout, "elm.swallow.bg", ui->scroller);
evas_object_show(ui->layout);
}
Теперь заменим содержимое нашего конформанта со скроллера на макет.
static void
_conformant_create(UIData *ui)
{
...
_layout_create(ui);
elm_object_content_set(ui->conform, ui->layout);
...
}
Таким образом мы добавили макет как прослойку между скроллером и конформантом.
Не забываем поменять скроллеру родителя с конформанта на макет в который мы его теперь помещаем.
static void
_scroller_create(UIData *ui)
{
ui->scroller = elm_scroller_add(ui->layout);
...
}
Дальше в уроке мы будем менять количество страниц в нашем приложении между запусками, чтобы посмотреть на результат. Поэтому немного отредактируем цикл создания страниц с элементами, что бы он зависил от одной константы.
Создаем константу указывающую количество страниц которое необходимо создать. Далее мы будем использовать ее также для правильного создания элементов индекс-виджета.
Добавим глобальную константу.
static const int PAGES_COUNT = 5;
Теперь у нас количество страниц может не соответствовать количеству изображений, поэтому необходимо изменить цикл создания страниц следующим образом.
static void
_box_create(UIData *ui)
{
...
const int IMAGES_COUNT = 5;
Evas_Object *page_layout;
for (int i = 0; i < PAGES_COUNT; ++i)
{
page_layout = _page_layout_create(ui->box, i % IMAGES_COUNT);
elm_box_pack_end(ui->box, page_layout);
}
...
}
Теперь, например, если количество страниц будет равно 6-ти то, страница с индексом 5 будет содержать изображение 0.png вместо несуществующего 5.png.
Создаем индекс виджет.
Evas_Object *index = elm_index_add(ui->layout);
Ставим ему круглый стиль.
elm_object_style_set(index, "circle");
Настраиваем индекс на горизонтальное отображение, поспольку этот стиль заточен на горизонтальное использование и наши страницы расположены горизонтально.
elm_index_horizontal_set(index, EINA_TRUE);
Отключаем автоматическое прятание виджета, иначе виджет не отобразится вовсе.
elm_index_autohide_disabled_set(index, EINA_TRUE);
Добавляем два массива для четных и нечетных конфигураций.
// Стили для четного количества страниц
static const char *even_style[] = {
"item/even_1",
"item/even_2",
"item/even_3",
"item/even_4",
"item/even_5",
"item/even_6",
"item/even_7",
"item/even_8",
"item/even_9",
"item/even_10",
"item/even_11",
"item/even_12",
"item/even_13",
"item/even_14",
"item/even_15",
"item/even_16",
"item/even_17",
"item/even_18",
"item/even_19",
"item/even_20",
};
// Стили для нечетного количества страниц
static const char *odd_style[] = {
"item/odd_1",
"item/odd_2",
"item/odd_3",
"item/odd_4",
"item/odd_5",
"item/odd_6",
"item/odd_7",
"item/odd_8",
"item/odd_9",
"item/odd_10",
"item/odd_11",
"item/odd_12",
"item/odd_13",
"item/odd_14",
"item/odd_15",
"item/odd_16",
"item/odd_17",
"item/odd_18",
"item/odd_19",
};
Добавим функционал для определения четное или нечетное у нас количество страниц, и стартовый индекс элемента массива стилей.
Eina_Bool is_even_count = PAGES_COUNT % 2 == 0;
const int CENTER_INDEX = is_even_count ? 10 : 9;
int start_index = CENTER_INDEX - PAGES_COUNT / 2;
const char **style = is_even_count ? even_style : odd_style;
Добавляем элементы индекса и ставим им необходимые стили, в соответствии с количеством наших страниц.
for (int i = 0; i < PAGES_COUNT; ++i)
{
Elm_Object_Item *it = elm_index_item_append(index, NULL, NULL, (void *)i);
elm_object_item_style_set(it, style[start_index + i]);
}
Второй аргумент не используется в данном стиле индекса, поэтому передаем туда NULL.
Третий аргумент – указатель на функцию коллбек которая будет вызвана при активации данного элемента. Нам она тоже не нужна.
Последним аргументом функции мы передаем номер порядковый страницы, эти данные прикрепятся к элементу индекса. Вы можете прикреплять любые данные к элементам в своих приложения, но в нашем случае нам достаточно хранить лишь индекс элемента.
Вызываем функцию для того что бы применить все действия проделанные при создании индекса, в противном случае он может некоректно отобразиться.
elm_index_level_go(index, 0);
Наконец отображаем индекс.
evas_object_show(index);
У нас получилась следующая функция для создания индекса.
static Evas_Object *
_index_create(UIData *ui)
{
Evas_Object *index = elm_index_add(ui->layout);
elm_object_style_set(index, "circle");
elm_index_horizontal_set(index, EINA_TRUE);
elm_index_autohide_disabled_set(index, EINA_TRUE);
Eina_Bool is_even_count = PAGES_COUNT % 2 == 0;
const int CENTER_INDEX = is_even_count ? 10 : 9;
int start_index = CENTER_INDEX - PAGES_COUNT / 2;
const char **style = is_even_count ? even_style : odd_style;
for (int i = 0; i < PAGES_COUNT; ++i)
{
Elm_Object_Item *it = elm_index_item_append(index, NULL, NULL, (void *)i);
elm_object_item_style_set(it, style[start_index + i]);
}
elm_index_level_go(index, 0);
evas_object_show(index);
return index;
}
Вызываем ее и помещаем индекс в переднюю область созданного ранее макета.
static void
_layout_create(UIData *ui)
{
...
ui->index = _index_create(ui);
elm_object_part_content_set(ui->layout, "elm.swallow.content", ui->index);
...
}
Давайте запустим и посмотрим что получилось.
Как видно виджет-индексации отобразился, но ничего не выбрано и не происходит при пролистывании.
Теперь создадим функцию для активации элемента передавая как аргумент виджет индекса и порядковый номер элемента, который необходимо активировать. Поскольку стандартной функции не предусмотрено.
static void
_page_indicator_selected_set(Evas_Object *index, int page_index)
{
Elm_Object_Item *item_to_select = elm_index_item_find(index, (void *)page_index);
elm_index_item_selected_set(item_to_select, EINA_TRUE);
}
Здесь мы воспользовались функцией для поиска элемента по прикрепленным к нему данным, зная что мы прикрепляли порядковый номер.
Теперь вызовем созданную нами функцию активации после добавления всех элементов, чтобы активировать первый.
static Evas_Object *
_index_create(UIData *ui)
{
...
_page_indicator_selected_set(index, 0);
...
}
Нам необходимо активировать различные элементы при изменении активной странцы. Поступим следующим образом: создадим собственное событие для индекса, которое будем симулировать из обработчика скроллера на изменение страницы. Назовем этот сигнал "active_page,changed".
Заведем для этого константу
#define PAGE_CHANGED_EVENT "active_page,changed"
Создадим отдельный обработчик для индекса в котором будем активировать элементы в зависимости от текущей страницы скроллера. И подпишем индекс на это событие.
static void
_active_page_changed_cb(void *data, Evas_Object *index, void *event)
{
UIData *ui = data;
int cur_h_page = 0;
elm_scroller_current_page_get(ui->scroller, &cur_h_page, NULL);
_page_indicator_selected_set(ui->index, cur_h_page);
}
static Evas_Object *
_index_create(UIData *ui)
{
...
evas_object_smart_callback_add(index, PAGE_CHANGED_EVENT, _active_page_changed_cb, ui);
...
}
Мы создали свой собственный смарт собыие для виджета и зарегестрировали его также как делали например с событием нажатия для кнопки. Но если для кнопки событие симулировалось на стороне библиотеки, то наше событие должно симулироваться нами.
Для этого воспользуемся следующей функцией.
void evas_object_smart_callback_call(Evas_Object *obj, const char *event, void *info);
Мы будем ее использовать из обработчика события прокрутки скроллера, созданного в прошлом уроке.
Там где мы выводили сообщения в лог теперь можно поместить симуляцию события изменения страницы для индекса. Теперь колбек будет выглядеть так.
static void
_scroll_cb(void *data, Evas_Object *scroller, void *event)
{
static int prev_h_page = 0;
int cur_h_page = 0;
elm_scroller_current_page_get(scroller, &cur_h_page, NULL);
if (cur_h_page != prev_h_page)
{
UIData *ui = data;
evas_object_smart_callback_call(ui->index, PAGE_CHANGED_EVENT, NULL);
prev_h_page = cur_h_page;
}
}
Теперь можем запустить наше приложение и посмотреть на результат.
Все заработало. Вы можете взять полный код приложения здесь WearLesson022.
В следующем уроке мы обработаем случай когда количество страниц больше чем допустимое для индекса, воспользовавшись круглым скроллером.