Опубліковано: 25 Жовтня 2017
Круглий селектор (Rotary Selector)

Протягом наступних 4 уроків ми покажемо вам, як написати демо-додаток, у якому буде створено віджет зі списком додатків. Якщо потрібний додаток не встановлено на годиннику, ми покажемо, як можна запустити Samsung Galaxy Apps для того аби його завантажити і встановити.

Ми розглянемо віджет rotary selector з модулю розширення бібліотеки EFL(не є стандартним віджетом бібліотеки Elementary), модулі менеджера пакетів і додатків, передбачених для отримання списку встановлених додатків та інформації про них, модуль для запуску встановленого додатку, а також можливість запуску Samsung Galaxy Apps для завантаження та встановлення додатку.

За основу буде взято шаблон, створений у 11-му уроці. Проект доступний для завантаження тут (WearableUITemplate).

У цьому уроку ми розглянемо rotary selector віджет, його основні функції і типи даних, крім того, подивимось, як цей віджет можна додати у ваш демо-додаток.

Введення в Rotary Selector

Rotary selector – віджет із розширення бібліотеки EFL, він не входить в склад стандартних віджетів бібліотеки Elementary і розроблений спеціально для круглих носимих пристроїв. Цей віджет складається з декількох елементів, розташованих по колу носимого пристрою, на одній або декількох сторінках.

Rotary Selector дозволяє зручно вибирати елементи або переходити на наступну чи попередню сторінку з допомогою обертання безеля. Цей віджет буде корисний, якщо наприклад, додати в нього елементи налаштувань додатку, замість використання стандартного списку або якщо використовувати його для відображення і здійснення через нього вибору деяких категорій. У певних випадках на годинниках rotary selector віджет можна розглядати як заміну стандартного списку. З його допомогою можна побачити більше елементів на екрані. Крім того, зовнішній вигляд віджету подібний до інтерфейсу стандартних додатків на годинниках, якщо це необхідно.

Створення Rotary Selector

Додайте rotary selector віджет у ваш демо-додаток і давайте розглянемо декілька важливих функцій.

Спочатку необхідно розширити структуру UIData, додавши в неї вказівник на rotary selector віджет.

typedef struct _UIData {
   Evas_Object *win;
   Evas_Object *conform;
   Evas_Object *rotary_selector;
} UIData;

Далі напишіть статичну функцію для створення і налаштування rotary selector віджета.

static void
_rotary_selector_create(UIData *ui)
{
}

Аби створити rotary selector віджет, необхідно всередині функції викликати наступну функцію:

ui->rotary_selector = eext_rotary_selector_add(ui->conform);

Функція створення селектору приймає єдиний параметр – вказівник на компонент батька, який буде відповідати за видалення rotary selector віджета.

Як було сказано вище, основною перевагою rotary selector віджету є швидкий доступ до вибору елементів і переміщення по сторінкам додатку з допомогою безеля. Для використання останнього, потрібно активувати цю можливість для віджета, викликавши наступну функцію:

void eext_rotary_object_event_activated_set(Evas_Object *obj, Eina_Bool activated);

Першим параметром передається вказівник на об’єкт rotary selector, другим – логічний прапорець для активації чи дезактивації навігації між елементами, використовуючи безель.

Після створення rotary selector, для активації обертання, викличте цю функцію:

static void
_rotary_selector_create(UIData *ui)
{
   ...

   eext_rotary_object_event_activated_set(ui->rotary_selector, EINA_TRUE);
}

Далі помістіть ваш об’єкт rotary selector у конформант і відобразіть його.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   elm_object_content_set(ui->conform, ui->rotary_selector);

   evas_object_show(ui->rotary_selector);
}

Після створення конформанта, викличте вашу функцію для створення віджета.

static void
_conformant_create(UIData *ui)
{
   ...

   _rotary_selector_create(ui);

   ...
}

Якщо запустити цей додаток – ви не побачите на екрані нічого крім індикатору вибору елементу, розташованого в правому верхньому кутку. Якщо натиснути на віджет, то можна побачити як індикатор змінює свій стан: з «ненатиснутого» на «натиснутий», можна також спробувати покрутити безель.

