Programowanie na urządzenia mobilne jest proste.
Android to nazwa nowego systemu operacyjnego dla urządzeń mobilnych, tworzonego i rozwijanego przez Google, a wspieranego przez szereg najważniejszych graczy na rynku. Fakt ten oraz zbliżająca się premiera pierwszego telefonu z zainstalowanym Androidem sprawiają, że warto przyjrzeć się możliwości tworzenia na niego własnych aplikacji.
Rynek mobilny stale ewoluuje w stronę urządzeń multimedialnych, będących tak naprawdę niewielkim komputerem. W ostatnich miesiącach furorę zrobił Apple ze swoim iPhonem, a teraz do rywalizacji przystępuje kolejny konkurent – Google z systemem operacyjnym Android, który wkrótce będzie obecny na telefonie HTC Dream.
Choć dla giganta z Mountain View jest to stosunkowo nowy obszar działalności, to wielu ekspertów wróży całemu przedsięwzięciu sukces. Google już od ponad roku przygotowuje swój system operacyjny, udostępniając publicznie SDK (Software Development Kit) i zachęcając programistów (m.in. przez konkurs z dużymi nagrodami finansowymi – patrz ramka) do budowy aplikacji dedykowanych Androidowi . W chwili premiery nowego urządzenia HTC, Android będzie miał więc szereg gotowych aplikacji, a Google doświadczenie i uwagi zebrane przez developerów podczas ich prac nad wersją testową systemu. Nie bez znaczenia jest również wsparcie Open Handset Alliance – konsorcjum, w skład którego wchodzi ponad 30 przedsiębiorstw, m.in. T-Mobile, HTC, LG, Motorola czy Samsung.
Jednym z głównych czynników decydujących o sukcesie systemu operacyjnego jest lista aplikacji, jakie można pod nim uruchomić. Firmy zaangażowane w rozwój Androida wydają zdawać sobie z tego sprawę i przygotowały dla developerów szereg narzędzi i porad, umożliwiających pisanie programów. Co ważne – w przeciwieństwie do Apple – całość jest dostępna publicznie dla każdego, bez konieczności wykupywania dostępu.
Google pomoże również w dystrybucji i sprzedaży naszych aplikacji, poprzez specjalnie do tego celu zaprojektowany \”Google Market\”. Podobne rozwiązanie wprowadziło Apple dla swojego urządzenia – \”Iphone app store\” okazał się strzałem w dziesiątkę, zarówno dla użytkowników, którzy w jednym miejscu mogą tanio rozszerzyć możliwości swojego telefonu, jak i dla developerów, zarabiających na sprzedaży aplikacji często niemałe pieniądze.
Architektura Androida
System operacyjny Android oparty jest na jądrze Linuksa w wersji 2.6, które zapewnia obsługę najniższych warstw, takich jak zarządzanie pamięcią, procesami, siecią czy sterownikami. Wyższą warstwę systemu stanowią biblioteki, odpowiedzialne m.in. za media (obsługa audio, wideo i zdjęć), wyświetlanie grafiki 2D i 3D (OpenGL), czcionek (FreeType) czy dostęp do bazy danych (SQLite). Środowisko działania dla aplikacji stanowi maszyna wirtualna Dalvik.
Do kodu tejże maszyny komplikowane są aplikacje napisane w Javie. Jest to specjalna wersja maszyny wirtualnej zaprojektowana przez Google dla urządzeń mobilnych, niekompatybilna ze standardową maszyną wirtualną Java. Najwyższe warstwy systemu stanowią framework aplikacji oraz same aplikacje.
Projektowanie dla urządzeń mobilnych
Tworząc aplikację dla Androida należy pamiętać o przeznaczeniu tego systemu operacyjnego, jakim jest urządzenie mobilne. Kod piszemy co prawda w Javie przy wykorzystaniu języka znaczników XML, jednakże stale na uwadze powinniśmy mieć specyficzne ograniczenia, jak i nowe możliwości, które charakteryzują przenośne urządzenia.
Do tej pierwszej grupy zalicza się przede wszystkim niewielki ekran, wykluczający obecność długich tekstów czy skomplikowanych elementów nawigacji. W przypadku Androida za standardową rozdzielczość przyjmuje się 320 x 480 pikseli, przy czym użytkownik może w bardzo łatwy sposób (obracając telefon) zmienić orientację i wówczas dostępna wysokość ekranu to 320 pikseli. Koniecznie trzeba pamiętać o tych zmiennych warunkach i testować aplikacje w orientacjach poziomej (ang. horizontal, landscape) oraz pionowej (ang. vertical, portrait).
10 mln dolarów dla twórców aplikacji
Google wie, jak skutecznie zachęcić programistów do zapoznania się z możliwościami tworzenia programów do nowego systemu operacyjnego. Wraz z publiczną premierą testowego SDK, gigant z Doliny Krzemowej ogłosił konkurs \”Android Developer Challenge\”, przeznaczając na nagrody dla autorów najciekawszych aplikacji łączną pulę 10 milionów dolarów.
Konkurs składał się z dwóch etapów. Na pierwszy przysłano prawie 2000 zgłoszeń, z których jury wybrało 50 najciekawszych i przeznaczyło na każdy z nich po 25 tysięcy dolarów na dalszy rozwój. Spośród finalistów jury wybrało następnie kolejne 20 najlepszych aplikacji. Twórcy pierwszych dziesięciu otrzymało nagrody w wysokości 275 tysięcy dolarów, a kolejnych dziesięciu – po 100 tysięcy. Lista wszystkich finałowych aplikacji dostępna jest pod adresem: http://code.google.com/android/adc_gallery.
Każdy z projektów zawiera prezentację zrzutów ekranu oraz opis funkcjonalności, co stanowi doskonałą lekturę na temat możliwości aplikacji mobilnych, jak i całej platformy Android .
Nazwy angielskie w nawiasach podawane są celowo, gdyż występują one w środowisku programistycznym, w którym możemy wybrać orientację, w jakiej uruchomiony ma zostać emulator. Ponadto zalecamy tworzenie całych aplikacji właśnie w języku angielskim – \”Google Market\” rozwiązuje problemy z dystrybucją programów, dlaczego więc nie spróbować naszymi rozwiązaniami zdobyć rynku globalnego? Zwłaszcza, że wciąż nie wiadomo kiedy Android pojawi się w Polsce.
Poza kwestiami lokalizacyjnymi oraz wielkością ekranu, warto mieć na uwadze inny sposób interakcji z użytkownikiem, niż ma to miejsce w standardowych programach – w urządzeniach mobilnych z Androidem dostępny jest ekran dotykowy, brakuje natomiast pełnowymiarowej klawiatury, przez co wprowadzanie tekstu nigdy nie będzie tak wygodne, jak w przypadku zwykłego komputera. Zapomnijmy więc o projektowaniu skomplikowanych formularzy i zmuszania użytkownika do podawania zbyt dużej ilości danych – ograniczmy to do niezbędnego minimum.
O sukcesie aplikacji mobilnej – poza solidnym wykonaniem, co jest oczywiste – decyduje przede wszystkim dobry pomysł na wykorzystanie możliwości, jakie niosą ze sobą przenośne urządzenia. Mowa tutaj o dostępie do odbiornika GPS, listy kontaktów, telefonu (rozmów głosowych), wiadomości SMS/MMS, aparatu fotograficznego/kamery czy czujników – akcelerometru czy np. termometru (Android daje taką możliwość).
Przykładowa aplikacja może np. za pomocą GPS sprawdzić najbliższych znajomych w okolicy (mając ich adresy na liście kontaktów), a następnie wysłać do nich MMS-a z właśnie wykonanym zdjęciem i informacją o miejscu spotkania. Możliwości tak naprawdę są nieograniczone – użytkownicy mają prawie zawsze przy sobie urządzenie, które przecież dodatkowo posiada dostęp do internetu (Wi-Fi, a także GPRS, EDGE czy UMTS).
Przygotowanie środowiska programistycznego
Prace nad aplikacją należy rozpocząć od instalacji i konfiguracji środowiska programistycznego. Do tego celu niezbędne jest pobranie ze stron Google Android (http://code.google.com/android/download_list.html) SDK, będącego obecnie w wersji 0.9 beta i dostępnego dla systemów operacyjnych Windows (XP oraz Vista), Linux (powinno działać w większości dystrybucji, testowane na Ubuntu) oraz Mac OS X. Android SDK zawiera zestaw niezbędnych narzędzi (m.in. emulator) oraz API w języku Java, niezbędnych w procesie tworzenia aplikacji.
Wspierane i zalecane przez Google środowisko programistyczne to Eclipse (wersje Europa oraz nowsza – Ganymade), do którego twórcy przygotowali plugin ADT (Android Development Tools). Nie jest to konfiguracja niezbędna do tworzenia aplikacji pod Androida, ale z pewnością najwygodniejsza. ADT zajmuje się automatycznie m.in. budowaniem aplikacji (kompilacją) czy też uruchamianiem i sterowaniem emulatora, zaś Eclipse zapewnia takie podstawowe funkcjonalności, jak kolorowanie składni czy podpowiadanie kodu.
Instalacja SDK oraz ADT jest niezwykle prosta. W przypadku tego pierwszego wystarczy pobrać archiwum .zip na dysk i rozpakować je do dowolnego katalogu. Plugin ADT instalowany jest z poziomu Eclipse w sposób analogiczny do innych tego typu rozszerzeń. Po wybraniu z menu opcji \”Software updates\” wystarczy dodać nowe zdalne źródło o adresie {stala}https://dl-ssl.google.com/android/eclipse/{/stala} i postępować zgodnie z poleceniami graficznego instalatora.
Po zakończeniu całej operacji i restarcie Eclipse\’a należy przejść do konfiguracji środowiska i w opcji \”Android\” wskazać lokalizację katalogu z rozpakowanym SDK. Po tej operacji można już utworzyć nowy projekt aplikacji.
Założenia projektu
Zanim jednak przystąpimy do tego etapu, warto odpowiednio zaplanować aplikację oraz poznać teoretyczne podstawy jej tworzenia. Na potrzeby artykułu stworzony zostanie od podstaw prosty program, realizujący funkcjonalność planowania listy zakupów.
Cała aplikacja będzie składała się z dwóch ekranów: listy produktów do kupienia oraz formularza dodawania nowych towarów. W czasie zakupów użytkownik, klikając na dany element, będzie go usuwał z listy, tak aby wyeliminować z niej produkty, które włożył już do swojego koszyka.
W Androidzie wyróżniamy cztery główne bloki, z których składa się aplikacja. Pierwszym z nich jest tzw. \”Activity\” (aktywność), będąca odpowiednikiem pojedynczego ekranu aplikacji. W przypadku programu tworzonego na potrzeby artykułu (\”Shopping list\” – lista zakupów) konieczne będzie stworzenie dwóch ekranów, zgodnie z początkowymi założeniami.
Kolejną charakterystyczną strukturą jest tzw. \”Intent\” (intencja), która odpowiada za przejścia pomiędzy ekranami. Chcąc z jednej aktywności przejść do innej, korzystamy właśnie z intencji.
Pozostałe bloki aplikacji to \”Content provider\” (dostawca danych, np. baza danych czy źródło sieciowe) oraz \”Service\” (usługa działająca w tle). Ta ostatnia w naszej aplikacji nie będzie potrzebna, z kolei jako \”Content provider\” posłuży nam baza danych SQLite, standardowo obsługiwana przez Androida.
System operacyjny od Google posiada również kilka charakterystycznych elementów, z którymi programista powinien zapoznać się przed rozpoczęciem pracy. W osobnych ramkach została omówiona struktura katalogów projektu, zasady projektowania interfejsu graficznego (layoutów) oraz cykl życia aplikacji, pozwalający zrozumieć zachowanie się poszczególnych \”Activities\”.
Cykl życia aktywności
Tworząc nową aktywność (Activity) wywoływana jest metoda {stala}onCreate(){/stala}, a następnie {stala}onStart(){/stala} i {stala}onResume(){/stala}. Dziedzicząc po klasie Activity, nie jest konieczne implementowanie każdej z tych metod. Najczęściej nadpisuje się metodę {stala}onCreate(){/stala}, zawierając w niej kod, który jest uruchamiany podczas uruchomienia aktywności.
W chwili gdy użytkownik uruchomi nową aktywność czy wręcz aplikację, poprzednia jest wstrzymywana, co objawia się wywołaniem metod {stala}onPause(){/stala} oraz {stala}onStop(){/stala}. W przypadku gdy użytkownik powróci do ekranu, system wywołuje metodę {stala}onRestart(){/stala}, a gdy aplikacja jest zamykana – {stala}onDestroy(){/stala}.
W przypadku tworzenia rozbudowanego systemu z dużą liczbą klas dziedziczących po Activity, należy mieć na uwadze cykl ich życia. W przeciwnym wypadku łatwo można pogubić się w całym procesie, co może skutkować np. niemożnością zamknięcia całej aplikacji przez użytkownika.
Tworzymy nowy projekt
Po poprawnej konfiguracji środowiska programistycznego oraz wstępie teoretycznym, czas w końcu rozpocząć tworzenie nowego projektu. W tym celu w Eclipse wybieramy \”New project\”, a następnie odszukujemy na liście \”Android project\”. W kolejnym kroku kreatora zostaniemy poproszeni o podanie: nazwy projektu (Shopping List), nazwy paczki (przyjęta taka sama konwencja nazewnicza jak standardowo w Javie – nazwa domenowa, pisana \”od końca\”, w naszym przypadku: pl.internetmaker.shopping), domyślnej Activity oraz nazwy aplikacji (w przeciwieństwie do nazwy projektu, można tutaj użyć dłuższej nazwy, zawierającej także spacje). Po zakończeniu pracy kreatora stworzona zostanie odpowiednia dla Androida struktura katalogów oraz domyślne pliki.
Warto na początku stworzyć także konfigurację uruchomienia, tak aby móc potem za jednym kliknięciem wystartować emulator wraz z otwartą projektowaną aplikacją. W tym celu klikamy na \”Run\” i wybieramy \”Open run dialog\”. W nowym okienku odnajdujemy element \”Android application\” i dwukrotnie na nim klikamy. Zostanie stworzona nowa konfiguracja, której możemy nadać własną nazwę, a także wybrać projekt (ShoppingList) i domyślną Activity. Korzystając z zakładki \”Target\” możemy także wybrać m.in. rozmiar ekranu i jego orientację.
Projektujemy klasy
Na potrzeby projektu będziemy musieli utworzyć trzy klasy:
- ShoppingList – główna klasa, uruchamiana domyślnie po starcie aplikacji. Jej zadaniem będzie wyświetlanie listy rzeczy do kupienia. W związku z tym klasa będzie rozszerzać ListActivity (specjalną aktywność, zaprojektowaną do wyświetlania list elementów),
- ShoppingItem – odpowiedzialna za aktywność (będzie rozszerzać klasę Activity) wyświetlającą formularz dodania nowego towaru do listy,
- ShoppingListDbAdapter – klasa odpowiedzialna za obsługę bazy danych (połączenie, stworzenie bazy) oraz podstawowe operacje na niej (pobranie wszystkich rekordów, a także dodawanie nowych i usuwanie istniejących).
Interfejs graficzny zostanie stworzony w oparciu o pliki XML. W tym celu utworzone zostaną trzy layouty:
- main.xml – layout określający miejsce występowania listy elementów oraz – w przypadku pustej listy – komunikatu o braku rzeczy do kupienia,
- list_item.xml – obiekt odpowiadający pojedynczemu elementowi na liście zakupów, wyświetlający go w jednym polu tekstowym,
- new_item.xml – wyświetlenie formularza zawierającego: etykietę, pole tekstowe oraz przycisk.
Struktura katalogów
Każdy projekt Androida charakteryzuje się ustaloną strukturą katalogów:
- src – zawiera kod źródłowy (source) klas
- res – zasoby (resources) aplikacji
- drawable – pliki graficzne
- layout – layouty (pliki.xml)
- values
- arrays.xml – wartości tablic
- colors.xml – kolory
- dimens.xml – rozmiary
- strings.xml – ciągi tekstowe (np. komunikaty, etykiety)
- styles.xml – style
- AndroidManifest.xml – lista komponentów aplikacji i klas je implementujących
Kodujemy – komunikacja z bazą danych
Prace rozpoczniemy od przygotowania klasy odpowiedzialnej za obsługę bazy danych SQLite. Tworzymy więc klasę {stala}ShoppingListDbAdapter{/stala}:
public class ShoppingListDbAdapter
{
public static final String KEY_ROWID = \"_id\";
public static final String KEY_NAME = \"name\";
private static final String TAG = \"ShoppingListDbAdapter\";
private DatabaseHelper dbHelper;
private SQLiteDatabase SQLDb;
Na starcie definiujemy kilka podstawowych własności: statyczne zmienne KEY_ROWID oraz KEY_NAME zawierają nazwy kolumn w tabeli (przechowujemy unikalny identyfikator rekordu oraz nazwę elementu do kupienia). Dobrą praktyką jest definiowanie zmiennej TAG (która przechowuje zazwyczaj nazwę klasy), wykorzystywanej w logowaniu zdarzeń i przy debugowaniu aplikacji.
Następnie przygotowujemy zmienną typu {stala}DatabaseHelper{/stala}, która posłuży do otwarcia/zamknięcia połączenia z bazą danych, przechowywaną następnie w zmiennej SQLDb.
To nie koniec własności klasy:
private static final String DATABASE_CREATE = \"CREATE TABLE shopping_list (_id integer primary key autoincrement, name text not null);\";
private static final String DATABASE_NAME = \"data\";
private static final String DATABASE_TABLE = \"shopping_list\";
private static final int DATABASE_VERSION = 2;
private final Context ctx;
Definiujemy kolejno: zapytanie SQL tworzące odpowiednią tabelę, nazwę bazy danych (która będzie odpowiadała nazwie stworzonego pliku z danymi) oraz nazwę tabeli i wersję bazy. Zmienna ctx posłuży na przechowanie kontekstu, niezbędnego do dostępu do bazy danych przez obiekt.
Kolejnym krokiem jest stworzenie klasy rozszerzającej {stala}SQLiteOpenHelper{/stala}, odpowiedzialnej za stworzenie nowej bazy danych oraz ew. jej aktualizację w przyszłości do nowej wersji. Spójrzmy na kod:
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate (SQLiteDatabase db)
{
db.execSQL(DATABASE_CREATE);
}
@Override
public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w(TAG, \"Upgrading database\");
db.execSQL(\"DROP TABLE IF EXISTS shopping_list\");
onCreate(db);
}
}
Wewnątrz metody {stala}onCreate(){/stala} wykonywane jest wcześniej zdefiniowane zapytanie tworzące tabelę, z kolei {stala}onUpgrade(){/stala} zawiera polecenia odpowiedzialne za skasowanie istniejącej tabeli i ponowne wywołanie {stala}onCreate(){/stala}.
Gdy użytkownik otworzy aplikację po raz drugi, żadna z tych metod nie zostanie wywołana – wykona się jedynie kod wewnątrz konstruktora, odpowiedzialny za przekazanie konstruktorowi klasy nadrzędnej informacji dotyczących nazwy oraz wersji bazy.
Tworzenie interfejsu graficznego
Interfejs aplikacji pisanych dla systemu Android składa się z layoutów, zawierających szereg elementów tzw. widoków. Dostępnych jest kilka rodzajów layoutów:
- LinearLayout – layout liniowy, w którym elementy wyświetlane są jeden pod drugim lub obok drugiego, w zależności od orientacji (pionowa/pozioma),
- RelativeLayout – rozmieszczenie elementów następuje względem siebie – poszczególne widoki mogą być wyświetlane np. po prawej stronie czy nad innym elementem,
- FrameLayout – najprostszy layout, zawierający tylko jeden element,
- TableLayout – widok tabelaryczny,
- AbsoluteLayout – layout, w którym poszczególne elementy są rozmieszczone względem lewego górnego punktu na ekranie.
Najczęściej stosowane są dwa pierwsze rozwiązania, przy czym należy pamiętać, że poszczególne layouty można zagnieżdżać (czyli np. {stala}LinearLayout{/stala} może zawierać w sobie kilka {stala}RelativeLayout{/stala}). Wśród widoków do najczęściej stosowanych elementów należą:
- TextView – prosta kontrolka do wyświetlania tekstu,
- EditText – pole tekstowe do wprowadzania tekstu przez użytkownika,
- ListView – wyświetlanie list elementów,
- Button – przyciski.
Każdy z tych elementów dziedziczy po klasie {stala}View{/stala} i posiada ogólne właściwości, takie jak szerokość, wysokość, id, pozycję etc.
Layouty, jak i poszczególne kontrolki, można tworzyć zarówno z poziomu kodu Java jak i XML.
Następnym zadaniem jest stworzenie metod odpowiedzialnych za otwarcie i zamknięcie połączenia z bazą:
public ShoppingListDbAdapter open() throws SQLException
{
dbHelper = new DatabaseHelper(ctx);
SQLDb = dbHelper.getWritableDatabase();
return this;
}
public void close()
{
dbHelper.close();
}
Do obydwu tych zadań wykorzystywany jest obiekt klasy {stala}DatabaseHelper{/stala}. Metoda {stala}getWritableDatabase(){/stala} otwiera bazę danych z prawami do zapisu, z kolei {stala}close(){/stala} – zamyka połączenie.
Kiedy gotowy jest kod odpowiedzialny za stworzenie i aktualizację bazy oraz otwarcie/zamknięcie połączenia z nią, czas na zaprogramowanie operacji na tabeli:
public long createItem(String name)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name);
return SQLDb.insert(DATABASE_TABLE, null, initialValues);
}
public boolean deleteItem(long rowId)
{
return SQLDb.delete(DATABASE_TABLE, KEY_ROWID + \"=\" + rowId, null) > 0;
}
public Cursor fetchAllItems()
{
return SQLDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME}, null, null, null, null, null);
}
Te trzy metody wystarczą na potrzeby całej aplikacji: {stala}createItem(){/stala} utworzy nowy rekord w bazie, {stala}deleteItem(){/stala} usunie już istniejący o podanym ID, a {stala}fetchAllItems(){/stala} pobierze wszystkie rekordy na potrzeby głównego okna aplikacji.
Jak widać nie ma konieczności operowania bezpośrednio na języku SQL. Zamiast tego można skorzystać z \”budowniczych\” zapytań, które kryją się pod metodami obiektu SQLDb ({stala}insert(){/stala}, {stala}delete(){/stala} oraz {stala}query(){/stala}).
Kodujemy – dodawanie nowego elementu do listy zakupów
Czas teraz na przygotowanie odpowiednich Activities. Logika nakazuje rozpocząć od głównego ekranu aplikacji, jednakże w naszym przypadku zaczniemy od projektu klasy i layoutu odpowiedzialnych za dodawanie nowych elementów do listy zakupów. Kod będzie prostszy i krótszy, więc bardziej nadaje się na rozpoczęcie przygody z Androidem.
Rozpocznijmy od interfejsu graficznego, plik new_item.xml:
Zgodnie ze wcześniejszymi ustaleniami, kod odpowiedzialny za wygląd aplikacji przenosimy do osobnych plików XML. Do stworzenia najprostszego formularza wykorzystujemy layout liniowy z orientacją pionową i trzema następującymi po sobie elementami: etykietą, polem edycyjnym oraz przyciskiem do wysyłki formularza.
Każdy element posiada standardowe atrybuty {stala}layout_width{/stala} oraz {stala}layout_height{/stala}, określające odpowiednio szerokość i wysokość elementu: {stala}fill_parent{/stala} oznacza wypełnienie obiektu \”rodzica\”, z kolei {stala}wrap_content{/stala} ustala taką szerokość, która jest niezbędna do wyświetlania całego elementu.
Dodatkowo polu edycyjnemu oraz przyciskowi nadajemy unikalny identyfikator, dzięki któremu z poziomu kodu będziemy mogli odczytać zawartość pola jak i sprawdzić czy przycisk został naciśnięty. Warto również zwrócić uwagę na wartości podane w parametrach {stala}text{/stala} – są to odwołania do danych podanych w pliku strings.xml, znajdującego się w katalogu values. Domyślnie plik ten nie zawiera tych wartości, dlatego należy go otworzyć w edytorze i dopisać dwie linie pomiędzy wewnątrz tagu {html}
Add item
I have to buy:
Pierwszy element odpowiada za treść wyświetlaną na przycisku, a drugi – nad polem tekstowym. Przeniesienie tego typu komunikatów do osobnego pliku pozwala na ich łatwą edycję w przyszłości, a także ułatwia ew. lokalizację – tłumaczenie na inne języki.
Czas teraz na stworzenie właściwej aktywności, która wyświetli stworzony layout oraz obsłuży dodawanie nowych elementów do bazy:
public class ShoppingItem extends Activity
{
private EditText Name;
private Button AddButton;
private ShoppingListDbAdapter db;
Definiujemy nową klasę, która rozszerza Activity, co jest niezbędne do stworzenia nowej aktywności i powiązane jest z cyklem życia całej aplikacji w Androidzie. Wewnątrz klasy definiujemy trzy własności. Dwie pierwsze odpowiadają elementom stworzonym w layoucie, a kolejna będzie przechowywać obiekt wcześniej napisanej klasie odpowiedzialnej za komunikację z bazą danych.
Następnie nadpisujemy metodę {stala}onCreate(){/stala} klasy Activity, która zostanie wywołana w momencie utworzenia aktywności:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.new_item);
db = new ShoppingListDbAdapter(this);
db.open();
Name = (EditText) findViewById(R.id.new_name);
AddButton = (Button) findViewById(R.id.add_item);
AddButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
db.createItem(Name.getText().toString());
finish();
}
});
}
Na początku wywołujemy konstruktor metody nadrzędnej, co jest obowiązkowe. Kolejny krok to wywołanie metody {stala}setContentView{/stala}, która określa layout używany przez Activity. Wskazujemy na plik new_item, który jest layoutem – konwencja nazewnicza w Javie ({stala}R.layout.new_item{/stala}) odpowiada strukturze katalogów (res/layout/new_item.xml). W ten sposób możemy odwoływać się do każdego zasobu istniejącego w projekcie, przykładowo dla pliku graficznego test.png w katalogu – w Javie dostaniemy się poprzez R.drawable.test. Jest to rozwiązanie bardzo szybkie i wygodne, a wbudowane w Eclipse podpowiadanie składni szybko pozwoli nam wybrać odpowiedni plik.
W dalszej części tworzymy instancję klasy {stala}ShoppingListDbAdapter{/stala} i za pomocą metody open() otwieramy połączenie z bazą. Następnie do zmiennych {stala}Name{/stala} oraz {stala}AddButton{/stala} przypisujemy elementy layoutów, którym nadaliśmy wcześniej unikalny identyfikator – dostęp jest możliwy poprzez {stala}R.id.nadane_id_elementu{/stala}.
Ostatnia część kodu to dodanie listenera do przycisku – po jego naciśnięciu zostanie wywołana metoda {stala}onClick(){/stala}, która z kolei wykona metody {stala}createItem(){/stala} oraz {stala}finish(){/stala}. Pierwsza z nich odpowiedzialna jest za dodanie nowego rekordu do bazy (jako parametr podajemy to co użytkownik wpisał w polu tekstowym), a druga – za zakończenie obecnej Activity (zniknięcie formularza).
Ponieważ {stala}ShoppingItem{/stala} nie jest domyślną Activity, należy ją jeszcze dopisać do AndroidManifest.xml, gdzie musi znaleźć się każda aktywność. W tym celu do tegoż pliku dopisujemy jedną linię, którą umieszczamy wewnątrz {html}
Jest to krok konieczny, o czym niestety często podczas tworzenia aplikacji się zapomina – na ziemię sprowadza nas wówczas komunikat podczas próby odpalania Activity z informacją, iż takowa nie istnieje.
Kodujemy – wyświetlanie listy zakupów
Do przygotowania pozostała jeszcze główna klasa aplikacji, której głównym zadaniem będzie wyświetlenie listy elementów do zakupu. {stala}ShoppingList{/stala} będzie też odpowiedzialne za usuwanie elementów po kliknięciu, a także za stworzenie sposobu przejścia do formularza dodawania nowych. Rozpoczynamy od przygotowania layoutów:
Stworzony layout liniowy zawiera dwa elementy: listę oraz tekst wyświetlony w momencie, kiedy lista nie ma elementów. Na uwagę zasługują specjalne nazwy identyfikatorów tych elementów, które posiadają przedrostek \”android:\”. W przypadku wyświetlania list elementów w layoucie musimy stworzyć właśnie dwa tego typu elementy o dokładnie takich nazwach – jest to element wymagany przez Androida.
Powyższy kod zapisujemy pod nazwą main.xml, jednocześnie tworząc nowy plik list_item. xml, odpowiedzialny za layout dla pojedynczego elementu listy:
Element na liście zawiera jedynie wpisaną przez użytkownika wartość (np. \”pieczywo\”), stąd wystarczy dodanie jednego {stala}TextView{/stala}. W porównaniu do poprzednich tego typu elementów, dodajemy dwa parametry odpowiedzialne za pozycję tekstu (gravity), który będzie wycentrowany oraz za jego rozmiar (18 pikseli).
W main.xml użyliśmy odwołania do @string/noitems, zatem należy do strings.xml dopisać kolejną linię:
Shopping List is empty. Add some items using menu.
Gdy interfejs jest przygotowany, czas na kod Java i klasę ShoppingList:
public class ShoppingList extends ListActivity
{
private static final int INSERT_ID = Menu.FIRST;
private ShoppingListDbAdapter db;
Na uwagę zasługuje fakt, że tym razem klasa dziedziczy po {stala}ListActivity{/stala}. Jest to specjalna aktywność przygotowana na potrzeby wyświetlania list elementów.
Przygotowujemy stałą INSERT_ID, która będzie wykorzystywana przy tworzeniu menu aplikacji, w którym umieścimy opcję przejścia do formularza dodawania. Zmienna db przechowywać będzie instancję klasy {stala}ShoppingListDbAdapter{/stala}, odpowiedzialną za operacje na bazie danych.
Kolejny krok to zaprogramowanie metody wywoływanej przy tworzeniu aktywności:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
db = new ShoppingListDbAdapter(this);
db.open();
fillData();
}
Powyższa konstrukcja już powinna być znana z klasy {stala}ShoppingItem{/stala}. Nowością jest jedynie metoda {stala}fillData(){/stala}, która będzie odpowiedzialna za pobranie rekordów z bazy i wyświetlenie ich na liście:
private void fillData()
{
Cursor itemsCursor = db.fetchAllItems();
startManagingCursor(itemsCursor);
String[] from = new String[] {ShoppingListDbAdapter.KEY_NAME};
int[] to = new int[]{R.id.name};
SimpleCursorAdapter items = new SimpleCursorAdapter(this,R.layout.list_item, itemsCursor, from, to);
setListAdapter(items);
}
Wykorzystujemy napisaną wcześniej metodę {stala}fetchAllItems(){/stala}, która pobiera wszystkie rekordy, a następnie przypisujemy je do nowo stworzonego kursora.
Kolejny krok to przygotowanie tablic, zawierających odpowiednio kolumny z bazy danych oraz elementy layoutu, do których wartości z kolumn zostaną przypisane. Następnie tworzymy nowy {stala}SimpleCustomAdapter{/stala}, do którego przekazujemy przygotowane przed chwilą wartości oraz wskazujemy na layout (list_item.xml), który ma zostać użyty.
Czas teraz na przygotowanie menu (aktywowane klawiszem \”menu\” urządzenia), w którym znajdzie się jedna opcja – odsyłająca do Activity z formularzem:
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.add_item);
return true;
}
W tym celu nadpisujemy metodę {stala}onCreateOptionsMenu{/stala}, w której korzystamy z funkcji {stala}add(){/stala} klasy Menu. Jako ostatni parametr podajemy wyświetlany tekst, który pobieramy z pliku strings.xml.
Kolejna metoda odpowiedzialna jest za obsługę kliknięcia w opcje menu:
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item)
{
switch(item.getItemId())
{
case INSERT_ID:
createItem();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
W przypadku wybrania przez użytkownika opcji dodania nowego elementu do listy zakupów, wywoływana jest metoda {stala}createItem(){/stala}:
private void createItem() {
Intent i = new Intent(this, ShoppingItem.class);
startActivity(i);
}
Wewnątrz mamy standardowy przykład na sposób przejścia do innej Activity. Najpierw tworzymy nową instancję klasy {stala}Intent{/stala}, podając jako parametry kontekst wywołania oraz nazwę klasy zawierającą docelową aktywność, a następnie przekazujemy ją jako parametr metody {stala}startActivity(){/stala}.
Mając zapewnioną obsługę wyświetlania listy zakupów oraz przejście do formularza, czas zająć się usuwaniem już zakupionych towarów:
@Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
super.onListItemClick(l, v, position, id);
db.deleteItem(id);
fillData();
}
W tym celu nadpisujemy metodę {stala}onListItemClick(){/stala}, wywoływaną po kliknięciu na dowolny element listy. Gdy takie zdarzenie będzie miało miejsce, za pomocą {stala}deleteItem(id){/stala} kasujemy wybrany rekord z bazy danych, a następnie odświeżamy listę używając {stala}fillData(){/stala}.
Klasę {stala}ShoppingList{/stala} kończy nadpisanie metody {stala}onResume(){/stala}:
@Override
protected void onResume() {
super.onResume();
fillData();
}
Funkcja ta wywoływana jest w momencie ponownego ukazania się na ekranie głównej aktywności. W przypadku tworzonej aplikacji taka sytuacja ma miejsce w momencie wypełnienia przez użytkownika formularza dodawania nowego towaru do zakupu – wywoływana była wówczas funkcja {stala}finish(){/stala}, kończąca aktywność {stala}ShoppingItem{/stala}. W tym przypadku w {stala}onResume(){/stala} klasy głównej wywołujemy {stala}fillData(){/stala}, która ma na celu odświeżenie listy, aby wyświetlić na niej nowo dodany element.
Aktywności {stala}ShoppingItem{/stala}, z racji tego że jest ona domyślną – nie musimy dopisywać do AndroidManifest.xml. Została ona tam dopisana automatycznie w momencie utworzenia projektu.
Podsumowanie
Pisanie pod Androida ma dwa oblicza. Podstawowe operacje (wyświetlanie danych, zapytania do bazy danych) są stosunkowo proste i mocno intuicyjne, przez co nie powinny sprawić problemu nawet początkującemu programiście Java. Jednak często z pozoru błahe sprawy (zwłaszcza dotyczące dostosowania elementów graficznych) potrafią skutecznie zastopować tworzenie kodu sprawiając, że deweloper stoi w miejscu.
Wynika to z faktu, że SDK Androida wciąż jest w fazie beta, a sam projekt nie skupił wokół siebie jeszcze tak dużej społeczności, która jest w stanie pomóc przy każdym z problemów. Stąd początki z Androidem mogą czasami sprawiać trudności. Warto jednak próbować je rozwiązać, aby być w pełni przygotowanym na premierę wersji finalnej, a także pierwszych urządzeń z systemem Google.
Android w sam sobie z pewnością jest wart uwagi, a gdy zostanie w pełni dopracowany oraz skupi wokół siebie dużą społeczność – tworzenie aplikacji będzie szybkie i intuicyjne, a perspektywy sukcesu – bardzo duże.
Zobacz w sieci
- http://code.google.com/android – Oficjalna strona Androida
- http://android-developers.blogspot.com – Oficjalny blog twórców Androida
- http://www.anddev.org – Pomocne forum dyskusyjne dla programistów Androida
- http://www.eclipse.org – Środowisko programistyczne Eclipse