Опубликовано: 25 Октября 2017
(Демо приложение №2)
Popup. Toast стиль. Типы событий скроллера

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

События скроллера

Скроллер является контейнером библиотеки Elementary и реализовывает свои смарт-колбеки. Это может быть удобно в некоторых ситуациях, например, мы можем узнать когда область прокрутки достигла границы содержимого, или анимация прокрутки завершена.

В носимых устройствах поддерживаются следующие виды сигналов которые можно использовать для обратной связи с контейнером:

“edge,left” - событые когда контент прокручен до левой границы контента.

“edge,right” - событые когда контент прокручен до правой границы контента.

“edge,top” - событые когда контент прокручен до верхней границы контента.

“edge,bottom” - событые когда контент прокручен до нижней границы контента.

“scroll” - событые вызывается во время прокрутки контента, вызывается очень много раз.

“scroll,anim,start” - событие с таким сигналом будет вызываться в двух случаях. Первый случай - когда пользователь сделает быстрый свайп в области прокрутки, то скроллер продолжит свое движение и постепенно остановится. в этом случае сигнал произойдет когда пользователь отпустит уберет палец со скроллера. Второй случай когда в коде вызовется одна из следующих функций для скроллера которая плавно переместит область прокрутки по заданным параметрам.

void elm_scroller_region_bring_in(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);
void elm_scroller_page_bring_in(Evas_Object *obj, int h_pagenumber, int v_pagenumber);

“scroll,anim,stop” - событие о том что анимация описанная в предыдущем сигнале завершена.

“scroll,drag,start” - событие которое вызывается когда пользователь начинает перемещать содержимое в области прокрутки.

“scroll,drag,stop” - событие которое вызывается когда пользователь останавливается перемещать содержимое в области прокрутки.

Toast стиль для Popup (Всплывающее окно)

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

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

Evas_Object *toast_help;

Получим следующее содержимое нашей структуры с виджетами.

typedef struct _UIData {
   Evas_Object *win;
   Evas_Object *conform;
   Evas_Object *toast_help;
   Evas_Object *scroller;
   Evas_Object *image;
} UIData;

Для создания и работы всплывающего окна сделаем такие две функции:

static void
_toast_click_cb(void *data, Evas_Object *toast, void *event_info)
{
   evas_object_del(toast);
}

static void
_toast_create(UIData *ui)
{
   ui->toast_help = elm_popup_add(ui->conform);

   elm_object_style_set(ui->toast_help, "toast/circle");

   const float TIMEOUT_TIME = 5.0; // Секунды
   elm_popup_timeout_set(ui->toast_help, TIMEOUT_TIME);

   const char *MESSAGE = "Проведите пальцем по экрану для перемещения...";
   elm_object_part_text_set(ui->toast_help, "elm.text", MESSAGE);

   evas_object_smart_callback_add(ui->toast_help, "block,clicked", _toast_click_cb, NULL);

   evas_object_show(ui->toast_help);
}

Давайте разберем что же мы делаем здесь для создания Toast.

Для создания объекта виджета используется следующий вызов:

ui->toast_help = elm_popup_add(ui->conform);

Для установки стиля любому виджету, или контейнеру Elementary вызывается следующая функция, стиль Toast для носимого устройства с круглым дисплеем называется “toast/circle”, как и для других виджетов первым параметром идет указатель на объект виджета, а вторым имя стиля который необходимо установить:

elm_object_style_set(ui->toast_help, "toast/circle");

Для всплывающего окна можно установить временную задержку после которой это окно закроется. Таймер задержки стартует во время вызова функции если окно уже отображено, в другом случае таймер сработает когда вызовется функция отображения объекта. Мы решили сделать 5-ти секундную задержку нашего вспомогательного окна.

const float TIMEOUT_TIME = 5.0; // Секунды
elm_popup_timeout_set(ui->toast_help, TIMEOUT_TIME);

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

const char *MESSAGE = "Проведите пальцем по экрану для перемещения...";
elm_object_part_text_set(ui->toast_help, "elm.text", MESSAGE);

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

Виджет всплывающего окна реализует смарт-событие нажатия на него сигнал “block,clicked”, для регистрации функции-колбека делается следующее. Сигнатура функции такая как и у стандартного смарт-колбека: первый параметр – указатель на пользовательские данные, второй – сам смарт-объект для которого вызывается событие (в нашем случае – всплывающее окно), третий – указатель на инфо с параметрами события.

static void
_toast_click_cb(void *data, Evas_Object *toast, void *event_info)
{
   evas_object_del(toast);
}

static void
_toast_create(UIData *ui)
{
   ...
   evas_object_smart_callback_add(ui->toast_help, "block,clicked", _toast_click_cb, NULL);
   ...
}

После всех подготовительных процедур отображаем наш Toast, после его отображения запуститься 5-ти секундный таймер который мы установили для закрытия всплывающего сообщения.

evas_object_show(ui->toast_help);

Теперь добавим вызов функции для создания Toast виджета после создание скроллера с его содержимым и помещением его в конформант.

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

   _scroller_create(ui);
   elm_object_content_set(ui->conform, ui->scroller);

   _toast_create(ui);

   ...
}

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

static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
   ui_app_exit();
}

Еще один вариант это создание Toast перенести в колбек жизненого цикла приложения на событие востановления. Но мы в нашем примере этого делать не будем.