Додавання елементів в Rotary Selector

Щоб оцінити всі переваги використання круглого селектора, додайте в нього елементи, викликавши наступну функцію:

Eext_Object_Item* eext_rotary_selector_item_append(Evas_Object *obj);

Функція приймає єдиний параметр – вказівник на об’єкт rotary selector і повертає вказівник на структуру для створеного елементу. Розглянемо визначення даної структури.

struct _Eext_Object_Item {
   Evas_Object *obj;
   void *data;
};

Перше поле – це власне вказівник на графічні дані створеного елементу. А от друге поле є дуже цікавим і дуже корисним у використанні - це вказівник на дані, які потім можна буде використовувати при роботі з елементами. Приклад використання цього поля ми покажемо в наступному уроку.

Додайте у ваш додаток 15 елементів.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   const int items_count = 15;
   for (int i = 0; i < items_count; ++i)
     {
        Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);
     }

   ...
}

Далі отримавши вказівник на створений елемент, можна його налаштувати, додавши в нього текст, за допомогою наступної функції:

void eext_rotary_selector_item_part_text_set(Eext_Object_Item *item,
                                             const char *part_name,
                                             const char *text);

Перший параметр – це вказівник на елемент, якому необхідно встановити текст, другий параметр – це ім’я текстової області, в яку буде встановлено текст, а третій – це той самий текст.

У елементу є дві текстові області:

"selector,main_text" - для основного тексту елемента;

"selector,sub_text" - для додаткового тексту елемента.

Варто відзначити, що додатковий текст елементу не є обов’язковим. Це свого роду уточнююча інформація і якщо текст має велику довжину, то він буде відображуватись як рядок, що біжить.

Додайте у вашу програму трохи інформації для створених елементів, викликавши додавання тексту.

static void
_rotary_selector(UIData *ui)
{
   ... 

   for (int i = 0; i < items_count; ++i)
     {
        ...

        const char MAX_TEXT_SIZE = 20;
        char item_text[MAX_TEXT_SIZE] = {'\0',};
        snprintf(item_text, MAX_TEXT_SIZE, "Елемент %d", i + 1);

        eext_rotary_selector_item_part_text_set(item, "selector,main_text", item_text);
        eext_rotary_selector_item_part_text_set(item, "selector,sub_text", "Додаткова інформація");
     }

   ...
}

Можна запустити додаток і подивитись на результат.

Як і очікувалось, на екрані відобразилось 15 елементів з основним і додатковим текстом, водночас можна побачити, що додатковий текст має більшу довжину, тому він буде відображений у вигляді рядку, що біжить. Також при обертанні безеля, можна побачити, як селектор переміщується по колу екрану, і якщо він досягає кінця поточної сторінки, то переходить на наступну.

Однією з основних функцій є функція додавання вмісту в елемент, при цьому варто відзначити, що вміст може бути різним за умови різного стану елементу. Для додавання вмісту використовується наступна функція:

void eext_rotary_selector_item_part_content_set(Eext_Object_Item *item,
                                                const char *part_name,
                                                Eext_Rotary_Selector_Item_State state,
                                                Evas_Object *content);

Перший параметр – це вказівник на елемент rotary selector віджета, другий параметр – ім’я області, в яку ви будете додавати вміст, третій параметр – стан елементу, в якому буде відображуватись контент, четвертий параметр – вказівник на об’єкт самого контенту, який поміщується в елемент віджету.

Перерахуймо назви областей для вставки контенту:

"item,icon" – для вставки картинки в елемент;

"selector,content" – для зміни зображення селектору вибору елементу;

"item,bg_image" – для зміни фону елементу;

"selector,content" – для зміни контенту селектору;

Варто зазначити, що найчастіше використовується область "item,bg_image".

Нижче представлені доступні стани елемента:

EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL – елемент в ненатиснутому стані;

EEXT_ROTARY_SELECTOR_ITEM_STATE_PRESSED – елемент в натиснутому стані;

EEXT_ROTARY_SELECTOR_ITEM_STATE_DISABLED – елемент не активний;

