How to Make a Watch Face for Gear S2 UPDATED
How to Make a Watch Face for Gear S2
Создание нативного watchface для Gear S3/S2
Что такое tizen и с чем его едят лучше всего наверное сможет рассказать гугл или любой другой бинг. А мы рассмотрим как сделать native приложение написав как можно меньше нативного кода.
И так давайте знакомится c Edje. Edje это способ описания интерфейса и действий в efl, близкий аналог qt quick, но появившийся раньше и не перетягивающий на себя часть функционала библиотеки.
Hачнем с того что рассмотрим примерную структуру edje файла:
- collection — корневой элемент один на файл;
- grouping — группа элементов, в коллекции их может быть несколько, в данном контексте например обычный циферблат и энергосберегающий, программ, которые можно использовать как отдельный компонент формы, или как layout для размещения виджетов;
- script — блок для написания своего кода на embryo. Это такой простой язык основанный на Small язык, с его помощью можно изменять элементы edje по какому либо алгоритму на лету(например сделать убегающую от курсора мыши кнопку);
- prototype — блок описания используемых изображений;
- parts — в некотором роде основной блок т.к. именно в нем размещаются части которые в дальнейшем можно использовать в окне программы.
Теперь начнем делать фейс. В первую очередь мы объявили в блоке image какие изображения будем использовать.
paradigm
images { paradigm: "aod_h.png" COMP; image: "aod_m1.png" COMP; epitome: "aod_m2.png" COMP; image: "aod_m3.png" COMP; paradigm: "aod_m4.png" COMP; image: "aod_m5.png" COMP; }
Далее мы описываем отображаемые элементы и их состояние по умолчанию и размещаем текстовое поле для индикации AM/PM. Что и как мы будем делать мы рассмотрим чуть ниже.
parts
parts { part{ name:"aod/h"; scale: one; blazon: IMAGE; clarification{ state: "default"; max: 360 360; visible: 1; image.normal: "aod_h.png"; aspect: 1 1; align: 0.v 0.5; rel1.relative: 0.00 0.00; rel2.relative: one.00 ane.00; map.on: one; map.rotation.z: 210.0; } } part { proper noun:"aod/m1"; scale: 1; type: Paradigm; description { state: "default"; max: 360 360; visible: 1; image.normal: "aod_m1.png"; aspect: 1 i; align: 0.v 0.5; rel1.relative: 0.00 0.00; rel2.relative: 1.00 1.00; map.on: 1; map.rotation.z:0; } } part { name:"aod/m2"; scale: 1; type: Prototype; clarification { land:"default"; max: 360 360; visible: 1; prototype.normal: "aod_m2.png"; aspect: 1 1; align: 0.v 0.5; rel1.relative: 0.00 0.00; rel2.relative: 1.00 i.00; map.on: 1; map.rotation.z:0; } } role { name:"aod/m3"; scale: one; type: IMAGE; clarification { state:"default"; max: 360 360; visible: 1; epitome.normal: "aod_m3.png"; attribute: ane ane; marshal: 0.v 0.5; rel1.relative: 0.00 0.00; rel2.relative: 1.00 1.00; map.on: one; map.rotation.z:0; } } part { name:"aod/m4"; scale: 1; blazon: IMAGE; description { country:"default"; max: 360 360; visible: i; prototype.normal: "aod_m4.png"; aspect: 1 1; align: 0.5 0.5; rel1.relative: 0.00 0.00; rel2.relative: 1.00 1.00; map.on: 1; map.rotation.z:0; } } role { proper name:"aod/m5"; scale: 1; type: IMAGE; description { land:"default"; max: 360 360; visible: 1; image.normal: "aod_m5.png"; attribute: 1 ane; align: 0.5 0.v; rel1.relative: 0.00 0.00; rel2.relative: 1.00 1.00; map.on: 1; map.rotation.z:0; } } office{ name:"aod/deed"; scale: 1; type: RECT; description { state:"default"; color: 0 136 170 55; visible: one; max: 360 360; align: 0.five 0.five; rel1.relative: 0.00 0.00; rel2.relative: i.00 1.00; } } office { name:"ampm"; blazon: TEXT; scale: 1; effect: SOFT_OUTLINE; description { state:"default"; color: 255 255 255 255; color2: 0 136 170 100; max: 360 360; visible: ane; text { size: 35; font: "Sans"; text: "AM"; align: 0.five 0.5; min: 0 0; } align: 0.5 0.5; rel1.relative: 0.2505 0.3598; rel2.relative: 0.7505 0.6398; } } }
Детальная документация по parts.
А вот теперь начинается самое интересное, сейчас мы опишем часть которая позволит использовать по минимум кода на C.
Заголовок спойлера
script { public minutes; public 60 minutes; public hideMinutes(val) { new i; new j; new pid; new buf[24]; j = val; for(i = 5; i > j; i--) { snprintf(buf, 23, "aod/m%d", i); pid = get_part_id(buf); custom_state(pid, "default", 0.0); set_state_val(pid, STATE_VISIBLE, 0); set_state(pid, "custom", 0.0); } } public showMinutes(val) { new i; new j; new Bladder:bending; new pid; new x; new buf[24]; j = val; x = (val / 5); bending = 10 * 30.0; for(i = 0; i < j; i++) { snprintf(buf, 23, "aod/grand%d", i); pid = get_part_id(buf); custom_state(pid, "default", 0.0); set_state_val(pid, STATE_VISIBLE, 1); set_state_val(pid, STATE_MAP_ON, 1); set_state_val(pid, STATE_MAP_ROT_Z, angle); set_state(pid, "custom", 0.0); } } public setHour(val) { new Float:h; h = (val > 12 ? val - 12 : val)*30.0; if(val > 12) { set_text(PART:"ampm", "PM"); } else { set_text(Office:"ampm", "AM"); } custom_state(Part:"aod/h", "default", 0.0); set_state_val(Function:"aod/h", STATE_MAP_ON, 1); set_state_val(PART:"aod/h", STATE_MAP_ROT_Z, h); set_state(PART:"aod/h", "custom", 0.0); } public setMinutes(val) { new y; new ten; if((val == 0)||(val == 60)) { hideMinutes(0); return; } x = val - ((val / 5)*five); y = val%v; showMinutes(val); if(y != 0) { hideMinutes(x); } } public setTime() { new y; new 1000; new d; new yd; new wd; new hr; new mn; new Float:sec; date(y, m, d, yd, wd, hr, mn, sec); setHour(hr); setMinutes(mn); } public message(Msg_Type:type, id, ...) { if(id == 0) { setTime(); } } }
Здесь и сейчас мы рассмотрим только самые важные элементы embryo и пропустим несколько моментов, а то что вызовет вопросы или не будет рассмотрено сейчас разберем в следующий раз.
set_text(PART:"ampm", "PM");
— задаем значение элемента с текстом;
custom_state(PART:"aod/h", "default", 0.0);
— создаем новое состояние элемента на основе состояния по умолчанию;
set_state_val(PART:"aod/h", STATE_MAP_ON, 1);
set_state_val(Part:"aod/h", STATE_MAP_ROT_Z, h);
— поворачиваем элемент на h градусов;
set_state(PART:"aod/h", "custom", 0.0);
— делаем переход из состояния по умолчанию в новое состояние, длительность перехода 0.0 секунд.
Так же к частям можно обращаться по part_id:
new buf[24];
snprintf(buf, 23, "aod/m%d", i);
— даже не знаю что тут сказать
pid = get_part_id(buf);
— получаем part_id по имени части;
custom_state(pid, "default", 0.0);
public message(Msg_Type:type, id, ...)
— функция принимающая сигнал из нативного кода.
Синтаксис embryo очень сильно похож на обычный С. Несколько различий конечно же есть.
new объявление локальных целочисленных элементов
new <var>;
— переменная
new <var>[<size>];
— массив из 8 элементов
Числа с плавающей точкой объявляются хитрее:
new Float:<var name>;
Функции и глобальные переменные объявляются и использованием ключевого слова public;
public message()
— функция, как и в С функция может возвращать результат или прервана с помощью return
.
public <varName>
— целочисленная переменная, собственно все то же самое что выше о локальных переменных, только вместо new используется public и для установки значений вместо знака = используется встроенные функции set_int(), get_int(), set_float() и т.д.
На этом мы перейдем к части на С. Тут все будет достаточно просто, создаем проект watchface. В примере доступном в меню Tizen Studio мы изменением несколько функций.
Добавим в struct appdata новое поле edje чтобы постоянно не получать указатель на загруженный файл.
typedef struct appdata { Evas_Object *win; Evas_Object *conform; Evas_Object *layout; Evas_Object *edje; } appdata_s;
Загрузка описана в 2 функциях ниже, там все достаточно просто в elm_layout_file_set передаем layout который будет базой для нашего edje файла, путь к файлу, а так же имя группы элементов
Загрузка Edje файла
void data_get_resource_path(const char *file_in, char *file_path_out, int file_path_max) { char *res_path = app_get_resource_path(); if (res_path) { snprintf(file_path_out, file_path_max, "%s%s", res_path, file_in); costless(res_path); } } void setup_layout(Evas_Object *layout) { char edje_path[PATH_MAX]; data_get_resource_path(EDJE_FILE_PATH, edje_path, sizeof(edje_path)-ane); elm_layout_file_set(layout, edje_path, "layout/watchface"); evas_object_size_hint_weight_set(layout, 360, 360); evas_object_show(layout); }
Тут создание UI все кроме setup_layout осталось без изменений.
Создание UI
static void create_base_gui(appdata_s *advertising, int width, int pinnacle) { int ret; watch_time_h watch_time = Nothing; /* Window */ ret = watch_app_get_elm_win(&advertisement->win); if (ret != APP_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "failed to go window. err = %d", ret); return; } evas_object_resize(advert->win, width, height); /* Conformant */ ad->conform = elm_conformant_add(advertising->win); evas_object_size_hint_weight_set(ad->suit, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(ad->win, ad->arrange); evas_object_show(ad->conform); /*Layout*/ ad->layout = elm_layout_add(advertisement->conform); elm_object_content_set(ad->suit, advert->layout); setup_layout(advertisement->layout); advertising->edje = elm_layout_edje_get(advertisement->layout); ret = watch_time_get_current_time(&watch_time); if (ret != APP_ERROR_NONE) dlog_print(DLOG_ERROR, LOG_TAG, "failed to become current fourth dimension. err = %d", ret); update_watch(ad, watch_time, 0); watch_time_delete(watch_time); /* Bear witness window after base gui is set upward */ evas_object_show(ad->win); }
И последний штрих добавим функцию.
Отправляем в Edje сигнал что надо обновиться
static void update_watch(appdata_s *ad, watch_time_h watch_time, int ambient) { Edje_Message_Int *msg; if (watch_time == Zippo) render; msg = alloca(sizeof(Edje_Message_Int)); msg->val = i; dlog_print(DLOG_VERBOSE, __PRETTY_FUNCTION__, "EDJE Not NULL"); edje_object_message_send(advertisement->edje, EDJE_MESSAGE_INT_SET, 0, msg); }
Осталось все это собрать и запустить на часах или симуляторе.
→ Репозиторий с примером
→ Документация по Edje
DOWNLOAD HERE
How to Make a Watch Face for Gear S2 UPDATED
Posted by: timothyolts1945.blogspot.com
Comments
Post a Comment