Теперь посмотрим что у нас получилось:

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

Делаем мини-игру из нашего демо приложения

Как мы и договаривались, сейчас превратим наше демо приложение в мини игру, на основе изученого в этом уроке.

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

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

nw_019_02_ru

Изображение карты - достаточно большое, одна клетка имеет размер 360x360.

Поместим ее по следующему пути res/images/map.jpg в нашем проекте.

Теперь укажем путь к этому изображению нашему виджету.

Для этого заменим “images/image_big.png” на “images/map.jpg”

static void
_image_create(UIData *ui)
{
   ...
   
   char abs_path_to_image[PATH_MAX] = {0,};
   _file_abs_resource_path_get("images/map.jpg", abs_path_to_image, PATH_MAX);
   
   ...
}

Определяем координаты на нашей карте где находится сокровище по горизонтали и вертикали:

nw_019_03

Получилось что X=3908, а Y=943.

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

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

   evas_object_smart_callback_add(ui->scroller, "scroll", _scroll_cb, ui);

   ...
}

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

Дальше создаем саму функцию-колбек:

static void
_scroll_cb(void *data, Evas_Object *scroller, void *event_info)
{
   UIData *ui = data;
}

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

Как мы уже решили, в этом колбеке мы будем проверять условие найдено сокровище или нет.

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

Evas_Coord x = 0;
Evas_Coord y = 0;
Evas_Coord w = 0;
Evas_Coord h = 0;
elm_scroller_region_get(ui->scroller, &x, &y, &w, &h);

Функция последними 4мя параметрами принимает 4 указателя на тип Evas_Coord на самом деле это просто переопределенный int. Аргументы идут в следующем порядке: сначала положение верхнего левого угла по горизонтали, затем положение верхнего левого угла по вертикали, ширина области и последней идет высота.

Определим центр этой области на контенте следующими действиями.

int center_x = x + w / 2;
int center_y = y + h / 2;

Добавим константы для координат сокровища которые мы получили для нашей карты.

const int TREASURE_X = 3908;
const int TREASURE_Y = 943;
const int AREA_RADIUS = 150;

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

Для определения находится ли точка в круге зделаем следующую функцию.

static Eina_Bool
_is_point_in_circle(double area_center_x,
                    double area_center_y,
                    double area_radius,
                    double checking_point_x,
                    double checking_point_y)
{
   double delta_x = fabs(area_center_x - checking_point_x);
   double delta_y = fabs(area_center_y - checking_point_y);

   double distance = sqrt(delta_x * delta_x + delta_y * delta_y);

   Eina_Bool is_in_circle = EINA_FALSE;

   if (distance < area_radius)
     {
        is_in_circle = EINA_TRUE;
     }

   return is_in_circle;
}

Первые три параметра функции это координаты центра окружности и радиус, последние два – координаты точки которую проверяем.

Простыми словами мы находим расстояние между центром окружности и точкой которую проверяем на вхождение. Если расстояние меньше чем радиус окружности, то точка входит в окружность.

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

static void
_win_toast_create(UIData *ui)
{
   ui->toast_win = elm_popup_add(ui->conform);

   elm_object_style_set(ui->toast_win, "toast/circle");

   const char *MESSAGE = "Поздравляем! Вы нашли сокровище!";
   elm_object_part_text_set(ui->toast_win, "elm.text", MESSAGE);

   evas_object_smart_callback_add(ui->toast_win, "block,clicked", _toast_click_cb, NULL);

   evas_object_show(ui->toast_win);
}

Функцию-колбек которая будет использоваться для нажатия пользователем на Toast возьмем ту же, что и для вслывающего окна помощи, поэтому убедитесь что _toast_click_cb() объявлена выше в коде чем только что созданная _win_toast_create(), чтобы избежать ошибок.

Дополнительные функции сделали, теперь давайте воспользуемся ими.

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

static void
_scroll_cb(void *data, Evas_Object *scroller, void *event_info)
{
   ...
   
   if (_is_point_in_circle(center_x, center_y, AREA_RADIUS, TREASURE_X, TREASURE_Y))
     {
        _win_toast_create(ui);
     }
}

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

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

elm_scroller_region_show(ui->scroller, x, y, w, h);

Иногда в приложении необходимо вообще запретить передвижение содержимого в скроллере.

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

elm_object_scroll_lock_x_set(ui->image, EINA_TRUE);
elm_object_scroll_lock_y_set(ui->image, EINA_TRUE);

Если вы хотите “разморозить” прокрутку виджета то вам необходимо вызвать эти функции со вторым параметром EINA_FALSE.

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

elm_scroller_policy_set(ui->scroller, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF);

В этой функции 2й и 3й параметры к горизонтальному и вертикальному виджету соответственно. В качестве них можно передать следующее:

ELM_SCROLLER_POLICY_AUTO – показывать индикатор автоматически только во время прокрутки, в другой момент прятать.

ELM_SCROLLER_POLICY_ON – всегда показывать индикатор.

ELM_SCROLLER_POLICY_OFF – никогда не показывать индикатор.

На этом пожалуй все. Давайте теперь посмотрим, какая же игра у нас получилась.

Скачать полный исходный код этого урока вы можете здесь WearLesson019.

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

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