Зуд под коленкой не дает уснуть, требует выплеснуть полученный экспириенс, так что я попробую на пальцах рассказать, как сделать виджет-часы для андроида; на пальцах – потому что (как я до сего времени любил говорить про Си) про яву я не прочитал ни одной книжки, поэтому предметом особо не владею – так, понахватался понемногу отовсюду.
Результатом должно стать вот это:
При добавлении виджета должен появляться экран настроек, где можно указать – какую программу запускать после тапа по области с датой и часами и как (12/24) выводить время:
Начинать надо с начала – с дезигна.
Для начала надо прочитать ту часть документации, где обьясняется, как рассчитать размер будущего виджета; К.О. утверджает, что это должен быть виджет 4*1, т.е. высотой в 1 “клетку” и шириной в 4; тогда по формуле из документации ширина нашего виджета = (number of cells * 74) - 2 = 294 пиксела и высота = 72; эти значения нужны для описания виджета, но сначала они понадобятся для рисования эскиза в фотошопе или гимпе.
Для натуралистичности рисовать прототип лучше всего поверх настоящей картинки, легко и просто взять ее с эмулятора (ежу понятно, что к этому моменту надо установить эклипсу или идею, скачать сдк и понастраивать все, вот чего-чего, а детальных прошождений этого процесса в инетах тыща и маленькая тележка). После установки сдк в папке диск:\путь_к_сдк\tools есть куча полезных программок, сейчас нужна ddms.bat (юные помошники К.О. дружно идут в сад, декламируя, что .bat - это не программа и все такое); после ее запуска появляется окно “Davlik Debug Monitor”, в котором видим подключенные для отладки телефоны или эмулятор и кучу полезных окошек с разной инфой; сейчас нам надо скриншот из эмулятора – выбираем эмулятор из списка (если вдруг там есть еще что-то.. или выбираем устройство, в общем то разницы никакой) и жмем Ctrl-S – появляется окошко со скриншотом:
Сохраняем в файл (или копируем) и открываем в графическом редакторе.
В виджете (в отличие от “обычных” программ) могут использоваться только ограниченный набор контролов – важно то, что нельзя использовать кастомные контролы (а я сдуру был сделал кастомный контрол, отладил его и только потом выяснил, что зря старался.. ну не то, чтоб зря, но в общем начал все заново); фактически виджет больше похож на сверстанную страничку, где можно использовать готовые изображения, текст и несколько специализированных контролов – я бы допер до этого намного раньше, если бы использовал еще один инструмент из сдк – hierarchyviewer.bat – он показывает, что на самом деле рисуется на экране телефона, кто из кого растет и все такое.. очень полезная программка.. не очень интуитивно понятная и удобная, но полезная:
Вот например так устроены мои часы. Видно, что по идее тут таблица из одного столбца и 3-х рядов – строка с днями недели, текущий из которых выделен (сам текст белый, выделение черное – хорошо читается на любом фоне), потом строка с крупными датой и временем, и еще строка с месяцами (принцип выделения тот же). Дни недели и месяцы – текст (локализующийся автоматически в зависимости от региональных настроек телефона), дата и время - “пререндеренные” цифры, крупные, белые с темной обводкой (по той же причине – читаемость на любом фоне).
Собственно сначала я нарисовал картинку, поместив ее в рассчитанный выше прямоугольник 294*72 пикселя, подбирая шрифты и разглядывая так и сяк (в т.ч. и в телефоне, загрузив картинку и положив ее как обоину); смысл возни был в том, чтобы дата и время читались безнапряжно, опыт пользования в течение 2-х недель вроде показывает, что все ок:
Выделение текущего дня недели или месяца на самом деле состоит из: маленького полукруга-картинки и текста с черным фоном; т.о. первая строка состоит на самом деле из 4-х “столбцов”: левый полукруг, прямоугольник с текстом, правый полукруг, прямоугольник с текстом; 3-я строка собственно строится так же
Все это похоже на таблицы вложенные: есть таблица с единственной ячейкой – весь виджет; в этой ячейке таблица с 3-я рядками и одним столбцом – вот эти самые 3 строчки; 2-я строчка (с датой и временем) состоит на самом деле еще из 2-х таблиц – для даты и для времени; сделано так (может и не совсем правильно) для того, чтобы дату выровнять по левому краю, а время – по правому. В ячейке для даты - (сюрприз-сюрприз) еще одна таблица, с 2-я столбцами, для каждой цифры свой, то же самое с ячейкой для времени – там таблица с 5-ю столбцами, цифры+разделитель “:”. Собственно если присмотреться, то на картинке с иерархией выше все это видно.
Так вот, к чему все это: с днями/месяцами понятно, картинка-полукруг, текст, картинка, текст; а дата/время формируются из отдельных цифр, значит надо подготовить эти цифры; собственно тут все очень просто – пишем “0”, обрезаем картинку вменяемо (скажем, оставляя по 1-пиксельному зазору слева-справа, по высоте она должна равняться 72 – 2 * высота_текстовых_областей, по моей картинке это 36 пикселей), сохраняем в файл скажем с именем d0.png (у моих цифр обводка полупрозрачная получилась, поэтому сохранять надо в png-24 с сохранением прозрачности), и так еще 9 раз с остальными цифрами, так же надо сохранить и картинки-полукруги, должно получится вагон и маленькая тележка картинок:
Собственно, тут дизайн и закончился; теперь начинается макетирование.
Вот те “таблицы”, про которые я только что написал – это конечно непрофессионально. Правильно говорить.. не знаю как, LAYOUTs, все, что видно на экране телефона, описывается этими лайаутами, и виджет тоже. Уже нужна ide, или эклипса, или idea (которая последняя, комьюнити эдишн, она знает про андроид, не требует денег и хоть и тормозная слегка, но имхо удобнее эклипсы)
Запускаем, создаем проект SimpleClockWidget, packageName “info.hamster.simpleclock.widget”, если предложат – не надо никаких активити со старта. Idea создает структуру папок при создании проекта, не помню, как эклипса – факт в том, что все эти картинки надо положить в папку res/drawable проекта.
Теперь создаем еще один ресурс, на этот раз – описание разметки нашего виджета: File-New-Android resource file, resource type – layout, resource name – main, появляется новый файл main.xml с заготовкой кода:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> </LinearLayout>
LinearLayout – условно говоря таблица с 1-й колонкой или рядом, элементы в которой размещаются соответственно снизу вверх или слева направо. У меня ячейки должны располагаться вертикально, так что:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> </LinearLayout>
Кроме того, здесь задается ширина и высота “ячейки” – по размерам родителя. Внутри наши 3 ряда, 2 потоньше и один потолще, с цифрами, так что
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:layout_alignParentTop="true" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="36dip" android:layout_centerVertical="true" android:layout_centerInParent="true" > </LinearLayout> <LinearLayout android:layout_alignParentBottom="true" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > </LinearLayout> </LinearLayout>
Они располагаются горизонтально, по ширине заполняют все доступное пространство, по высоте лайоут с датой имеет фиксированное значение 38 пикселей, а лайауты с днем/месяцем – по 18 пикселей. Содержимое лайаута с днями недели:
<ImageView android:src="@drawable/l" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/cur_day" android:text="empty_day" android:textSize="18sp" android:textColor="#FFFFFF" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/blackbg" /> <ImageView android:src="@drawable/r" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="" android:id="@+id/days" android:textSize="16sp" android:textColor="#989898" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
Здесь много всего: сначала идет картинка, которая берется из файла l.png из папки с ресурсами, потом идет текстовый контрол, на который я буду ссылаться из кода по id cur_day, здесь фигурирует еще один графический ресурс, о котором я забыл – изображение 1*1 пиксел, полностью залитое черным цветом (по другому я пока не догадался сделать фон теста), для отладки сразу задаю значение empty_day; дальше снова картинка и снова текст, теперь серый. Параметры всех тегов подбирались в бОльшей степени методом тыка, так что я даже не очень могу внятно обьяснить, почему так все, а не иначе :)
Точно так же выглядит лайаут с месяцами, другие id только естественно; лайоут с часами/датой формируется из отдельных цифр, для отладки пока достаточно одной:
<ImageView android:src="@drawable/l" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/cur_day" android:text="empty_day" android:textSize="18sp" android:textColor="#FFFFFF" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/blackbg" /> <ImageView android:src="@drawable/r" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="" android:id="@+id/days" android:textSize="16sp" android:textColor="#989898" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
Чтобы посмотреть, как это все выглядит "в живую", надо сделать следующее: создать активити с любым именем, указать, что это "стартовое" активити (идеа все это делает сама - создает файл, добавляет строки нужные в манифест), в onCreate добавить загрузку разметки активити:
<ImageView android:src="@drawable/l" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/cur_day" android:text="empty_day" android:textSize="18sp" android:textColor="#FFFFFF" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/blackbg" /> <ImageView android:src="@drawable/r" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="" android:id="@+id/days" android:textSize="16sp" android:textColor="#989898" android:gravity="center_vertical|left" android:layout_gravity="center_vertical" android:singleLine="true" android:layout_width="fill_parent" android:layout_height="wrap_content" />
Для отладки можно еще указать точно высоту основного лайоута (74 пикселя) и добавить файл для фона, потому что черное на черном не очень то смотрится - получим что-то вот такое:
т.е. как будто бы все вписывается. Дальше будет средняя часть с цифрами.
Исходники проекта в текущем состоянии - здесь. Достаточно распаоквать куда-то, в идеа создать новый проект из существующих исходников - дальше она все сама сделает
Забавная статья, я как раз тем же и занимаюсь, вот только simple clock оказалось совсем не просто сделать... Часики заработали, а вот функцию запуска по тапу так и не удаётся до сих пор сделать.. Может у тебя уже есть какие мысли по этому поводу?
ОтветитьУдалитьдык смотрим дальше, part 0.1, 0.2 и так далее.. там это есть
ОтветитьУдалитьТак как на счёт кастомных контролов на виджет ?
ОтветитьУдалитьНа сколько я понимаю, их всё же можно сделать. Вроде видел виджеты, которые изменяют свой вид по мере работы....
кастомные контролы использовать нельзя - и точка.. доступ к контролу в виджете идет через remoteview, а тот понимает очень ограниченное количество контролов.
ОтветитьУдалитьно с другой стороны - можно использовать картинку и рисовать по ней, что захочется; я не знаю, с какой частотой реально можно обновлять картинку правда, можно ли сделать там бегущую строку, потыкав "иерархи-вьювером" по разным контролам - все используют картинки, если надо сделать что-то эдакое..
по-моему как-то так..
Вот нашёл. Подробненько описано и более менее понятно. Вдруг кому-то понадобится http://derevyanko.blogspot.com/2010/10/android-how-to.html
ОтветитьУдалитьну так да - используя картинку и обновляя виджет можно получить подобие анимации..
ОтветитьУдалитьбегущую строку я погорячился - в принципе и ее сделать можно по такому же принципу (хе-хе, тавтология) - текст обновлять, он будет бежать..но как грузиться система будет - имхо только даром батарейку тратить :)