Android: Создание своих собственных визуальных элементов

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

Создадим свой собственный визуальный элемент управления. Делается это созданием нового класса, наследуемого от класса View.
Всё, что данный элемент управления должен отрисовать в себе делается в оверрайднутом методе onDraw, у которого в качестве параметра – канва. В этом методе самое главное – это в самом конце вызвать метод invalidate() – он даст знать Андроиду, что надо перерисовать канву. Если этот метод не вызвать, то чёрт его знает сколько придётся ждать пока Андроид решит, что данные устарели и принудительно перерисует наш элемент управления.

Предположим, что нам надо перерисовывать содержимое элемента управления в зависимости от того что пользователь навозюкал там внутри пальцем. За это отвечает метод onTouchEvent. Принимаемый им параметр event позволяет узнать всё, что пользователь делал с нашим контролом – как возюкал или тыкал и где конкретно он это делал.

Этих методов достаточно, чтобы контрол уже работал. Мы ставим наш контрол на лэйаут и далее у нас появляется задача как-то управлять этим контролом снаружи. Например, нам нужно знать координаты места где пользователь повозюкал пальцем. Метод, который мы описали ранее – это встроенный метод контрола, он не может никак повлиять на внешние для этого контрола вещи. Но мы можем создать в том классе статические глобальные (для класса) переменные и менять их из того метода, а уж доступ к этим переменным нам обеспечен – за счёт того, что они статические. Статические – значит они общие для нашего класса, а не создаются с каждым инстансом. Это не очень хороший стиль программирования с моей точки зрения, но если контрол в программе присутствует в единственном экземпляре – то почему бы и нет.

public class myNewView extends View
{

static int X; // Переменные, доступные снаружи
static int Y;

public myNewView(Context context, AttributeSet attrs)
{
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event); // очень важная строка, позволяет не только обработать
//касание здесь, но и при назначении метода onClick в
//Activity, инстантиировавшего наш
//контрол - обработать нажатие еще и там.
X=(int) event.getX(); // установка переменных для доступа к ним снаружи
Y=(int) event.getY();
return true;
}

@Override
protected void onDraw(Canvas canvas)
// метод OnDraw вызвается Андроидом тогда, когда нужно отрисовать данный View
{
// Здесь можно нарисовать всё что угодно на canvas
invalidate();// invalidate() нужен для того, чтобы оповестить Android,
//что нужно выполнить метод OnDraw снова, без него View не будет перериcовываться.
}
}

Проблемы с Samsung Galaxy Note

В торговом центре довелось пощупать новый Samsung Galaxy Note. Вообще, с момента презентации на Хабре хотелось пощупать, решить хочу ли я такое устройство…

Испросил разрешения и поставил на него Zhongwen Cards. Запустил и офигел – приложение НЕ растянулось на весь экран. При этом когда я пробовал его на Galaxy Tab’е с экраном гораздо большим – всё было нормально. Интересно. Пока не нашёл с чем это может быть связано…

Zhongwen Cards

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

Переписал модуль импорта XML. Подключил его в том числе и к работе с файлами на SD-карте. Пока формат только мой собственный, нужно будет сделать импорт также из Pleco, Anki и, возможно, ZDT (не помню, есть ли там экспорт в XML). Нужно теперь сделать еще возможность экспорта, исправить несколько юзабилити-неприятностей. Еще всё-таки очень хочется довершить ту старую идею с тестом на путающиеся инициали и финали, совместно с путающимися тонами. Ну а затем наверное стоит продумать что еще у меня не кастомизируется и сделать это кастомизируемым :)

Также подумываю над тем, чтобы разделить локали – возможный задел на монетизацию. Потому что я, конечно, альтруист, но в первую очередь для русскоязычного населения. У мериканьцев на английском и так дофига учебного софта и материалов :)

Zhongwen Cards v.1.0

С Андроид-маркетом я пока подожду. При регистрации в качестве разработчика с меня попробовали слупить 25 мёртвых американских президентов. Мне это дорого, особенно если учесть, что приложение я собирался делать бесплатным. Собственно, теперь я понял, почему большинство приложений в Маркете платные – надо же как-то стартовый взнос возвращать :)

Пока выложил первую ревизию приложения сюда. Ссылка: http://zwc.shengsu.org/zhongwencards.apk

 

А вот фотографии работающего приложения.

Главное меню – набор колод с картами. Колоды можно создавать, скачивать, переименовывать, удалять.

Карточки в колодах можно создавать, редактировать, удалять, копировать в другие колоды. При вводе пиньиня можно пользоваться цифрами для обозначения тонов – программа их сконвертирует в диакритические тоны сама. То есть можно ввести “huan1 ying2″, а в карточке получится “huān yíng”. Это очень удобно, т.к. в стандартной андроидной английской раскладке отсутствуют необходимые символы с диакритикой.


Основная часть программы. Собственно режим флэш-карточек. Порядок следования сторон карточки настраивается. Мне более всего удобен порядок “Пиньинь – Ханьцзы – Перевод”, где я по пиньиню пытаюсь вспомнить иероглифы, ну и перевод тоже. Вот здесь, как вы видите, я ошибся:

Перелистнул карточку и опечалился своей ошибке. Нарисованное можно стереть и пару раз нарисовать правильно, чтобы вбить в руку:

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

Zhongwen Cards: проблемы поддержки и монетизации

В общем, я уже доволен своей программой до той степени, когда её можно уже открыто презентовать. Это не означает, что реализовано уже всё. Еще буду допиливать, но на текущей стадии уже работает весь необходимый изначально функционал – те самые пресловутые три причины (см. ранее).

Далее, я считаю, нужно делать первый релиз программы. Пословица гласит, что встречают по одёжке. Это значит, что на главное место выходит домашняя страница программы. Она должна быть приятной. В моём понимании это означает (1) отсутствие вырвиглазности, (2) интуитивно понятные разделы и (3) безглючный сервис.

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

Идея замещающих колод, подсмотренная у OrangeOrApple.com, подкупает простотой использования. Не требуется регистрация – просто введи имя колоды, скопируй из Экселя свои карточки – обрабатываются четыре поля (Ханьцзы, пиньинь, перевод и примечание, которое может быть использовано, к примеру, под пример использования) – и нажми на кнопку отправить. Всё, колода в базе. Дальше из программы жмём “Скачать колоду”, вводим имя колоды – и колода скачивается. Далее можно переименовать колоду в программе. Маленькое но – имя колоде на сайте надо выбирать такое, чтобы быть уверенным, что за тот промежуток времени, пока колода не будет скачана в программу, никто не засунет в базу другую колоду под тем же именем…

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

Ханьцзы \t пиньинь \t перевод \t примечание \n

 

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

Первая и вторая проблема подбивают меня на то, что для домашних страничек программ и сервиса нужно ставить CMS. Сижу и думаю – какую же выбрать, чтобы не пришлось долго иметься с совершенно неинтересными мне веб-дизайном и HTML-кодингом. Джумла? Друпал? Что-то еще?

Ну и напоследок об монетизации. Модное словечко, ага. Это то, ради чего пишутся большинство программ и сервисов. Ненавижу, если честно. Единственный способ монетизации, который мне нравится – это парадигма DonationWare. При этом я прекрасно понимаю, что заработать прилично на этой парадигме практически нереально – сам я ни разу ни копейки не донейтил никому. Но зато можно купаться в посланных тебе лучах радости когда у людей всё работает и отбиваться бесплатностью от лучей поноса, когда что-то не работает. В общем, парадигма для тех, кому лениво осуществлять поддержку дурацких запросов тех, кто телескопами смотрит на инфузорий-туфелек только потому, что там есть увеличительное стекло.

 

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

Zhongwen Cards

Просто дико лениво доделывать программу после того, как она полностью заработала. Даже и не знаю что с этим делать – вот всегда так.

На данный момент не реализовано из того, что хотелось бы иметь:

  1. Перемещение и копирование карточек между колодами;
  2. Переименовывание колод; Сделал;
  3. Вывод всех карточек из всех колод с полным выводом всех полей и возможностью сортировки;
  4. Отображение комментария по карточке по запросу; Сделал;
  5. Закачка колоды из программы на сайт с последующей выгрузкой файла;
  6. Настройка тем оформления – не только цвет и размер шрифта на карточках (что уже есть), но и цвет подложки каждой стороны карточки отдельно; Сделал;
  7. Вывод карточек в случайном порядке – в данный момент они выводятся только строго по порядку; Сделал;
  8. Нормальный дизайн на сайте и нормальные скрипты с дурозащитой.
  9. Интернационализация приложения – все строки надо вызывать всё же из R.string.blah-blah, а не прописывать железно. Сделал.
  10. Нужно придумать и нарисовать иконку приложения.

Плюс сам код выглядит очень непрезентабельно – всё же это учебное приложение.

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

Android: Zhongwen Cards

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

Требования такие:

  1. Карточки должны быть обязательно трёхсторонними! Можно и больше сторон, но три стороны – это минимум. Потому что специфика китайского языка, для изучения которого мне нужно приложение, подразумевает отдельное запоминание Ханьцзы написания, транскрипции пиньинь с тонами и перевода. Большинство программ, которые я посмотрел были либо с фиксированно двусторонними карточками, либо три стороны было фичей “абы было” – без нормальной поддержки.
  2. Обязательна возможность рисовать пальцем по экрану во время показа карточки! Опять же та же самая специфика – мозг устроен так, что когда показываешь пиньинь и не даёшь возможности написать иероглифы, мозг говорит “а, я знаю как это пишется”. Переворачиваешь карточку и мозг тебе говорит – “ну да, именно это я и имел ввиду”. Реального запоминания при этом не происходит. Для реального запоминания иероглифов пока еще не придумано ничего лучше прописывания.
  3. Обязательна возможность как создавать/редактировать карточки на самом устройстве, так и возможность подготавливать их в любом другом месте с последующей загрузкой. При этом мне не важно будет ли загрузка онлайн или оффлайн, лишь бы после загрузки карточки хранились в самом телефоне.