Використання цієї функції буде продемонстровано в наступних уроках.

Поряд з функцією для встановлення контенту, також є функція для отримання вмісту елементу, нижче розглянемо її опис.

Evas_Object* eext_rotary_selector_item_part_content_get(const Eext_Object_Item *item,
                                                        const char *part_name,
                                                        Eext_Rotary_Selector_Item_State state);

Ця функція аналогічна описаній вище функції для встановлення контенту, але з однією відмінністю – замість того аби встановлювати контент, функція його повертає. Нижче перераховані описи тих функцій, що можуть знадобитись при роботі з круглим селектором.

const Eina_List* eext_rotary_selector_items_get(const Evas_Object *obj);

Функція приймає вказівник на rotary selector віджет і повертає константний вказівник на список елементів. Зверніть увагу, що він константний, тобто дані доступні лише для читання.

Аби отримати поточний виділений елемент, необхідно використати наступну функцію:

Eext_Object_Item* eext_rotary_selector_selected_item_get(const Evas_Object *obj);

Функція приймає вказівник на rotary selector віджет і повертає вказівник на поточний виділений елемент. Також варто згадати функцію, яка встановлює опцію виділення вказаного елементу.

void eext_rotary_selector_selected_item_set(Evas_Object *obj, Eext_Object_Item *item);

Перший параметр – це вказівник на rotary selector, другий параметр – це вказівник на наступний елемент, який необхідно виділити. При виклику цієї функції, круглий селектор перемістить індикатор на вказаний елемент, отже, віджет підтримує виділення лише одного елементу в конкретний момент часу. У цій функції є один нюанс, який ми розглянемо пізніше.

Обробка подій rotary selector

Давайте розглянемо основні сигнали rotary selector віджета, і подивимось, як додати функції для обробки цих сигналів в додатку. Круглий селектор підтримує два смарт-сигнали, на які можна додати обробники:

"item,clicked" – сигнал при натисканні на елемент на екрані;

"item,selected" – сигнал, що елемент був обраний, використовуючи безель.

Тепер приведемо приклад, як можна використовувати сигнали. Додайте їх у ваш демо-додаток. Змініть функцію для створення rotary selector віджета, додавши виклик функцій, які реєструють обробники на перераховані сигнали.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   evas_object_smart_callback_add(ui->rotary_selector, "item,clicked", _item_clicked_cb, NULL);
   evas_object_smart_callback_add(ui->rotary_selector, "item,selected", _item_selected_cb, NULL);

   ...
}

Реалізуйте функції обробники сигналів і виведіть в логування інформацію про те, що виклик цих обробників був виконаний.

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", "Елемент натиснутий");
}

static void
_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", "Елемент вибраний");
}

Не забутьте додати заголовний файл модуля логування.

#include <dlog.h>

Запустіть ваш додаток разом з вікном логування, виберіть який тип повідомлення вас цікавить (у цьому разі – інформаційні повідомлення), далі встановіть фільтр по тегу. Якщо ви почнете обертати безель, то при зміні активного елемента, у вікні логування буде виводитись повідомлення про вибір елемента, а у випадку натискання на елемент – буде виводитись інформація про подію натискання.

У наступних уроках ми покажемо вам, як наповнювати ці обробники функціональністю.

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

Необхідно змінити функцію обробки сигналу виділення елементу, додавши туди код, який буде випадковим чином змінювати колір елементу. Для зміни кольору елементу круглого селектору є наступна функція:

void eext_rotary_selector_item_part_color_set(Eext_Object_Item *item,
                                              const char *part_name,
                                              Eext_Rotary_Selector_Item_State state,
                                              int r,
                                              int g,
                                              int b,
                                              int a);

Перший параметр – це вказівник на елемент, другий параметр – це ім’я області елементу, якому необхідно змінювати колір, третій параметр – стан елементу (перелік станів ми розглядали раніше в цьому уроку), з четвертого по сьомий параметри – колір у форматі RGBA (значення від 0 до 255 включно).

Приведемо перелік областей, які найчастіше використовуються:

