Как стать автором
Обновить

Кавказская кухня: проблемы и решения

Время на прочтение6 мин
Количество просмотров2.2K
Привет всем хабровчанам! Рад сообщить вам, что компания, в которой я работаю, выпустила приложение «Кавказская кухня» под Android. Теперь любимые блюда будут всегда у вас под рукой. Я занимался почти всем процессом разработки приложения и хотел бы поделится деталями с вами. Вначале хочется рассказать о самом приложении, а во второй части статьи о проблемах возникших при разработке и возможных вариантах решения. В итоге, те кому интересно приложение могут прочесть первую часть, а те кому интересен процесс разработки вторую. Поехали!

Обзор приложения

Скриншоты и само приложение можно посмотреть как обычно в маркете — market.android.com/details?id=net.octobersoft.android.caucasiancuisine

С помощью приложения вы сможете быстро найти свой любимый рецепт и способ приготовления(в базе чуть больше 200 рецептов). Кроме того, с помощью помощника вы можете быстро найти рецепты по имеющимся у вас продуктам, с подсказкой сколько ингредиентов содержатся в найденном рецепте. С помощью корзины вы сможете сохранять нужные вам продукты, либо добавлять свои, после чего начиная приготовления какого-либо рецепта поставить таймер и засучив рукава начать готовить. Добавить ингредиенты в корзину можно при просмотре рецепта(в первой вкладке после выбора категории рецепта и понравившегося рецепта), а новые добавленные в корзину продукты будут отображаться в помощнике со всеми остальными. И конечно, все понравившиеся рецепты можно держать в избранном, для быстрого доступа к ним. При выборе всех рецептов появляется поисковый бар, который поможет вам быстро найти нужный рецепт. Такое вот удобное приложение, которое поможет вам приготовить любимое блюдо.

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

Разработка. Возникшие трудности и возможные решения

Табы и несколько окон
Как вы могли заметить, навигация по приложению осуществляется с помощью табов. Но во многих вкладках есть несколько окон. Сложность заключается в том, что если не чистить запущенные окна при переходах, то иерархия вьюшек привысит допустимое значение и приложени упадет. Для этого можно создать свой класс наследуемый от TabActivity(который наследует главное окно приложения, где все вкладки), который переопределит onBackPressed() нужным образом, либо вызывать самостоятельно finish() при переходах.
Также, была проблема с кастомизацией внешнего вида для одной вкладки. На самом деле сложного в этом нет ничего, вы просто пишите свой лэйаут с вьюшками и стилизуете его как угодно передавая в таб.

Состояния активити

Как утверждают некоторые авторы, чаще всего(и все что вам в основном необходимо) используются состояния onCreate(само собою), onStart() и onPause(). Да, действительно это так, но в нашем приложении таймер работает еще и в фоновом режиме и при разрушении окна(когда вызывается onDestroy(), он сбрасывает состояние в базу, чтобы потом возобновиться при запуске приложения). Авторы Pro Android 2 утверждают что данный метод, может и не вызываться во все, как и onStop(), так что будьте внимательны и изучите жизненный цикл окна досконально. Можно было написать для таймера службу, как вариант решения проблем, но тогда могли бы возникнуть и другие.

Градиентные изображения

Некоторые изображения в нашем приложении содержат градиенты и по-умолчанию(по крайней мере у меня в эмуляторе и на реальных устройствах) градиенты расплываются в полосы, что выглядит жутковато. Решение проблемы как выяснилось простое: задать палитру окна. А сделать это можно переопределив метод активити onAttachedToWindow() добавив следующие строки в метод:

super.onAttachedToWindow(); //не забывайте всегда вызывать версии методов для суперкласса!
Window currWind = getWindow();
currWind.setFormat(PixelFormat.RGBA_8888);


Не забывайте про Handler

Если потребуется отображать что-либо в потоке UI, а вы находитесь не в контектсе активити(это конечно же не Android-way), вы не сможете обойтись беэ этого класса. Другой вариант — воспользоваться AsyncTask. Хотя вариантов использования данного класса гораздо больше(к примеру очередь потоков, обновляющих элементы UI).

Вызов событий клавиатуры программно

Иногда такое может потребоваться и решается например так:
new BaseInputConnection(txtView,false).sendKeyEvemt(backPressedKeyEvent);

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

Кэш SQLite