Все приложения, которые я просмотрел не удовлетворяют как минимум двум из трёх из этих требований. Большинство – вообще ни одному. Самое близкое, что я видел – это приложение с забавным названием “Kaka Flashcards” поляка по имени Марцин Отшеретко. Там нету только второго пункта.

Зато абсолютно во все приложения, которые я видел, напихана совершенно ненужная возможность качать и расшаривать карточки с различных флэшкарточных сетевых ресурсов. Вот нафига мне могут понадобиться чужие карточки? Это же полный бред…

 

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

В данный момент занимаюсь разработкой третьего запроса – созданием колод и карточек. Приложение работает на базе данных. Внутренне база данных уже заполняется и извлекается без проблем. Осталась возможность загружать карточки снаружи – это очень важно, по той причине, что заполнить несколько карточек для тестовых целей несложно, а вот создать нормальную колоду карточек на 20 минимум уже запаришься, к тому же при разработке база создаётся и удаляется бесчисленное количество раз, вводить карточки каждый раз – это совершенно безумное расточительство собственного времени. К тому же, стандартные методы ввода Андроида не позволяют ввести гласные с необходимой диакритикой – я предпочитаю, чтобы тона в пиньине показывались именно ей, а не цифрами.

Сейчас остановился на том, что для дальнейшей разработки требуется серверная часть. Создал поддомен, изучаю заново PHP и MySQL, ваяю загрузку карточек в базу. Плюс, будет необходим парсинг данных так, как это сделано у OrangeOrApple.com – из Экселя данные копируются и вставляются в форму все вместе, а не по одной карточке.

Java: Понимание того, как работает final

Есть в Java такое любопытное зарезервированное слово – “final”, которое является модификатором при объявлении переменных. Не очень пока понятно для чего подобное нужно и где оно важно, но всё же.

final превращает переменную в однократно-программируемую. То есть объявили переменную, после чего где-нибудь в коде её проинициализировали – и опа! Второй раз её перепрограммировать уже нельзя – компилятор будет ругаться.

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

Android: Integer vs int

Паскальное детство даёт о себе знать. Так уж получилось, что Паскаль у меня был первым осмысленным языком программирования. И единственным на целый год. Потому что через год, перейдя на C, я стал смотреть на Паскаль как на нечто несерьёзное. И сейчас считаю Паскаль и его диалекты навроде Delphi, который с некоторых пор официально называется именно “язык Delphi”, а не “Object Pascal”, детскими игрушками не для серьёзного применения. И вопли о том, что дескать Скайп или еще какая-нибудь широко распространённая приблуда на нём написаны меня не волнуют.

Так вот, написав предыдущий пост я задумался, а почему я там пишу Integer, а не int, который я тоже видел в листингах программ для Андроида. Полез в документацию – оказалось, что Integer – это класс-оболочка для int. Это означает, что если нужна переменная целочисленного типа, принимающая значения в известных пределах, то можно использовать и int, вот только у Integer есть неоспоримое преимущество в наличии нескольких весьма полезных публичных методов – те же parseInt или toString, которые я чувствую придётся применять довольно часто.

Android: SharedPreferences

Полдня потратил, чтобы врубиться в класс SharedPreferences для Андроида.
Вот что пишут везде.

Во-первых в Activity нужна public static final константа, характеризующая набор настроек (которых может быть много разных):
public static final String MY_PREFERENCES = "MyPrefs";

Для сохранения общих настроек приложения нужен вот такой код:
SharedPreferences settings = getSharedPreferences(MY_PREFERENCES,0);
SharedPreferences.Editor prefEditor = settings.edit();
prefEditor.putString("FirstString", "abcdefg");
prefEditor.putInt("SecondString", 55);
prefEditor.commit();

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

Для загрузки их же при старте приложения (или конкретного Activity, чьи настройки нужны) в методе onCreate прописывать надо:
SharedPreferences settings = getSharedPreferences(MY_PREFERENCES,0);
String str1;
Integer i2;

if(settings.contains("FirstString")){
str1 = settings.getString("FirstString", "");
}else
{
str1 = "";
}

if(settings.contains("SecondString")){
i2 = settings.getInt("SecondString", 0);
}
else
{
i2 = 0;
}

Вроде бы всё работает. Вот только сохранять в настройки фиксированные значения неинтересно и не нужно никогда. А поэтому нужно использовать преобразования типов. Вот такие:

Чтобы преобразовать из Integer в String:

Integer a;
String str;
str = Integer.toString(a)

Преобразовать из String в Integer:

Integer a;
String str;
a = Integer.parseInt(str);

Интересная особенность контролов в Java: чтобы к примеру взять строку из контрола EditText и преобразовать её в Integer нужно сделать следующее:

Integer i;
EditText text;
text = (EditText) findViewById(R.id.editText1);
i = Integer.parseInt(text.getText().toString());

Забавно здесь то, что метод getText() у EditText’а возвращает нифига не String, а объект класса android.text.Editable – чтобы получить из него String и нужен вызов метода toString().