"item,bg_image" – для фону елемента;

"item,icon" – для зображення елемента;

"selector,icon" – для зображення індикатора вибору.

Для даного прикладу ми розглянемо область "item,bg_image", яка слугує фоном елементу.

Додайте заголовні файли, необхідні для отримання часу і використання генератору випадкових чисел.

#include <time.h>
#include <stdlib.h>

У якості зерна для генератора випадкових чисел, використовується поточний час.

static bool
_app_create_cb(void *data)
{
   ...

   srand(time(NULL));

   ...
}

Змініть функцію реєстрації обробника події виділення елемента, передавши у ваш обробник вказівник на rotary selector, який ви потім використаєте всередині обробника в якості даних користувача.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   evas_object_smart_callback_add(ui->rotary_selector,
                                  "item,clicked",
                                  _item_clicked_cb,
                                  ui->rotary_selector);

   evas_object_smart_callback_add(ui->rotary_selector,
                                  "item,selected",
                                  _item_selected_cb,
                                  ui->rotary_selector);

   ...
}

Для того або встановити колір елементу, в обробниках викличте функцію для отримання поточного виділеного елементу круглого селектору. Потім, використовуючи функцію отримання випадкових чисел, згенеруйте числа від 0 до 255 для RGB кольору. В обробнику виділення елементу, встановіть отриманий колір в якості фону елементу. А в обробнику натискання, завжди фарбуйте елемент в білий колір.

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", "Елемент натиснутий");
   if (!data) return;

   Evas_Object *rotary_selector = data;
   Eext_Object_Item *selected_item = eext_rotary_selector_selected_item_get(rotary_selector);
   eext_rotary_selector_item_part_color_set(selected_item,
                                            "item,bg_image",
                                            EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL,
                                            255, 255, 255, 255);
}

static void
_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", "Елемент вибраний");
   if (!data) return;
 
   Evas_Object *rotary_selector = data;
   Eext_Object_Item *selected_item = eext_rotary_selector_selected_item_get(rotary_selector);
   int r = rand() % 256;
   int g = rand() % 256;
   int b = rand() % 256;
   eext_rotary_selector_item_part_color_set(selected_item,
                                            "item,bg_image",
                                            EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL,
                                            r, g, b, 255);
}

Запустіть додаток.

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

Підводні камені rotary selector

Розглянемо особливу поведінку функції встановлення поточного елементу.

Припустимо, при відкриванні додатку, ви вирішили встановити останній вибраний елемент (попередньо запам’ятавши його після закриття). Після створення елемента і його налаштування, якщо він відповідає вашій умові (тобто його дані рівні даним збереженого елемента), ви викликаєте функцію eext_rotary_selector_selected_item_set, встановивши створений елемент виділеним.

Запустивши додаток, ви побачите, що індикатор вибору елемента буде завжди вказувати на перший елемент, а не на той, який ви хочете. Проблема пов’язана з тим, що на момент виклику вашої функції, віджет ще не встиг повністю промалюватись, тому при виклику функції активації елемента, графічно цей елемент ще не існує. Вирішення цієї проблеми – це відтермінований запуск функції вибору елементу.

Для початку необхідно викликати функцію для реєстрації обробника на подію відображення rotary selector віджета.

evas_object_event_callback_add(rotary_selector, EVAS_CALLBACK_SHOW, _on_show_rotary, id_item);

Далі в цьому обробнику необхідно створити таймер з інтервалом, який буде залежати від кількості елементів. Наприклад при 25 елементах, цілком достатньо інтервалу 0.2 секунди.

static void
_on_show_rotary(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   show_selected_item_rotary = ecore_timer_add(0.2, _timer_select_item_rotary_cb, data);
}

В обробнику таймера викличте функцію встановлення поточного елементу.

static Eina_Bool
_timer_select_item_rotary_cb(void *data)
{
  ...

  eext_rotary_selector_selected_item_set(rotary_selector, selected_item);

  return ECORE_CALLBACK_DONE;
}

От і все, тепер при запуску додатку, індикатор буде відображатись на встановленому вами елементі.

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

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *