Простенький Time Manager для Android
Решил тут на досуге написать статейку о том, как писать приложения для Android’а. Писать будет простенький тайм менеджер. В этой части я напишу саму программу, приделаю к ней интерфейс с анимацией, а в следующей, если будет интересно, напишу к ней сервис, чтоб программа работала еще и в фоновом режиме.
Что будет рассмотрено:
- RelativeLayout для реализации интерфейса программы с наложением изображений друг на друга.
- Timer для реализации алгоритма подсчета времени.
- Animation для
свистоперделоккрасивого интерфейса апплета.
Для наглядности добавил скринкаст как все это добро работает.
Итак, пишем простенький тайм менеджер для Android’а.
Предположим, что SDK + Eclipse у нас уже установлены.
Запускаем Eclipse, и идем в File — New — Other.
В открывшемся окне выбираем Android — Android Project.
Откроется вот такое окно.
Вбиваем параметры:
Project Name — это имя проекта, должно быть уникальным в текущем Workspace.
BuildTarget — версия ОС, для которой собирается проект. Следует помнить, что собирая проект для 1.5, он будет запускаться и на 1.6, и на 2.1, а если собирать для 1.6, то на 1.5 уже нет.
Application Name — название приложения. Будет отображаться в главном меню, откуда его можно будет запустить.
Package Name — com. .
Create Activity — имя Activity для запуска приложения.
Так как скрин делался после написания проекта, вы можете заметить у меня сверху ошибка «There is already a file . » из-за того, что проект с таким именем уже создан.
Жмем Finish.
Интерфейс
Вот к такому виду его надо привести, при этом размер текста таймеров должен быть одинаковым, увеличиваться \ уменьшаться благодаря Animation.
Для этого используем RelativeLayout с тремя чилдренами: ImageView, для тени вверху апплета, и двумя LinearLayout’ами, один для таймеров и еще один для кнопок внизу.
Если есть время и возможность можете нарисовать все сами, но на всякий пожарный прикладываю архивчик со всеми графическими элементами.
TimeTracker.zip (194Кб)
Распаковываем и кидаем в директорию drawable файлы bg.png, *.9.png, icon.png и topshadow.png.
9.png это 9-patch drawable. Поподробнее о нем можно почитать на developer.android.com/guide/developing/tools/draw9patch.html, замечу лишь, что эти файлы имеют возможность растягивать только определенные части изображения при необходимости. У меня таким образом работают кнопки. Если текст содержимого больше исходного изображения, либо у кнопки стоит атрибут layout_width / height = fill_parent, то android растянет не весь битмап, а только отмеченные участки.
Когда начинал делать не учел, но на будущее замечу, что эти изображения должны иметь минимальную длинну и ширину, у меня же ширина — 100px, поэтому кнопку уже ста пикселей я уже не сделаю.
Теперь необходимо дать знать android’у как растягивать фоновое изображение, header и менять картинки от состояния кнопки.
Для этого создаем в папке drawable файлы: background.xml, greebutton.xml, redbutton.xml и header.xml.
В первый файл пишем:
src — путь к битмапу, а именно к файлу bg.png, что лежит в папке drawable.
tileMode — как им правильно зарисовать поверхность.
Указываем битмап для каждого состояния кнопки. Можете, например добавить картинку для состояния focused.
Для redbutton.xml все так же как и в greenbutton.xml, за исключением битмапов. На хабре люди умные, думаю поймете, что туда надо написать.
Для header.xml тоже все так же как и в background.xml, только с другим битмапом.
Перебираемся в папочку layout — там лежат xml файлы, в которых описывается интерфейс апплета.
В нашем случае там лежит один единственный файл, открываем его и приводим к такому виду:
RelativeLayout позволяет размещать элементы относительно себя или друг друга. Благодаря этому мы сможем наложить на header таймеры.
Я описал следующие параметры:
orientation — ориентация чилдренов этого лэйаута.
К примеру если разместить две кнопки в LinearLayout с параметром orientation=«vertical», то они будут друг под другом, а с параметром orientation=«horizontal», друг за другом.
layout_width и layout_height — ширина и высота. Принимают три типа значения: fill_parent (принять параметр родителя), wrap_content (минимальное значение, где содержание не будет при этом обрезано), либо числовое значение.
background — фон. Немного выше мы создали background.xml в папке drawable, вот мы к нему сейчас и обращаемся.
Поясню, «@ / «. Таким же образом задаются идентификаторы, но об этом чуть позже.
Затем внутри RelativeLayout’а мы создаем еще 3 элемента, о которых я говорил чуть выше.
ImageView
src — путь к header.xml, который мы недавно создали, в котором лежат параметры для тени вверху апплета.
scaleType — как его растягивать.
В принципе можно было обойтись без header.xml, а сразу указать путь к topshadow.png.
layout_alignParentTop — параметры такого типа есть у каждого чилдрена RelativeView, и указывают, где разместить элемент. Конкретно этот говорит о том, что рисовать надо у верхней границы родителя, тоесть RelativeView.
LinearLayout с таймерами
id — идентификатор виджета. Для создания нового идентификатора используется «@+id/ «, для доступа к уже существующему, например в настройках надо сделать пункт зависимым от выбора другого пункта «@id/ «.
textColor — цвет текста. Задается либо как #RRGGBB, либо #AARRGGBB, где AA — hex альфа канала (прозрачность), RR, GG и BB — hex каждого цвета.
singleLine — текст в одну строку.
gravity — выравнивание самого текста.
text — попробуйте сами догадаться XD.
LinearLayout с кнопками
Тут впринципе все понятно, замечу лишь, что text задан не как строка, а как ссылка на строку в файле со строками… тафталогия какая-то… кароче чуть позже об этом напишу, пока оставте так.
Анимация
Принцип такой. Жмем кнопку, один таймер уменьшается, другой увеличивается.
Создаем в папке res подпапку anim. Туда пихаем 4 xml файла: magnify_rest.xml, shrink_rest.xml, magnify_work.xml и shrink_work.xml.
magnify — увеличение таймера.
shrink — уменьшение таймера.
fromXScale и fromYScale — значения размеров до начала анимации. Если поставить 2.0, то текст сначала резко увеличится в два раза, и уже от этого значения будет менять свой размер.
toXScale и toYScale — значения размеров к концу анимации. У меня стоит 2.0, тоесть текст будет увеличиваться в два раза от исходного значения, заданного в layout’е, а не от fromXScale и fromYScale.
pivotX и pivotY — точки, от которых происходит анимация. К примеру: если поставить в обоих по 50%, то текст будет увеличен во все стороны, если pivotX = 0%, то текст будет увеличиваться вправо, 100% — влево.
startOffset — промежуток до начала анимации.
duration — время анимации в миллисекундах.
Тут все тоже самое, за исключением размеров.
В итоге я хочу получить:
два таймера друг над другом, выравненных по вертикальному центру родителя. Жму кнопку, верхний увеличивается вверх, нижний уменьшается тоже вверх. Жму на другую, и они уменьшаются и увеличиваются соответственно, только уже вниз. Все это сделано для того, чтоб они не наезжали друг на друга.
Поэтому в верхних двух pivot’ы указаны как нижняя центральная точка. В двух других файлах все тоже самое, за исключением того, что pivot’ы уже указывают на верхнюю центральную точку, тоесть 50% и 0%.
Строки и локализация
Идем в res/values/strings.xml и приводим его к такому виду:
name — чуть ранее я упоминал про это, так вот конструкция string/hello выдаст нам Hello World, Main!
В этом файле содержаться все строки для использования в программе.
Для локализации создаем директорию values- , в нашем случае — values-ru, и копируем туда файл strings.xml, затем открываем его и переводим на русский значения строк.
Программа
Дело за малым. Осталось написать саму программу для того, чтоб вся эта красота зашевелилась, затикала и занажималась.
Открываем java файл в папке src. Он там должен быть один.
Вверху видим название package, в моем случае com.nixan.timetracker.
Потом идут импорты других классов.
Затем объявление самого класса:
public class Main extends Activity
У вас вместо Main может быть что-нибудь еще, а именно, то, что мы задавали в create activity при создании проекта.
Activity — это… аналог форм под виндой, скажем так. Есть еще ListActivity — перечесляемый список каких — нибудь элементов, PreferenceActivity — окно с настройками, при этом все они определяются в отдельном xml файле и нет необходимости изобретать layout под всю эту красоту, но речь не об этом.
У этого класса есть метод onCreate(), который вызывается при создании окна. Таких методов несколько, подробнее можете посмотреть developer.android.com/guide/topics/fundamentals.html#actlife
Перед onCreate задаем переменные:
rest_button и work_button — кнопки переключения работа/отдых.
rest_timer и work_timer — текст, отображающий текущие значения таймера.
magnify_rest, shrink_rest, magnify_work и shrink_work — анимации, которые мы задавали в xml файле.
counter — то, что таймер будет делать.
timer — сам таймер.
resting и working — переменные, в которых хранятся текущие состояния.
rest_time и work_time — собственно сколько секунд проработали и отдохнули.
stats_editor — настройки, в них хранятся отработанные и отдохнувшие секунды, для того, чтоб закрыв программу, таймер не сбрасывался.
Первым делом проверяем, есть ли вызов onCreate родителя.
Если нет, то дописываем.
Затем говорим этому Activity, какой именно layout надо отрисовать.
R.layout.main — аналог «@layout/main», где main это main.xml в папке layout. Если ваш layout называется не main, поправьте вызов.
Перед setContentView(); можно дописать еще requestWindowFeature();, у которой в параметрах можно передать, например Window.FEATURE_NO_TITLE, чтоб убрать серый заголовок вверху окна.
Получаем сохраненные параметры таймеров:
getInt — методу передаются два параметра: строка указывающая на имя параметра и параметр по-умолчанию, если строка не найдена.
В моем случае я сделал преобразование секунд в строку ЧЧ: ММ: СС двумя разными способами.
Заранее sorry for my bad Java, но думается мне, что первый кушает меньше памяти, но работает медленнее, а другой наоборот, поэтому сильно не ругайте.
Я еще раз скажу, не знаю, насколько это правильно и грамотно, но вроде работает.
И сразу после инициализации переменных добавляем:
Таким образом выставляем ранее посчитанные секунды в текст таймеров.
Создаем задание для таймера:
Перед Override переменные, которые отвечают за секунды, минуты и часы таймера. Так как весь процесс у меня реализован в одном таймере, то по набору на отдых и работу:
И собственно задание счета:
resting — булевая переменная, обозначающая, что мы отдыхаем.
runOnUiThread() — исполняет участок кода в потоке с интерфейсом.
Выше — участок кода для отдыха, для работы же просто после этого добавляется схожий код, только с набором секунд, минут и часов для работы, первый if меняется на if (working) и текст мы устанавливаем уже не для rest_timer’а, а для work_timer’а.
Дело за малым: обработка нажатий кнопок.
Если в данный момент мы не отдыхаем, то начинаем отдыхать.
Если отдыхаем, то прекращаем отдыхать.
Если работает, то прекращаем работать.
При этом меняются только логические переменные working и resting, которые каждую секунду проверяются в таймере, и если они включены, то время начинает увеличиваться.
Таким образом нажимая на кнопку дважды мы вообще может выключить таймер, не вклчючая при этом режим работы.
Для кнопки работы все схоже.
Сохраняемя, подключаем трубку и запускаем приложение.
И сразу же находим первый баг. Если таймер запущен, и мы поворачиваем экран, то таймер останавливается.
Находим в каждом обработчике нажатий на кнопку:
И дописываем перед ним:
То есть при отрисовке формы, мы будем получать значения ключей, сохраненных при нажатии кнопок.
Источник
Разработка циферблатов для Android Wear — это проще, чем кажется
Когда возникла идея делать циферблаты для часов на Android Wear, к делу приступили без страха. Опыт разработки под «большой» андроид был, а на часиках крошечный экран и всего одна кнопка — легче лёгкого.
Как всегда, инструкции от разработчиков на developer.android.com/training/building-wearables.html остались без внимания — надо было творить, а не читать мануалы. Поэтому многие вещи, которые можно оттуда почерпнуть, становились озарениями и собственными “прорывами”.
Приложение состоит из двух частей: mobile и wear, которые любезно генерирует Android Studio при создании нового проекта. Откровением стало то, что wear компилируется, пакуется и вкладывается в raw ресурсы mobile. Mobile же представляет собой стандартное приложение для телефона, которое при установке разворачивается и пересылает wear из своего комплекта на часы. Пересылкой и закачкой занимается Android Wear — приложение-мостик, соединяющее носимые гаджеты с “большим братом” (качается из гугл-плея, чтобы раскрыть полный потенциал часов). Кстати, Моторола предлагает вдогонку установить еще своё приложение, которое как Android Wear, только Moto Connect:) Из плюшек — своя, мотороловская реклама приложений, которые глянулись лично им, ну и конечно своих фитнес-примочки.
Решено было не пилить велосипед, а воспользоваться последними достижениями инженерии, построив приложение на SDK от профессионалов, приложивших руку к истокам платформы (цитата с сайта): github.com/ustwo/clockwise
Впоследствии, когда платформа стала роднее, оказалось, что ребята написали не очень толстую прослойку над нативным WatchFaceService, оставив тем самым полную свободу для творчества и сэкономив день-другой для написания такого же базового сервиса. В любом случае спасибо: платформа позволяет конфигурировать элементы стиля будущих часов, частоту обновления, тянет на себе функционал работы с датами и делегирует методы onDraw и onLayout. Кроме того, сообщает форму часов, правда только в двух вариантах — круглые/квадратные. Моторола с её «бородой» (она же «chin» или «flat tire») определяется как круглая, но с шириной больше высоты, так её и вычисляем.
Подкузьмил знатный встроенный метод on24HourFormatChanged, который у нас не вызывался при смене формата, когда циферблат был активен. Единственный вызов происходил в момент запуска. Пришлось мониторить формат при каждом обновлении экрана.
Поскольку в команде присутствовал дизайнер, сразу было принято волевое решение — циферблаты будут отрисовываться изображениями. Никакой логики, рисования по canvas, только PNG, только хардкор. Были заведены библиотеки изображений, отвечающие за цифры и позиционные ImageView, куда нужные цифры подставлялись по HH:mm в 24 или 12-часовом формате в зависимости от локали пользователя.
Далее, вдогонку за вкусами и предпочтениями пользователей, возникла потребность выводить дату. Для неё появились 6 ImageView dd.MM.yy или MM.dd, если владелец часов родом из США.
Самым частым упрёком, который выдвигали нам на тот момент в Google Play было отсутствие ambient-режима. Его мы сознательно не реализовывали, потому что его существование упоминалось в инструкции, причём почти без картинок и далеко не в начале.
Согласно хорошей идее создателей платформы, часы через несколько секунд бездействия переходят в режим экономии батареи, посылая сервису, отрисовывающему циферблат, соответствующее уведомление. Циферблат должен отреагировать на новое состояние, полностью упростив интерфейс. Вместо красочного рисунка со множеством свистелок, экран необходимо залить на 95% чёрным цветом, оставив только тусклые циферки. Далее качество изображения зависит от производителя часов: кто-то отображает оттенки серого в ambient режиме, а кто-то выводит картинку в ч/б. Также в этом режиме предписывается отключить все энергоёмкие функции вашего приложения — никаких геолокаций и просмотра youtube.
Последним шагом стал функционал отображения информации об уровне заряда батареи телефона и часов. Общение с телефоном происходит через гугловый слой сообщений, поэтому такой незначительный вроде бы объём информации потребовал реализации дополнительного сервиса, ставшего краеугольным камнем в инфраструктуре обмена сообщениями между телефоном и часами.
P.S. И лайфхак для тех, кто не в курсе. Чтобы избавиться от надоедливой подсказки «Ok Google» на экране часов, нужно 2-3 раза произнести это самое «заклинание». Вуаля, больше глаз ничего не мозолит!
Источник