SQLite кэширует данные. Просто необходимо об этом помнить во время разработки приложения и ставить галочку wipe user data(если хотите обновить данные). В свое время я потратил несколько часов на понимание почему так, не зная этой особенности.

Списки

Наследовать от ListActivity или нет? На самом деле, разница только в том нужны ли в дизайне окна другие элементы и собственно сам лэйаут. Если да, то наследование вам не к чему, если нет, то наследуйте класс и setContentView вызывать ненужно.
Также следует помнить одну важную вещь про списки. Список использует повторно элементы вью, поэтому работать с ним нужно внимательно. Можно например воспользоваться методами вью: setTag(), getTag() для того чтобы привязать произвольные идентификаторы. Также следует внимательно отнестись к написанию пользовательского адаптера и использования в методе getView() параметра convertView, собственно про это и я писал выше!

Другой проблемой при работе со списками было некорректное отображение элементов списка в ScrollView.
Макет включает такой кусок:
...
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<ImageView
android:src="@drawable/icon"
android:id="@+id/recipe_img"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:layout_weight="0"/>

<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:divider="#00000000"
android:cacheColorHint="#00000000" />

<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2"
android:gravity="center"
android:scaleType="fitXY"
android:src="@drawable/cook"/>
...


В таком варианте дизайна элементы списка усекаются и видны не все(по-моему есть такой issues на гуглкоде)
Решение проблемы было задать размер списка по кол-ву элементов в нем, код ниже:
//set ListView size by items count
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}

int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}

ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}


Диалоги

При написании кастомного диалога(наследовав класс Dialog) возникла трудность: кнопки легко нашлись по findViewById() и корректно работали, но виджет EditText нет(метод возвращал null). Для поиска корневого элемента, как обычно, использовался LayoutInflater. Выход был прост — создать класс Dialog, передать нужный лэйаут и потом уже вызывать от диалога findViewById(). И кстати, контекст приложения передать так и не удалось как я не пытался, потому что диалоги привязаны к текущему активити. Но подозреваю все же, что можно решить и эту проблему!

Не изобретайте велосипед

Много готовых классов и приложений уже есть в Android и потому лучше сперва посмотреть внимательно доку или погуглить.

Используйте LogCat

Логгирование помагает быстро найти ошибку при разработке(особенно используя свои фильтры в Eclipse) и просмотреть необходимый вывод активити, служб и т.д., потому всегда используйте его. Но из релиз-версии логгирование нужно убрать(как говорит гугл), хотя куча стандартных сервисов и приложений на устройстве все равно пишут в лог!

SQLite Manager

Я использовал данный плагин к Firefox 3.х для работы с базой sqlite. Он бесплатен, удобен, функционален(можно писать процедуры, смотреть структуры таблиц, данные, выполнять запросы и т.д.). Так что всем кто еще не знает или хочет выбрать подобный инструмент рекомендую, для разработки самое то. Хотя и альтернатив куча!

Ограничение на размер приложения

Это самый забавный пункт. В день или точнее час релиза в маркет мы узнаем, что приложение не может быть больше 50Мб, а наше весит 79! Спасибо тебе Гугл за такую фишку… из-за этого пришлось сделать не самое приятное решение…

Скриншоты

Так выглядит окно с рецептами в выбранной категории:
image

Экран со всеми категориями рецептов:
image

Экран выбранного рецепта:


А так выглядит окно с таймерами:


Заключение

Подчеркну, что мои рекомендации могут быть оспорены, так как я не являюсь гуру в Android(это мое первое приложение и по совместительству первый пост на Хабре) и в платформе часто встречается несколько путей решения одной проблемы, но рассмотренные советы работают и это радует! Поддерживаю призыв к обмену опытом, который уже был на Хабре.

В остальном, процеcс разработки был примерно как в веб-приложениях: пишется модель данных, CRUD-операции для этой модели, интерфейс и необходимый функционал. Трудности были по незнанию платформы Android и отсутствия опыта в создании приложений под мобильные платформы.

Замечания и пожелания по топику в личные сообщения.
Спасибо за внимание!

UPD Теперь приложение доступно и в бесплатной версии:
market.android.com/details?id=net.octobersoft.android.caucasiancuisinefree
Теги:
Хабы:
Всего голосов 47: ↑33 и ↓14+19
Комментарии63

Публикации

Истории

Работа

Ближайшие события