Опубликовано: 25 Октября 2017
(Демо приложение №3)
Index

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

Важно:

В эмуляторе с версией Tizen 2.3.2 и меньше виджет индексации со стилем "circle" отображается некорректно, поэтому мы использовали эмулятор с версией Tizen 3.0.0 в этом уроке для демонстрации. Эта проблема связана лишь с темой эмуляторов. На реальных устройствах индекс-виджет отображается корректно.

Описание виджета индексации

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

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

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

Как говорилось виджет может иметь до 20 элементов.

nw_022_01_en

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

Давайте разберем стили элементов поподробнее.

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

Максимальное нечетное количество элементов в индексе равно 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 ниже.

nw_022_02_en

Здесь были допущены следующие ошибки при выборе стилей:

1. Выбран набор стилей для нечетного количества элементов, хотя количество элементов в данном случае четное.

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

3. Неправильно выбранный стиль для центрального элемента.

4. Использование четных и нечетных элементов одновременно.

Правильное использование стилей показано ниже на картинке.

nw_022_03_en

Здесь мы использовали стили "item/even_#" для четного количества элементов, нумерация [7, 8, 9, 10, 11, 12, 13, 14].

Расположение элементов должно быть по контуру круга это касается только стиля "circle" самого виджета-индексации, не его элементов.

Подготовка приложения для добавления индекса

Как вы могли заметить виджет индексации со стилем circle приспособлен находится в верхней части экрана при этом на самом верхнем слое виджетов вашего приложения.

Для его добавления в наше демо-приложение необходимо предусмотреть специальную область.

Давайте добавим макет с двумя областями. Одна (фоновая) будет использоваться для помещения скроллера с его содержимым, а вторая (передняя) будет использоваться индекс-виджетом.

Для нас отлично подойдет макет со стандартной темой elm/layout/application/default. Мы разбирали этот макет в 12-м уроке. Он имеет следующий вид:

nw_022_04_en

Перейдем к коду.

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

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.

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *