Académique Documents
Professionnel Documents
Culture Documents
Przedmowa ............................................................................................. 21
O autorach .............................................................................................. 23
Informacje o redaktorze technicznym ................................................. 25
Podzikowania ....................................................................................... 27
Sowo wstpne ....................................................................................... 29
Rozdzia 1.
Rozdzia 2.
Spis treci
Intencja ............................................................................................................ 59
Dostawca treci ............................................................................................... 59
Usuga .............................................................................................................. 59
AndroidManifest.xml .................................................................................... 60
Urzdzenia AVD ............................................................................................ 60
Witaj, wiecie! ....................................................................................................... 60
Wirtualne urzdzenia AVD ................................................................................ 65
Poznanie struktury aplikacji Androida ............................................................. 67
Analiza aplikacji Notepad .................................................................................... 69
Wczytanie oraz uruchomienie aplikacji Notepad ...................................... 69
Rozoenie kodu na czynniki pierwsze ........................................................ 71
Badanie cyklu ycia aplikacji ............................................................................... 78
Usuwanie bdw w aplikacji .............................................................................. 81
Uruchamianie emulatora .............................................................................. 83
StrictMode ....................................................................................................... 84
Odnoniki ........................................................................................................ 89
Podsumowanie ...................................................................................................... 89
Rozdzia 3.
Spis treci
Rozdzia 5.
Rozdzia 6.
Spis treci
Rozdzia 8.
Spis treci
10
Spis treci
Spis treci
11
12
Spis treci
Spis treci
13
14
Spis treci
Spis treci
15
16
Spis treci
Spis treci
17
18
Spis treci
Spis treci
19
20
Spis treci
Przedmowa
22
Sowo wstpne
23
O autorach
24
Sowo wstpne
25
Informacje o redaktorze
technicznym
26
Sowo wstpne
27
Podzikowania
Napisanie tej ksiki wymagao wielkiego wysiku nie tylko z naszej autorw strony, lecz
rwnie od czci bardzo utalentowanego zespou wydawnictwa Apress, a take ze strony redaktora technicznego. Chcielimy zatem podzikowa Steveowi Anglinowi, Matthew Moodiemu,
Corbin Collins, Heather Lang, Tracy Brown, Mary Behr oraz Brigid Duffy z wydawnictwa Apress.
Chcielimy take wyrazi nasze uznanie dla redaktora technicznego, Dylana Phillipsa, za prac,
jak woy w t ksik. Jego komentarze oraz poprawki byy bezcenne. W trakcie poszukiwa
odpowiedzi na forum programistycznym Androida czsto pomoc suyli nam Dianne
Hackborn, Nick Pelly, Brad Fitzpatrick oraz inni czonkowie Android Team. Byli gotowi do
pomocy o kadej porze dnia oraz w weekendy, za co chcemy im wszystkim powiedzie: Dzikujemy!. To zdecydowanie oni s najciej pracujcym zespoem w wiecie urzdze mobilnych.
Spoeczno uytkownikw systemu Android jest bardzo aktywna i rozbudowana. Nieraz ludzie
ci suyli pomoc w znajdowaniu odpowiedzi na trudne pytania, udzielali take poytecznych
rad. Mamy nadziej, e ta ksika w jaki sposb przyda si caej tej spoecznoci.
Jestemy take gboko wdziczni naszym rodzinom za wyrozumiao podczas przeduajcego si pisania ksiki.
28
Sowo wstpne
29
Sowo wstpne
Czy kiedykolwiek chciae by Rodinem1? Siedzie z dutem w doni i ciosa ska, formujc
j na ksztat wasnej wizji? Wikszo programistw dcych najpopularniejszymi nurtami
trzymao si z daleka od mocno ograniczonych urzdze mobilnych ze strachu przed niemonoci wyrzebienia uytecznej aplikacji. Ale te czasy ju miny.
System Android pozwala nam na prac z nieprawdopodobn liczb programowalnych
urzdze. W tej ksice pragniemy potwierdzi podejrzenia, jakoby system Android znakomicie nadawa si do pisania aplikacji. Jeli programujesz w Javie, zyskujesz olbrzymi
szans na korzyci pynce z tej ekscytujcej, penej moliwoci, wielozadaniowej platformy
obliczeniowej. Cieszymy si z Androida, poniewa stanowi on zaawansowan platform,
wprowadzajc wiele nowych paradygmatw w kwestii projektowania szkieletw (pomimo
ogranicze urzdze mobilnych).
Jest to nasza trzecia, dotychczas najlepsza, edycja ksiki powiconej Androidowi. Pro Android 3
jest rozlegym przewodnikiem programistycznym. W tym wydaniu udoskonalilimy, przepracowalimy stylistycznie oraz poprawilimy wszystkie elementy ksiki Android 2. Tworzenie
aplikacji w celu stworzenia gruntownie uaktualnionego przewodnika, sucego zarwno pocztkujcym, jak i zaawansowanym programistom bdcego wynikiem trzech lat pracy. Omawiamy
ponad 100 zagadnie podzielonych na 31 rozdziaw. Niniejsze wydanie ksiki obejmuje
wersje 2.3 oraz 3.0 systemu Android, ktre s zoptymalizowanymi wersjami dla, odpowiednio,
telefonw i tabletw.
W tym wydaniu powicilimy wicej uwagi wewntrznym mechanizmom Androida poprzez
omwienie wtkw, procesw, dugoterminowych usug, odbiorcw komunikatw oraz menederw alarmw. Omawiamy rwnie o wiele wicej kontrolek interfejsu uytkownika. Zamiecilimy ponad 150 stron powiconych wersji 3.0 systemu, gdzie omwilimy fragmenty, dialogi
fragmentw, klas ActionBar, a take funkcj przecigania. Znacznie rozwinlimy rozdziay
powicone czujnikom i usugom. Rozdzia omawiajcy rodowisko OpenGL zosta zaktualizowany pod ktem obsugi wersji OpenGL ES 2.0.
Zasadniczymi elementami tej ksiki s objanienia poj, listingi oraz samouczki. Wszystkie
rozdziay zostay podporzdkowane tej filozofii. Kady samodzielny samouczek zosta opatrzony
komentarzem eksperta. Wszystkie projekty zawarte w ksice s dostpne do pobrania i mona
je bez trudu zaimportowa do rodowiska Eclipse. Ciko rwnie pracowalimy nad tym, aby
kady pokazany tu kod mg zosta bezproblemowo skompilowany. Lista plikw skadajcych
August Franois-Ren Rodin francuski rzebiarz, ur. 12 listopada 1840 r. w Paryu, zm. 17 listopada
1917 r. w Meudon. W swoich pracach czy elementy symbolizmu i impresjonizmu. By prekursorem
nowoczesnego rzebiarstwa przyp. red.
30
si na kady projekt w danym rozdziale zostaa jasno przedstawiona, dziki czemu wasnorczne
utworzenie projektu staje si jeszcze prostsze.
Tematyka ksiki obejmuje takie kluczowe pojcia, jak zasoby, intencje, dostawcy treci, procesy,
wtki, kontrolki interfejsu uytkownika, odbiorcy wiadomoci, usugi oraz usugi dugoterminowe. Osoby dopiero poznajce rodowisko OpenGL znajd tu mnstwo materiaw dotyczcych wersji OpenGL ES 1.0 oraz 2.0. Wiele miejsca powicilimy funkcjom przetwarzania tekstu
na mow, czujnikom oraz wielodotykowoci. Szeroko rwnie omwilimy zagadnienia dotyczce wersji 3.0 Androida, wrd ktrych mona znale informacje o fragmentach, dialogach
fragmentw, klasie ActionBar i funkcji przecigania.
Na koniec warto wspomnie, e wyszlimy w tej ksice poza podstawowe zagadnienia, e na
kady temat zadawalimy trudne pytania oraz e udokumentowalimy wyniki (mnogo tematw
zawartych w tej ksice wida w szczegowym spisie treci). Aktualizujemy rwnie na bieco
pomocnicz stron (www.androidbook.com), publikujc najnowsze oraz przyszociowe materiay
dotyczce zestawu Android SDK. W razie pojawienia si jakichkolwiek pyta w trakcie czytania
ksiki od uzyskania szybkiej odpowiedzi dzieli Ci tylko jeden e-mail.
R OZDZIA
1
Wprowadzenie
do platformy obliczeniowej
Android
32
Nowa platforma
dla nowego typu komputera osobistego
Wspania wieci dla programistw jest informacja, e wyspecjalizowane urzdzenia, takie
jak telefony komrkowe, mog zosta obecnie zaliczone do grona platform obliczeniowych
oglnego przeznaczenia (rysunek 1.1). Poczwszy od wersji Android 3.0, do tej listy moemy
oficjalnie doda tablety. W ten sposb programowanie dla urzdze przenonych staje si dostpne dla jzykw programowania oglnego przeznaczenia, dziki czemu powikszaj si zakres oraz udziay w rynku aplikacji przeznaczonych dla tych urzdze.
Platforma Android umoliwia urzeczywistnienie tej idei uniwersalnych komputerw w przypadku urzdze typu handheld. Jest to wszechstronne rodowisko z systemem operacyjnym
opartym na Linuksie, ktry zarzdza urzdzeniami, pamici oraz procesami. Biblioteki Java
Androida zapewniaj obsug funkcji telefonu, wideo, przetwarzania tekstu na mow, grafiki,
cznoci, programowania interfejsu uytkownika oraz wielu innych aspektw urzdzenia.
Chocia Android zosta zaprojektowany pod ktem urzdze przenonych oraz urzdze
typu tablet, posiada struktur w peni wyposaonego systemu operacyjnego. Firma
Google udostpnia t struktur programistom jzyka Java poprzez zestaw SDK
(ang. Software Development Kit zestaw do tworzenia oprogramowania) o nazwie
Android SDK. Praca na tym zestawie sprawia, e wcale nie ma si wraenia, i tworzy si
aplikacj dla urzdzenia przenonego, poniewa mona korzysta z wikszoci bibliotek
klas uywanych na stacji roboczej lub serwerze cznie z relacyjnymi bazami danych.
Zestaw Android SDK zapewnia obsug znacznej czci platformy Java Standard Edition (Java SE),
z wyjtkiem narzdzia Abstract Window Toolkit (AWT) oraz Swing. Zamiast tych narzdzi
Android SDK zosta zaopatrzony we wasny, obszerny, nowoczesny szkielet interfejsu uytkownika. Jzykiem programowania jest Java, zatem niezbdne jest rodowisko JVM (ang. Java
Virtual Machine wirtualna maszyna Javy), w ktrym odbywa si interpretowanie uruchomionego kodu bajtowego. Dziki rodowisku JVM uzyskujemy niezbdn optymalizacj, pozwalajc osign wydajno porwnywaln do wydajnoci aplikacji skompilowanych w takich jzykach, jak C oraz C++. Android zawiera wasne, zoptymalizowane rodowisko JVM,
umoliwiajce uruchomienie skompilowanych plikw klasy Java w celu okrelenia takich ogra-
33
nicze urzdzenia typu handheld, jak pojemno pamici, szybko procesora oraz moc. Ta
wirtualna maszyna, nazwana Dalvik VM, zostanie dokadniej omwiona w podrozdziale Zapoznanie si ze rodowiskiem Dalvik VM.
Podobiestwo jzyka Java do jego wersji stosowanej w komputerach PC oraz jego
prostota w poczeniu z rozbudowan bibliotek klas Androida sprawiaj, e jest to
bardzo atrakcyjna platforma programistyczna.
Na rysunku 1.2 zosta ukazany stos programowy Androida (wicej informacji na ten temat
mona znale w podrozdziale Stos programowy Androida).
34
W przeciwiestwie do pozostaych systemw operacyjnych, Android mia by otwarty, przystpny, o jawnym kodzie rdowym oraz, co waniejsze, mia zapewnia nowoczesny,
scentralizowany i spjny szkielet projektowania.
W 2005 roku firma Google wykupia mode przedsibiorstwo Android Inc., ktre rozpoczo
projektowanie platformy Android (rysunek 1.3). Wrd najwaniejszych pracownikw firmy
Android Inc. byli w owym czasie Andy Rubin, Rich Miner, Nick Sears oraz Chris White.
Pod koniec 2007 roku grupa czoowych przedsibiorstw utworzya wok platformy Android
klaster przemysowy Open Handset Alliance (http://www.openhandsetalliance.com). Niektrzy
czonkowie tego to:
Sprint Nextel,
T-Mobile,
Motorola,
Samsung,
Sony Ericsson,
Toshiba,
Vodafone,
Google,
Intel,
Texas Instruments.
Do 2011 roku liczba czonkw tej grupy znacznie si zwikszya (jest ich obecnie ponad 80), co
mona sprawdzi na stronie zrzeszenia Open Handset Alliance.
Zgodnie z informacjami zawartymi w witrynie klastra jednym z jego celw jest szybkie wprowadzanie innowacji oraz lepsza odpowied na potrzeby konsumentw w przestrzeni mobilnej,
a jednym z pierwszych wanych osigni by Android. Zosta on zaprojektowany w celu zaspokojenia potrzeb operatorw sieci komrkowych, producentw urzdze oraz twrcw aplikacji. Czonkowie zrzeszenia zobowizali si udostpni t istotn wasno intelektualn poprzez zastosowanie w stosunku do Androida warunkw licencji Apache License 2.01.
1
Apache License 2.0 jest licencj wolnego oprogramowania autorstwa Apache Software Foundation.
Licencja ta dopuszcza uycie kodu rdowego zarwno na potrzeby wolnego oprogramowania,
jak i zamknitego oprogramowania komercyjnego przyp. red.
35
Zestaw Android SDK zosta wydany jako wczesna wersja w listopadzie 2007 roku. We wrzeniu 2008 roku firma T-Mobile zapowiedziaa wydanie T-Mobile G1, pierwszego smartfonu
bazujcego na platformie Android. Kilka dni pniej firma Google ogosia wydanie zestawu
Android SDK Release Candidate 1.02. W padzierniku 2008 roku firma Google udostpnia kod
rdowy platformy Android w ramach licencji Apache. Pod koniec 2010 roku firma Google
wydaa zestaw Android SDK w wersji 2.3 dla smartfonw. Zestawowi temu nadano nazw kodow Gingerbread. W marcu 2011 roku zosta on zaktualizowany do wersji 2.3.3. Na pocztku
2011 roku zostaa wydana zoptymalizowana wersja Androida (w wersji 3.0) przeznaczona dla
tabletw, noszca nazw kodow Honeycomb. Jednym z pierwszych tabletw dziaajcych pod
kontrol tej wersji systemu operacyjnego jest Motorola XOOM.
Jednym z najwaniejszych celw twrcw Androida byo umoliwienie wsppracy rnych
aplikacji ze sob, a take wielokrotnego wykorzystywania skadnikw jednej aplikacji przez inn.
Takie uywanie fragmentw innych programw dotyczy nie tylko usug, lecz rwnie danych
oraz interfejsu UI (ang. User Interface interfejs uytkownika). W efekcie Android posiada
szereg funkcji konstrukcyjnych, dziki ktrym sta si w rzeczywisty sposb otwarty.
Android wczenie przycign wielu zwolennikw. Utrzyma rwnie zainteresowanie programistw, gdy posiada w peni rozwinite narzdzia, dziki ktrym mona wykorzystywa
poprzez model przetwarzania w chmurze (ang. cloud computing) udostpnione zasoby
sieciowe. Twrcy Androida usprawnili rwnie funkcjonowanie lokalnych magazynw danych
w samym urzdzeniu przenonym. Na ciepe przyjcie Androida wpyna rwnie moliwo
obsugi relacyjnych baz danych przez urzdzenia przenone.
Android w wersjach 1.0 oraz 1.1 (2008 rok) nie posiada moliwoci obsugi klawiatury programowej, wic urzdzenia musiay by wyposaone w fizyczne przyciski. Funkcja ta zostaa
wprowadzona w zestawie Android SDK 1.5 w kwietniu 2009 roku wraz z innymi dodatkami,
takimi jak zaawansowane moliwoci nagrywania multimediw, widety oraz aktywne foldery.
We wrzeniu 2009 roku pojawia si wersja 1.6 systemu Android, a w przecigu miesica zostaa
wydana wersja opatrzona numerem 2.0, dziki czemu nastpi przedwiteczny wysyp urzdze obsugujcych ten system. W tej wersji zaprezentowano funkcje zaawansowanego wyszukiwania danych oraz przetwarzania tekstu na mow.
Dziki obsudze jzyka HTML 5 system Android 2.0 posiada interesujce moliwoci wykorzystania stron WWW. Interfejs API kontaktw uleg znacznemu usprawnieniu. Dodano obsug
formatu Flash. Codziennie wydaje si coraz wicej aplikacji opartych na Androidzie, pojawiaj
si rwnie coraz nowsze rodzaje niezalenych sieciowych sklepw z aplikacjami. Mona ju
zakupi od dawna wyczekiwane komputery typu tablet, bazujce na systemie Android.
W wersji 2.3 Androida wrd najwaniejszych funkcji mona znale takie, jak zdalne usuwanie
zabezpieczonych danych przez administratorw, moliwo korzystania z aparatu oraz kamery
w warunkach sabego owietlenia czy korzystanie z hotspotw WiFi. Warto te zwrci uwag
na znaczn popraw wydajnoci, usprawnione dziaanie interfejsu Bluetooth, moliwo opcjonalnej instalacji aplikacji na karcie SD, moliwo korzystania ze rodowiska OpenGL ES 2.0,
usprawnione tworzenie kopii zapasowych, poprawion funkcj wyszukiwania, obsug standardu NFC (ang. Near Field Communication komunikacja bliskiego pola) umoliwiajc
przeprowadzanie operacji na kartach kredytowych, znacznie usprawnion obsug czujnikw
oraz wykrywania ruchu (podobnie jak w przypadku konsoli Wii), czat wideo oraz poprawiony
Android Market.
2
Release Candidate to niemal finalna wersja oprogramowania, w ktrej mog jeszcze zosta
wprowadzone drobne poprawki przyp. tum.
36
Najnowsze wcielenie Androida, oznaczone numerem 3.0, jest przeznaczone do obsugi urzdze typu tablet oraz o wiele potniejszych procesorw dwurdzeniowych, takich jak Nvidia
Tegra2. Najwaniejsz funkcj udostpnion w tej wersji jest obsuga wikszych wywietlaczy.
Wprowadzono zupenie now koncepcj prezentowania treci w aplikacjach, zwan fragmentami. Te cechy stanowi o atrakcyjnoci Androida 3.0. Wprowadzono rwnie wicej funkcji
spotykanych dotychczas w komputerach stacjonarnych, na przykad klas ActionBar lub moliwo przecigania elementw. Znacznej modernizacji ulegy widety ekranu startowego. Dostpnych jest teraz wicej kontrolek interfejsu uytkownika. W zakresie grafiki trjwymiarowej
rodowisko OpenGL zostao zaopatrzone w interfejs Renderscript, dalej rozwijajcy wersj ES
2.0. Jest to znakomite wprowadzenie na rynek dla tabletw pracujcych w systemie Android.
37
Rdzeniem platformy Android jest jdro Linuksa, zapewniajce obsug sterownikw urzdzenia,
dostp do zasobw, zarzdzanie energi oraz innymi zadaniami systemu operacyjnego. Sterowniki urzdzenia obejmuj ekran, aparat fotograficzny, klawiatur, WiFi, pami flash, audio oraz
komunikacj IPC (ang. Inter-Process Communication komunikacja midzyprocesowa; pojcie
to oznacza wymian danych pomidzy procesami systemu operacyjnego). Chocia rdzeniem
systemu jest Linux, wikszo aplikacji jeli nie wszystkie w urzdzeniach takich jak Motorola Droid jest projektowana w jzyku Java oraz uruchamiana w rodowisku Dalvik VM.
Na kolejnym poziomie, ponad rdzeniem Linuksa, umieszczono du liczb bibliotek C/C++,
wrd ktrych znajduj si biblioteki OpenGL, WebKit, FreeType, SSL (ang. Secure Sockets Layer;
protok sucy do bezpiecznej transmisji zaszyfrowanego strumienia danych), biblioteka
wykonawcza jzyka C (libc), SQLite oraz Media. Biblioteka systemowa jzyka C oparta na systemie Berkeley Software Distribution (BSD) jest dopasowana (zmniejszono j o ponad poow
w stosunku do pierwotnego rozmiaru) do urzdze posiadajcych wbudowany system bazujcy na Linuksie. Biblioteki multimediw s oparte na standardzie OpenCORE PacketVideo
(www.packetvideo.com/). Zapewniaj one obsug nagrywania oraz odtwarzania formatw audio
38
i wideo. Biblioteka Surface Manager kontroluje dostp do systemu wywietlania, a take obsuguje grafik dwu- oraz trjwymiarow. Prawdopodobnie wraz z nowymi wersjami systemu bd
dodawane kolejne biblioteki natywne.
Biblioteka WebKit odpowiada za obsug przegldarki; to wanie ona obsuguje przegldarki Google Chrome oraz Safari. Biblioteka FreeType zajmuje si obsug czcionek. SQLite
(www.sqlite.org/) jest relacyjn baz danych dostpn na samym urzdzeniu przenonym. Jest
to take forma niezalenej, posiadajcej jawny kod rdowy technologii relacyjnej bazy danych, niezwizanej bezporednio z Androidem. Mona rwnie pobra narzdzia przeznaczone
dla bazy SQLite i uywa ich do baz danych Androida.
Wikszo szkieletu aplikacji uzyskuje dostp do tych bibliotek podstawowych poprzez rodowisko Dalvik VM, ktre stanowi bram do platformy Android. Jak zostao wyjanione w poprzednich podrozdziaach, rodowisko Dalvik zostao zoptymalizowane do jednoczesnego uruchamiania wielu instancji wirtualnych maszyn. Podczas uzyskiwania dostpu do podstawowych
bibliotek przez aplikacje Java kada z tych aplikacji otrzymuje wasn instancj maszyny VM.
Gwne biblioteki interfejsu API rodowiska Java w Androidzie obejmuj telefoni, zasoby,
lokacje, interfejs uytkownika, dostawcw (dane) treci oraz menedery pakietw (instalacja,
zabezpieczenia i tak dalej). Programici projektuj aplikacje dla uytkownika kocowego w grnej warstwie tego interfejsu API. Przykadami takich aplikacji s Home, Contacts, Phone,
Browser i tak dalej.
Android posiada rwnie wasn bibliotek do obsugi grafiki Google 2D Skia, ktr napisano w jzykach C i C++. Skia jest rwnie elementem rdzenia przegldarki Google Chrome. Jednak interfejsy API odpowiedzialne za grafik trjwymiarow bazuj w Androidzie na implementacji pakietu OpenGL ES grupy Khronos (http://www.khronos.org). Pakiet ten zawiera
podzbiory funkcji OpenGL, ktrych adresatami s wbudowane systemy.
Jeli za chodzi o multimedia, Android obsuguje najpopularniejsze formaty obrazw, dwikw
oraz wideo. Z perspektywy sieci bezprzewodowych dostpne s interfejsy API obsugujce sieci
Bluetooth, EDGE, 3G, WiFi oraz telefoni GSM (ang. Global System for Mobile Communication
globalny system komunikacji mobilnej), w zalenoci od parametrw sprztowych urzdzenia.
Emulator Androida
Zestaw Android SDK wyposaono we wtyczk organizacji Eclipse, nazwan narzdziami ADT
(ang. Android Development Tools narzdzia projektowe dla rodowiska Android). To rodowisko IDE (ang. Integrated Development Environment zintegrowane rodowisko projektowe)
suy do projektowania, usuwania bdw oraz testowania aplikacji Java (szczegowe informacje
na temat narzdzi ADT znajduj si w rozdziale 2.). Mona rwnie uywa zestawu Android
SDK bez narzdzi ADT; wykorzystywane s wtedy narzdzia wiersza polece. Obydwie metody
39
40
Ten przykadowy kod powoduje, e Android poprzez intencj otwiera odpowiednie okno,
w ktrym bdzie wywietlana zawarto strony WWW. W zalenoci od listy dostpnych przegldarek zainstalowanych w urzdzeniu Android wybierze najodpowiedniejsz. Intencje zostay
szczegowo omwione w rozdziale 5.
Android zapewnia take rozbudowan obsug zasobw, obejmujcych znajome kategorie elementw oraz plikw, na przykad cigi tekstowe oraz mapy bitowe, jak rwnie mniej znane
skadniki, takie jak definicje widoku oparte na jzyku XML. S one wykorzystywane w nowoczesny, atwy, intuicyjny oraz wygodny dla uytkownika sposb. Poniej zosta zamieszczony
przykad, w ktrym identyfikatory zasobw zostaj automatycznie wygenerowane dla zasobw
zdefiniowanych w plikach XML:
public final class R {
public static final class attr { }
public static final class drawable {
public static final int myanimation=0x7f020001;
public static final int numbers19=0x7f02000e;
}
public static final class id {
public static final int textViewId1=0x7f080003;
}
public static final class layout {
public static final int frame_animations_layout=0x7f030001;
public static final int main=0x7f030002;
}
public static final class string {
41
W celu zaadowania ukadu graficznego do okna aktywnoci wykorzystamy identyfikator wygenerowany dla tego pliku XML (proces ten zostanie przeanalizowany w rozdziale 6.). Android
obsuguje take menu (to zagadnienie zostanie rozwinite w rozdziale 7.) od standardowych
do kontekstowych. Praca przy takich menu jest bardzo wygodna, gdy s one rwnie wczytywane jako pliki XML, a ich identyfikatory zasobw s generowane automatycznie. Menu mona
deklarowa w pliku XML w nastpujcy sposb:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Android obsuguje okna dialogowe, z ktrych wszystkie s asynchroniczne. Mog one stanowi
nowego rodzaju wyzwanie dla projektantw przyzwyczajonych do synchronicznych, modalnych okien dialogowych stosowanych w niektrych szkieletach okienkowych. Menu zajmiemy
si szerzej w rozdziale 7., a oknami dialogowymi w rozdziale 8., powiconym rwnie sposobom korzystania z protokow asynchronicznych okien dialogowych.
42
Android obsuguje take animacje w formie stosu interfejsu UI, opartego na widokach oraz rysowanych obiektach. S dostpne dwa rodzaje animacji: animowane przejcia (ang. tweening)
oraz rysowane klatka po klatce. Animowane przejcia polegaj na rysowaniu obrazw znajdujcych si pomidzy kluczowymi klatkami animacji. Osiga si to poprzez zmian rednich wartoci
w regularnych odstpach czasu oraz ponowne rysowanie powierzchni. Animacja klatka po
klatce wystpuje wtedy, gdy jest rysowana seria klatek w regularnych odstpach czasowych.
Android umoliwia wykorzystanie obydwu technik animacji poprzez wywoywanie zwrotne,
stosowanie interpolatorw oraz macierzy transformacji.
Ponadto istnieje moliwo zdefiniowania tych animacji w pliku zasobw XML. W poniszym
przykadzie seria ponumerowanych obrazw jest odtwarzana w animacji klatka po klatce:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/numbers11" android:duration="50" />
...
<item android:drawable="@drawable/numbers19" android:duration="50" />
</animation-list>
43
W Androidzie zostaa rwnie udostpniona obsuga ekranu dotykowego oraz gestw opartych
na ruchach palcw po wywietlaczu urzdzenia. Kady rodzaj ruchu po ekranie mona zapisa
jako gest. Nastpnie taki gest zostaje powizany w aplikacji z okrelonymi czynnociami. Ekrany
dotykowe oraz gesty zostay dokadnie przeanalizowane w rozdziale 25.
Coraz waniejszym elementem obsugi urzdze mobilnych staj si czujniki. S one omwione w rozdziale 26.
Kolejn niezbdn innowacj wymagan dla urzdze mobilnych jest dynamiczna natura ich
konfiguracji. Na przykad bardzo atwo zmieni tryb przegldania pomidzy orientacj pionow
a poziom. Mona rwnie zadokowa urzdzenie przenone i korzysta z niego jak z laptopa.
W Androidzie 3.0 zostao wprowadzone pojcie fragmentw, dziki ktrym mona skutecznie
definiowa takie rnorodne zachowania. Rozdzia 29. zosta powicony fragmentom.
Omwilimy rwnie now funkcj paskw menu, zaprezentowan w wersji 3.0, ktrej przyjrzymy si w rozdziale 30. Koncepcja paskw menu w Androidzie zrwnuje j z paradygmatem
paskw menu znanych z komputerw stacjonarnych. W rozdziale 25. omwilimy funkcj przecigania elementw (dawny sposb), temat ten poruszono take w rozdziale 31. (przeciganie
elementw sposobem wprowadzonym w Androidzie 3.0).
Poza rodowiskiem Android SDK dostpnych jest wiele niezalenych innowacji, usprawniajcych oraz uatwiajcych proces projektowania. Przykadami s narzdzia XML/VM, PhoneGap oraz Titanium.
44
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".HelloWorld"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
W pliku manifecie zostaj zdefiniowane aktywnoci, nastpuje rejestracja dostawcw treci oraz
usug, a take s deklarowane uprawnienia. W dalszej czci ksiki podczas omawiania rnorodnych koncepcji bd pojawiay si kolejne szczegy dotyczce pliku manifestu.
45
46
47
Ponadto Android posiada olbrzymi liczb pakietw w przestrzeni nazw java.*. Wyrni mona
pakiety awt.font, io, lang, lang.annotation, lang.ref, lang.reflect, math, net, nio, nio.channels,
nio.channels.spi, nio.charset, security, security.acl, security.cert, security.interfaces, security.spec,
sql, text, util, util.concurrent, util.concurrent.atomic, util.concurrent.locks, util.jar, util.logging,
util.prefs, util.regex oraz util.zip. Nastpne pakiety pochodz z przestrzeni nazw javax: crypto,
crypto.spec, microedition.khronos.egl, microedition.khronos.opengles, net, net.ssl, security.auth,
security.auth.callback, security.auth.login, security.auth.x500, security.cert, sql, xml oraz xmlparsers.
Jakby tego byo mao, Android zosta wyposaony w wiele pakietw z takich przestrzeni nazw, jak
org.apache.http.*, a take org.json, org.w3c.dom, org.xml.sax, org.xml.sax.ext, org.xml.sax.helpers,
org.xmlpull.v1 oraz org.xmlpull.v1.sax2. Razem te liczne pakiety tworz rozbudowan platform
obliczeniow, umoliwiajc pisanie aplikacji dla urzdze typu handheld.
48
49
Podsumowanie
W tym rozdziale chcielimy wzbudzi u Czytelnika zainteresowanie Androidem. Osoby programujce w rodowisku Java maj znakomit okazj, aby wycign korzyci z tej ekscytujcej,
rozbudowanej platformy obliczeniowej oglnego przeznaczenia. Zapraszamy w podr przez
reszt ksiki celem tej wdrwki jest dogbne zrozumienie zestawu Android SDK.
50
R OZDZIA
2
Konfigurowanie rodowiska
programowania
W poprzednim rozdziale omwilimy histori Androida oraz zarysowalimy koncepcje, ktre zostan omwione w dalszej czci ksiki. W tym momencie Czytelnik
prawdopodobnie moe zechcie ju zaj si kodem. Rozpoczniemy od przedstawienia elementw potrzebnych do tworzenia aplikacji w rodowisku Android
SDK oraz od przygotowania tego rodowiska. Nastpnie szczegowo przeanalizujemy aplikacj Witaj, wiecie! oraz rozoymy na czynniki pierwsze nieco bardziej
zoony fragment kodu. W dalszej kolejnoci objanimy cykl ycia aplikacji w Androidzie, a na kocu powicimy chwil tematowi wyszukiwania bdw w aplikacji
za pomoc narzdzi AVD (ang. Android Virtual Devices wirtualne urzdzenia
Androida).
Do tworzenia aplikacji przeznaczonych dla Androida wymagane jest posiadanie
zestawu JDK (ang. Java SE Development Kit zestaw do projektowania w rodowisku Java SE), rodowiska Android SDK oraz rodowiska projektowego. Inaczej mwic, mona pisa aplikacje za pomoc najprostszego edytora tekstowego, ale na
potrzeby tworzenia projektw omwionych w tej ksice lepsze bdzie powszechnie
dostpne rodowisko IDE Eclipse. Android SDK wymaga zestawu JDK w wersji co
najmniej 5 (korzystalimy z JDK 6) oraz rodowiska Eclipse w wersji nie wczeniejszej ni 3.4 (uywalimy wersji Eclipse 3.5, noszcej nazw Galileo, oraz wersji 3.6,
nazwanej Helios).
eby uatwi sobie ycie, mona zainstalowa narzdzia ADT (ang. Android Development Tools narzdzia projektowe Androida). Jest to wtyczka rodowiska Eclipse,
umoliwiajca tworzenie aplikacji przeznaczonych dla Androida w rodowisku IDE
Eclipse. W istocie wszystkie przykady w tej ksice zostay zaprojektowane w rodowisku Eclipse za pomoc narzdzi ADT.
Zestaw Android SDK skada si z dwch gwnych skadnikw. S to narzdzia i pakiety. Podczas jego pierwszej instalacji otrzymujemy do dyspozycji wycznie podstawowe narzdzia. S to przewanie pliki wykonywalne oraz pomocnicze, wspierajce proces tworzenia aplikacji. Pakietami nazywamy pliki, ktre s unikatowe
dla danej wersji Androida (nazywanej platform), lub dodatki przeznaczone dla
okrelonej platformy. Do platform zaliczamy Androida w wersjach od 1.5 do 3.0.
52
Na dodatki skadaj si takie narzdzia, jak interfejs API Google Maps, walidator licencji
przeznaczonych dla Android Market (ang. Market License Validator), a nawet dodatki pochodzce od producentw telefonw, jak na przykad wtyczka Galaxy Tab firmy Samsung.
Po zainstalowaniu pakietu SDK bdzie mona nastpnie wykorzysta jedno z narzdzi do
pobrania i skonfigurowania platform oraz dodatkw. Zaczynajmy!
Konfigurowanie rodowiska
eby mc tworzy aplikacje dla Androida, naley zapewni sobie rodowisko projektowe.
W tym podrozdziale zajmiemy si omwieniem procesu pobierania aplikacji JDK 6, rodowiska
Eclipse, zestawu Android SDK (narzdzia i pakiety) oraz dodatku ADT. Pomoemy take skonfigurowa rodowisko Eclipse, tak aby mona byo w nim tworzy aplikacje dla Androida.
rodowisko Android SDK jest kompatybilne z systemami Windows (Windows XP, Windows
Vista oraz Windows 7), Mac OS X (jedynie z procesorami Intel) oraz Linux (rwnie wycznie
z procesorami Intel). W tym rozdziale omwimy proces konfigurowania rodowiska we wszystkich wymienionych rodzajach systemw (w przypadku Linuksa jedynie dla wariantu Ubuntu).
W kolejnych rozdziaach nie bdziemy si zajmowa rnicami pomidzy poszczeglnymi
systemami operacyjnymi.
Polecenie to spowoduje zainstalowanie aplikacji JDK oraz wszystkich wymaganych dodatkowych skadnikw, takich jak rodowisko JRE (ang. Java Runtime Environment rodowisko uruchomieniowe Java). Jeeli tak si nie stanie, oznacza to prawdopodobnie, e naley
doda nowe rdo oprogramowania (ang. software source) i sprbowa wykona powysze
polecenie ponownie. Na stronie https://help.ubuntu.com/community/Repositories/Ubuntu wyjaniono zasad dziaania rde oprogramowania oraz sposb dodawania poczenia do oprogramowania pochodzcego z niezalenego rda. Proces ten jest odmienny dla rnych wersji
Linuksa. Po jego przeprowadzeniu naley sprbowa ponownie wykona widoczne powyej
polecenie.
Wraz z wprowadzeniem wersji Ubuntu 10.04 (Lucid Lynx) zalecane jest korzystanie raczej
z zestawu OpenJDK, a nie Oracle/Sun JDK. Aby go zainstalowa, stosujemy nastpujce polecenie:
sudo apt-get install openjdk-6-jdk
53
Jeeli aplikacja nie zostanie znaleziona, naley tak jak zostao wczeniej wspomniane skonfigurowa oprogramowanie wydane przez niezalenego wydawc i ponownie uruchomi polecenie. Spowoduje to automatyczne dodanie wszystkich pakietw wymaganych przez zestaw JDK.
Istnieje moliwo jednoczesnego posiadania zestaww OpenJDK oraz Oracle/Sun JDK. W celu
przeczania pomidzy aktywnymi wersjami rodowiska Java zainstalowanymi w systemie
Ubuntu uruchamiamy ponisze polecenie w interpreterze powoki:
sudo update-alternatives --config java
54
Alternatywnym rozwizaniem (wycznie w przypadku systemu Windows) jest pobranie instalatora w formacie .exe, a nie skompresowanego do formatu .zip. Instalator sprawdzi obecno
zestawu Java JDK, wypakuje wymagane pliki oraz uruchomi aplikacj SDK Manager, co uatwi
pobranie pozostaych plikw.
Bez wzgldu na to, czy korzystamy z instalatora, czy bezporednio uruchamiamy aplikacj SDK
Manager, nastpnym etapem jest zainstalowanie niektrych pakietw. W trakcie instalacji
zestawu Android SDK nie zawiera on adnej platformy (na przykad rnych wersji systemu
Android). Instalowanie platform jest bardzo proste. Po uruchomieniu aplikacji SDK Manager
naley wybra Window/Android SDK and AVD Manager, klikn element Available Packages,
zaznaczy adres rda https://dl-ssl.google.com/android/repository/repository.xml, a nastpnie
55
wybra potrzebne platformy i dodatki, na przykad Android 2.3.3 (rysunek 2.2). Aby rodowisko
dziaao, naley koniecznie doda narzdzia powizane z dan platform. Poniewa ju niebawem bdziemy z niej korzysta, warto teraz doda platform przynajmniej w wersji 1.6.
Teraz wystarczy klikn przycisk Install Selected. Trzeba zatwierdzi kady element, zaznaczajc
opcj Accept1, a nastpnie zatwierdzi przyciskiem Install Accepted. Android pobierze wybrane
pakiety i platformy. Dodatki Google APIs su do projektowania aplikacji wykorzystujcych
Google Maps. Istnieje moliwo przegldania zainstalowanych dodatkw po klikniciu opcji
Installed Packages, widocznej na rysunku 2.2 w lewym grnym rogu okna. Jeli bdzie trzeba,
w kadej chwili mona tu wrci i zainstalowa nastpne pakiety.
Naley si jeszcze upewni, e cz polecenia, ktra dotyczy cieki do katalogu tools, bdzie wskazywaa miejsce zainstalowania katalogu.
1
56
Okno narzdzi
W dalszej czci ksiki pojawi si momenty, gdy trzeba bdzie uruchamia pewne programy
z wiersza polece. S one czci rodowiska JDK lub Android SDK. Dziki umieszczeniu ich
w zmiennej systemowej PATH nie bdzie trzeba wpisywa penej cieki do nich, jednak do
uruchomienia tych programw konieczne jest otwarcie okna narzdzi. W nastpnych rozdziaach bdziemy korzysta z takiego okna. Najprostszym sposobem jego uruchomienia w systemie
Windows jest kliknicie menu Start/Wyszukaj, a nastpnie wpisanie cmd w polu tekstowym
i kliknicie przycisku OK. W systemie Mac OS X naley wybra aplikacj Terminal w folderze
Applications z poziomu menedera plikw Finder lub z poziomu Dock. W systemie Linux aplikacja Terminal znajduje si w menu Applications/Accessories.
Trzeba wspomnie o jeszcze jednej sprawie dotyczcej rnic pomidzy systemami operacyjnymi: niekiedy trzeba zna adres IP stacji roboczej. W systemie Windows naley uruchomi
wiersz polece i wpisa polecenie ipconfig. Wrd wynikw bdzie widnia wpis dotyczcy
IPv4 (lub podobny), a obok zostanie wywietlony adres IP danego komputera. Wyglda on mniej
wicej tak: 192.168.1.25. W systemach Mac OS X oraz Linux naley uruchomi wiersz polece
i wpisa ifconfig. Adres IP jest umieszczony obok wpisu inet addr. Moe te by widoczne
poczenie sieciowe przy nazwie localhost lub lo. Adres IP tego poczenia to 127.0.0.1. Jest to
specjalny typ poczenia sieciowego, wykorzystywany przez system operacyjny, i nie ma nic
wsplnego z adresem IP stacji roboczej. Naley poszuka wiersza, w ktrym widoczny jest
inny adres IP.
57
Rysunek 2.3. Instalacja narzdzi ADT za pomoc funkcji Install New Software w rodowisku Eclipse
Ostatnim etapem aktywacji narzdzi ADT w obrbie rodowiska Eclipse jest odniesienie ich do
zestawu Android SDK. W tym celu naley w rodowisku Eclipse otworzy menu Window
i wybra opcj Preferences (w systemie Mac OS X opcja ta jest dostpna w menu Eclipse). W oknie
dialogowym Preferences naley wybra wze Android i wpisa ciek katalogu Android SDK
(rysunek 2.4), a nastpnie klikn przycisk Apply. W midzyczasie moe si pojawi okno dialogowe, w ktrym mona zaznaczy opcj wysyania do firmy Google statystyk dotyczcych
wykorzystania programu Android SDK. Wybr naley do Czytelnika. Teraz wystarczy klikn
OK, eby zamkn okno Preferences.
58
SDK Manager mona uruchomi z poziomu rodowiska Eclipse. Naley w tym celu wybra zakadk Window/Android SDK and AVD Manager. Powinno zosta wywietlone okno przedstawione na rysunku 2.2, chocia prawdopodobnie nie bd widoczne wszystkie opcje dostpne
podczas osobnego uruchamiania aplikacji SDK Manager.
Ju niemal nadszed czas na zapoznanie si z pierwsz aplikacj dla Androida najpierw jednak
musimy zapozna si z podstawowymi pojciami odnoszcymi si do aplikacji tworzonych dla
tej platformy.
Widok
Widoki s elementami interfejsu uytkownika tworzcymi jego podstawowe bloki budulcowe.
Mog one przybra ksztat przycisku, etykiety, pola tekstowego oraz wielu innych skadnikw
interfejsu UI. Jeeli Czytelnik wie, czym s widoki w platformach J2EE oraz Swing, szybko zrozumie widoki w Androidzie. Widoki czsto s wykorzystywane jako kontenery dla innych widokw, co zazwyczaj oznacza istnienie hierarchii widokw w interfejsie uytkownika. Ostatecznie
wszystkie elementy widoczne na ekranie s widokami.
59
Aktywno
Aktywno jest pojciem interfejsu uytkownika. Aktywno przewanie jest reprezentacj pojedynczego okna aplikacji. Zazwyczaj zawarty jest w niej przynajmniej jeden widok, ale niekoniecznie musi tak by. Okrelenie aktywno do dokadnie wskazuje jej przeznaczenie jest
to obiekt pomagajcy uytkownikowi wykona dan czynno. Tak czynnoci moe by przegldanie, tworzenie lub edycja danych. Wikszo aplikacji tworzonych dla systemu Android
zawiera kilka aktywnoci.
Intencja
Uoglniajc, sowo intencja oznacza intencj, zamiar wykonania jakiej pracy. W terminie tym
mieci si kilka poj, wic najlepszym sposobem jego zrozumienia jest wykorzystanie intencji
w praktyce. Intencje s wykorzystywane w nastpujcych celach:
nadawanie komunikatu,
uruchamianie usugi,
rozpoczynanie aktywnoci,
wywietlanie strony WWW lub listy kontaktw,
wybieranie lub odbieranie poczenia telefonicznego.
Intencje nie zawsze s inicjowane przez aplikacj s take wykorzystywane przez system do
powiadamiania aplikacji o okrelonych zdarzeniach (na przykad o otrzymaniu wiadomoci
tekstowej).
Intencje mona podzieli na jawne oraz niejawne. Jeeli zostanie wyranie okrelone, e adres
URL ma by widoczny, system automatycznie zdecyduje, jaki skadnik bdzie dotyczy intencji.
Istnieje take moliwo okrelenia konkretnej informacji, w jaki sposb powinna by potraktowana intencja. Intencje luno cz dziaanie z jego uchwytem.
Dostawca treci
Wspdzielenie danych pomidzy aplikacjami urzdzenia przenonego jest powszechnie
stosowan praktyk. Android definiuje wic standardowy mechanizm wspuytkowania danych (takich jak listy kontaktw) przez aplikacje bez koniecznoci odsaniania podstawowych magazynw, struktury oraz implementacji. Dziki dostawcom treci mona ujawnia
dane oraz pozwala jednym aplikacjom korzysta z zasobw innych programw.
Usuga
Usugi Androida s podobne do usug obecnych w systemie Windows lub na innych platformach s to procesy dziaajce w tle, ktre potencjalnie mog trwa przez dugi czas. W Androidzie s zdefiniowane dwa rodzaje usug: usugi lokalne oraz usugi zdalne. Usugi lokalne s
elementami dostpnymi wycznie dla aplikacji je obsugujcej. Z drugiej strony usugi zdalne
s przeznaczone dla innych aplikacji, czcych si z nimi w sposb zdalny.
Przykadem usugi jest skadnik wykorzystywany przez aplikacj pocztow do sprawdzania, czy
pojawiy si nowe wiadomoci. Usuga ta jest lokalna, jeeli nie jest uywana przez inne aplikacje
znajdujce si w urzdzeniu. Jeeli korzysta z niej kilka usug, mona j zaimplementowa
w formie usugi zdalnej. Jak zostanie wyjanione w rozdziale 11., jest to zwizane z rnic pomidzy funkcjami startService() oraz bindService().
60
Istnieje moliwo stosowania istniejcych usug, jak rwnie pisania wasnych za pomoc
rozszerzania klasy Service.
AndroidManifest.xml
Plik AndroidManifest.xml, podobny do pliku web.xml w wiecie J2EE, okrela zawarto
oraz zachowanie aplikacji. Na przykad znajduje si w nim lista aktywnoci oraz usug danej
aplikacji, a take uprawnie i waciwoci wymaganych do jej uruchomienia.
Urzdzenia AVD
Urzdzenie AVD (ang. Android Virtual Device wirtualne urzdzenie Androida) pozwala
programistom na przetestowanie aplikacji bez koniecznoci poczenia si z rzeczywistym
urzdzeniem (zazwyczaj telefonem lub tabletem). Mona tworzy rne konfiguracje urzdze AVD, zdolne do emulowania rnych modeli istniejcych urzdze.
Witaj, wiecie!
Teraz moemy rozpocz pisanie pierwszej aplikacji dla Androida. Na pocztek utworzymy
prosty program Witaj, wiecie!. Szkielet aplikacji zbudujemy w nastpujcy sposb:
1. Uruchom rodowisko Eclipse i wybierz File/New/Project. W oknie dialogowym
New Project otwrz wze Android, a nastpnie wybierz opcj Android Project, po
czym kliknij przycisk Next. Ujrzysz okno New Android Project, zaprezentowane na
rysunku 2.5 (by moe dostp do projektu Android istnieje w menu New, dziki
czemu mona nieco szybciej otwiera nowe projekty). Jeeli istnieje taka moliwo,
moesz rwnie skorzysta z przycisku New Android Project na pasku narzdzi.
2. Wpisz, zgodnie z rysunkiem 2.5, nazw projektu HelloAndroid. Musimy w jaki
sposb odrnia nazw tego projektu od innych projektw tworzonych w rodowisku
Eclipse, naley wic wybiera takie nazwy, ktre na pierwszy rzut oka na list projektw
bd atwe do rozpoznania. Warto rwnie zauway, e domylne umiejscowienie
projektu jest zwizane z lokalizacj przestrzeni roboczej rodowiska Eclipse. Kreator
nowego projektu doda nazw nowej aplikacji do obszaru roboczego. W naszym
przypadku, jeli przestrzeni robocz jest C:\android, nowy projekt zostanie umieszczony
w katalogu C:\android\HelloAndroid.
3. Na razie zostaw sekcj Contents bez zmian, poniewa w przestrzeni roboczej chcemy
utworzy nowy projekt umieszczony w domylnej lokacji.
4. Zaznacz Android 1.6 w oknie Build Target, tak jak zostao to pokazane na rysunku 2.5.
Ta wersja Androida bdzie suya za baz naszej aplikacji. Program ten bdzie mona
uruchomi na pniejszych wersjach platformy, na przykad 2.1 albo 2.3, ale wersja
1.6 posiada wszystkie wymagane funkcje, zatem zostanie ona nasz wersj docelow.
Zasadniczo najlepiej jest wybiera najnisz dopuszczaln wersj, poniewa w ten
sposb maksymalizujemy liczb urzdze, na ktrych tworzona aplikacja zadziaa
zgodnie z oczekiwaniami.
5. Wprowad Witaj, Androidzie jako nazw aplikacji. Nazwa ta bdzie pojawiaa si
wraz z ikon aplikacji, w pasku tytuowym oraz na listach aplikacji. Powinna by
opisowa, ale nie za duga.
61
62
9. Kliknij przycisk Finish, dziki czemu narzdzia ADT wygeneruj szkielet projektu.
Teraz otwrz plik HelloActivity.java w folderze src i zmodyfikuj metod onCreate()
w nastpujcy sposb:
/** Called when activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
63
64
65
Wiadomo ju, w jaki sposb utworzy now aplikacj w Androidzie oraz jak j uruchomi na
emulatorze. Teraz przyjrzymy si uwaniej urzdzeniom AVD, po czym zagbimy si w wiat
artefaktw oraz struktury aplikacji Androida.
66
(lub edytowania) urzdze AVD do innej lokalizacji. Stwrzmy teraz folder, w ktrym bdzie
przechowywany obraz naszego urzdzenia AVD, na przykad c:\avd\. Kolejnym etapem jest
utworzenie listy dostpnych docelowych wersji Androida za pomoc nastpujcego polecenia,
wprowadzonego w oknie narzdzi:
android list target
Opis
create avd
67
Rysunek 2.11. Ekran wynikowy utworzenia urzdzenia AVD za pomoc pliku android.bat
Naley rwnie mie na uwadze, e wybr interfejsu Google API z listy SDK Target daje dostp
do funkcji korzystania z map w urzdzeniu AVD, podczas gdy wybranie interfejsu Android
1.5 lub pniejszego nie zapewni takiej moliwoci. O wiele wicej uwagi powicimy mapom
w rozdziale 17.
Aplikacje dla Androida skadaj si z elementw niezbdnych oraz opcjonalnych. W tabeli 2.2
zostay wymienione skadniki aplikacji tworzonej dla Androida.
68
Opis
Wymagany?
AndroidManifest.xml
Tak
src
Tak
assets
Nie
res
Tak
drawable
Nie
anim
Nie
layout
Nie
menu
Nie
values
Nie
xml
Nie
raw
Nie
Jak zostao pokazane w tabeli 2.2, aplikacja systemu Android skada si z trzech zasadniczych
elementw: deskryptora aplikacji, zbioru zasobw oraz kodu rdowego aplikacji. Jeeli zignorowa na chwil plik AndroidManifest.xml, mona zauway prostot aplikacji: logika biznesowa
przybiera form kodu, a caa reszta to zasoby. Taka nieskomplikowana struktura przypomina
szkielet aplikacji J2EE, w ktrym zasobom odpowiadaj strony JSP, logice biznesowej serwlety,
a odpowiednikiem pliku AndroidManifest.xml jest plik web.xml.
69
Mona rwnie porwna modele projektowania w rodowiskach J2EE oraz Android. W przypadku J2EE widoki s budowane za pomoc jzyka znacznikw. W Androidzie wykorzystano t
sam filozofi, ale stosowanym jzykiem jest XML. Jest to korzystne rozwizanie, gdy nie ma koniecznoci wplatania widoku do gwnego kodu; wygld i zachowanie aplikacji mona zmienia
poprzez edytowanie znacznikw.
Naley rwnie pamita o kilku ograniczeniach dotyczcych zasobw. Po pierwsze, Android obsuguje jedynie liniow list plikw, znajdujc si w predefiniowanych plikach umieszczonych
w folderze res. Na przykad nie moe uzyska dostpu do zagniedonych folderw znajdujcych
si w katalogu layout (tak samo w przypadku pozostaych folderw podrzdnych do folderu res).
Po drugie, istniej pewne podobiestwa pomidzy folderem assets oraz folderem raw, umieszczonym w katalogu res. W obydwu katalogach mog by przechowywane nieskompresowane pliki, ale dane znajdujce si w folderze raw s uznawane za zasoby, a w folderze assets ju
nie. Zatem pliki z katalogu raw bd zlokalizowane, dostpne poprzez identyfikatory zasobw
i tak dalej. Jednak informacje znajdujce si w katalogu assets s traktowane jako dane oglnego
przeznaczenia, pozbawione ogranicze oraz obsugi zasobw. Warto zwrci na to uwag, gdy
pozbawienie danych znajdujcych si w katalogu assets miana zasobw umoliwia utworzenie
wasnej hierarchii plikw i folderw w jego wntrzu (wicej informacji na temat zasobw znajduje si w rozdziale 3.).
Dosy wyranie wida, e w Androidzie cakiem czsto stosuje si jzyk XML. Powszechnie
wiadomo, e jest to do rozbudowany jzyk, rodzi si zatem pytanie, czy korzystanie
z niego, gdy celem jest urzdzenie posiadajce ograniczone zasoby, ma sens. Okazuje si,
e kod XML, uywany podczas projektowania aplikacji, jest w rzeczywistoci kompilowany
do kodu binarnego przy uyciu narzdzia AAPR (ang. Android Asset Packaging Tool
narzdzie pakowania zasobw Androida). Zatem podczas instalowania aplikacji na
urzdzeniu pliki s konwertowane i przechowywane w formie kodu binarnego. Podczas
uruchomienia plik jest odczytywany w tej formie i nie jest konwertowany ponownie
na plik XML. Ta metoda czy zalety obydwu technologii mona pracowa z jzykiem
XML i nie martwi si o ilo cennych zasobw urzdzenia.
70
aplikacji nie bdzie w niej adnych zapisanych notatek, wic uytkownik ujrzy pust list. Po
wciniciu przycisku Menu zostanie wywietlona lista czynnoci, a wrd nich opcja dodania nowej notatki. Po utworzeniu nowego pliku mona go edytowa lub usun za pomoc odpowiedniej opcji.
eby wczyta przykadow aplikacj Notepad w rodowisku Eclipse, naley wykona nastpujce
czynnoci:
1. Uruchom program Eclipse.
2. Otwrz File/New/Project.
3. W oknie dialogowym New Project wybierz Android/Android Project i kliknij Next.
4. W nastpnym oknie wpisz NotesList jako nazw projektu, wybierz opcj Create project
from existing sample, nastpnie zaznacz pole Android 1.6 na licie Build Target.
Z rozwijanej listy wybierz aplikacj Notepad. Zwr uwag, e jest ona umiejscowiona
w folderze platforms\android-1.6\samples pakietu Android SDK, ktry wczeniej pobrae.
Po wybraniu tej aplikacji zostanie automatycznie odczytany plik AndroidManifest.xml
i zostan wypenione pozostae pola w tym oknie dialogowym (rysunek 2.13).
71
Teraz aplikacja NotesList powinna by dostpna w rodowisku Eclipse. Jeeli zostan wywietlone jakie informacje o problemach zwizanych z tym projektem, mona sprbowa uy opcji
Clean z menu Project, aby je usun. eby uruchomi aplikacj, mona utworzy aplikacj uruchomieniow (podobnie jak to zrobilimy przy okazji programu Witaj, wiecie!) lub klikn
prawym przyciskiem ikon projektu, wybra opcj Run As, a nastpnie Android Application.
Spowoduje to uruchomienie emulatora i zainstalowanie na nim aplikacji. Po wczytaniu emulatora wystarczy odblokowa ekran emulatora, eby zostaa wywietlona aplikacja NotesList.
Aby si z ni zaznajomi, mona po niej pomyszkowa przez kilka minut.
Jak wida, program zawiera kilka plikw .java, obrazw .png, trzy widoki (w folderze layout)
oraz plik AndroidManifest.xml. Gdyby to bya aplikacja wiersza polece, naleaoby poszuka pliku, w ktrym jest umieszczona metoda Main. Zatem co jest odpowiednikiem metody Main
w Androidzie?
W rodowisku Android jest definiowana pocztkowa aktywno, zwana take aktywnoci szczytowego poziomu. Jeeli przyjrze si zawartoci pliku AndroidManifest.xml, mona tam znale
72
jednego dostawc oraz trzy aktywnoci. Aktywno NotesList wyznacza filtr intencji dla
akcji android.intent.action.MAIN, a take dla kategorii android.intent.category.LAUNCHER.
Po uruchomieniu aplikacji Androida zostaje ona wczytana przez urzdzenie i jest odczytywany
plik AndroidManifest.xml. Zostaj wyszukane i uruchomione aktywnoci posiadajce filtr intencji, ktry skada si z aktywnoci MAIN oraz kategorii LAUNCHER, tak jak pokazano poniej.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Po odnalezieniu waciwej aktywnoci urzdzenie musi powiza j z rzeczywist klas. Dokonuje tego poprzez poczenie nazwy gwnego pakietu z nazw aktywnoci, w naszym przypadku bdzie to com.example.android.notepad.NotesList (listing 2.1).
Listing 2.1. Plik AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.notepad"
>
<application android:icon="@drawable/app_notes"
android:label="@string/app_name"
>
<provider android:name="NotePadProvider"
android:authorities="com.google.provider.NotePad"
/>
<activity android:name="NotesList" android:label="@string/title_notes_list">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
...
</manifest>
Nazwa gwnego pakietu aplikacji jest zdefiniowana jako atrybut elementu <manifest> w pliku
AndroidManifest.xml, a kada aktywno posiada atrybut nazwy.
Po okreleniu pocztkowej aktywnoci zostaje ona uruchomiona. Nastpuje rwnie wywoanie
metody onCreate(). Przyjrzyjmy si elementowi NotesList.onCreate(), przedstawionemu
na listingu 2.2.
73
Aktywnoci w Androidzie s przewanie uruchamiane przez intencje, a take przez inne aktywnoci. Metoda onCreate() sprawdza, czy intencja biecej aktywnoci zawiera dane (notatki).
Jeeli nie zawiera, zostaje ustanowiony identyfikator URI, dziki ktremu zostaj pobrane dane.
W rozdziale 4. zademonstrujemy, e Android uzyskuje dostp do danych poprzez dostawcw
treci korzystajcych z identyfikatorw URI. W tym przypadku identyfikator URI dostarcza
wystarczajco wiele informacji, eby pobra dane z bazy danych. Staa Notes.CONTENT_URI jest
zdefiniowana jako element static final w pliku Notepad.java, na przykad w taki sposb:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes");
Klasa Notes znajduje si wewntrz klasy Notepad. Na razie wystarczy wiedzie, e przedstawiony
powyej identyfikator URI sprawia, e dostawca treci pobiera wszystkie notatki. Gdyby identyfikator ten wyglda nastpujco:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes/11");
to uywany dostawca treci zwrciby notatk posiadajc identyfikator o wartoci 11. Temat
dostawcw treci oraz identyfikatorw URI zostanie poruszony w rozdziale 4.
Klasa NotesList jest dopenieniem klasy ListActivity, definiujcej sposb wywietlania
danych w postaci listy. Skadniki listy s zarzdzane poprzez wewntrzn klas ListView
(element interfejsu UI), wywietlajc notatki w oknie listy. Po wstawieniu identyfikatora
URI do intencji danej aktywnoci aktywno ta zgasza gotowo do zbudowania kontekstowego menu dla notatek. Jeeli Czytelnik stara si pozna t aplikacj, zauway zapewne,
e w zalenoci od wybranego elementu wywietlane jest menu kontekstowe. Jeli na przykad zostanie zaznaczona notatka, zostan wywietlone opcje Edit note oraz Edit title. Jeeli
notatka nie zostanie zaznaczona, dostpna bdzie opcja Add note.
74
Typ danych
Opis
URI
Uri
projection
String[]
selection
String
selectionArgs
String[]
sortOrder
String
Elementy managedQuery() oraz bliniaczy query() omwimy w dalszej czci tego podrozdziau
oraz w rozdziale 4. Na razie istotna jest informacja, e kwerendy w Androidzie zwracaj dane tabelaryczne. Parametr projection pozwala okreli interesujce nas kolumny. Mona take
ograniczy wynikowy zestaw oraz posortowa go za pomoc klauzul sortowania, uywanych
w jzyku SQL (na przykad asc lub desc). Naley zauway take, e kwerenda w Androidzie
musi zwrci kolumn o nazwie _ID, eby mc obsugiwa wywietlanie pojedynczych rekordw. Ponadto naley zna typ danych zwracanych przez dostawc treci czy kolumna
zawiera dane typu string, int, binary i tak dalej.
Po wykonaniu kwerendy zwrcony kursor jest przekazywany konstruktorowi elementu
SimpleCursorAdapter, przeksztacajcemu rekordy zestawu danych w elementy interfejsu
uytkownika (ListView). Przyjrzyjmy si bliej parametrom przekazywanym do konstruktora elementu SimpleCursorAdapter:
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(this, R.layout.noteslist_item,
cursor, new String[] { Notes.TITLE }, new int[] { android.R.id.text1 });
75
Przyjrzyjmy si kolejnej koncepcji Androida, o ktrej wspomnielimy nieco wczeniej: metodzie onListItemClick() w klasie NotesList (listing 2.3).
Listing 2.3. Metoda onListItemClick
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
String action = getIntent().getAction();
if (Intent.ACTION_PICK.equals(action) ||
Intent.ACTION_GET_CONTENT.equals(action)) {
setResult(RESULT_OK, new Intent().setData(uri));
} else {
startActivity(new Intent(Intent.ACTION_EDIT, uri));
}
}
76
Identyfikatory URI treci zawsze przybieraj nastpujc form: content://, nastpnie uprawnienie (AUTHORITY), a na kocu segment oglny (zaleny od kontekstu). Poniewa identyfikator
URI nie zawiera rzeczywistych informacji, w jaki sposb musi wpywa na wykonanie kodu
generujcego dane. Jaki jest zwizek pomidzy tym identyfikatorem a kodem? W jaki sposb
odniesienie URI wpywa na kod produkujcy informacje? Czy identyfikator URI jest usug
HTTP lub sieciow? Okazuje si, e identyfikator URI, a dokadniej jego cz zwizana
z uprawnieniami, jest skonfigurowany w pliku AndroidManifest.xml jako dostawca treci, na
przykad nastpujco:
<provider android:name="NotePadProvider"
android:authorities="com.google.provider.NotePad"/>
Kiedy Android trafi na identyfikator URI, ktry naley przeanalizowa, odczytuje jego cz
zwizan z uprawnieniami i sprawdza klas ContentProvider skonfigurowan dla tych uprawnie. Aplikacja Notepad posiada klas NotePadProvider, umieszczon w pliku AndroidManifest.xml, skonfigurowan dla uprawnienia com.google.provider.NotePad. Na listingu
2.4 zosta przedstawiony niewielki wycinek tej klasy.
Listing 2.4. Klasa NotePadProvider
public class NotePadProvider extends ContentProvider
{
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs,String sortOrder) {}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {}
@Override
public String getType(Uri uri) {}
@Override
public boolean onCreate() {}
77
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db,
int oldVersion, int newVersion) {
//
}
}
}
//
}
Jak zostao przedstawione na listingu 2.5, metoda onCreate() generuje tabel aplikacji Notepad. Naley zwrci uwag, e konstruktor klasy wywouje konstruktor superklasy za pomoc
nazwy tabeli. Superklasa wywoa metod onCreate() jedynie w wypadku, gdy taka tabela nie
istnieje w bazie danych. Warto rwnie zauway, e jedn z kolumn w tabeli aplikacji Notepad
jest _ID, omwiona kilka stron wczeniej.
78
insert(),
przedstawionej na li-
79
Koncepcja cyklu ycia aplikacji jest logiczna, jednak podstawowa struktura aplikacji systemu
Android komplikuje spraw. Gwoli cisoci, architektura aplikacji jest zorientowana na skadniki oraz integracj. Pozwala to na wzbogacenie dozna uytkownika, moliwo bezproblemowego wielokrotnego korzystania z aplikacji oraz atwo jej integracji, jednak przed menederem cyklu ycia stoi bardzo skomplikowane zadanie.
Rozwamy typowy scenariusz. Uytkownik rozmawia z kim przez telefon i musi otworzy
wiadomo e-mail, eby odpowiedzie na zadane przez rozmwc pytanie. Przechodzi do ekranu gwnego, otwiera aplikacj pocztow, klika adres cza do witryny zawierajcej poszukiwan wiadomo i przytacza jej fragment ze strony internetowej. W takim przypadku wymagane s cztery aplikacje: ekranu gwnego, telefonu, pocztowa oraz przegldarka. Uytkownik
w sposb cigy moe zmienia te aplikacje, jednak w tle system zapisuje oraz przywraca ich stan.
Przykadowo po klikniciu adresu cza w wiadomoci e-mail system zapisuje metadane uruchomionej aktywnoci tej wiadomoci, zanim przekae aktywnoci przegldarki dane potrzebne
do przekierowania na adres URL. Tak naprawd system zapisuje metadane kadej aktywnoci
przed uruchomieniem nastpnej, dziki czemu moe do niej wrci (na przykad gdy uytkownik wraca do poprzedniej strony). Jeeli wystpi problem z iloci pamici, zostanie zamknity
proces wykonujcy aktywno, a w razie koniecznoci zostanie wznowiony.
System Android jest wraliwy na cykl ycia aplikacji oraz jej elementw skadowych. Zatem eby
stworzy stabiln aplikacj, naley zrozumie zdarzenia cyklu ycia oraz nauczy si nimi posugiwa. Procesy korzystajce z danej aplikacji oraz jej skadnikw natrafiaj na rnorodne
zdarzenia cyklu ycia i istnieje moliwo zaimplementowania wywoa zwrotnych, zajmujcych
si zmianami ich stanu. Na pocztek warto zapozna si z wywoaniami cyklu ycia aktywnoci
(listing 2.7).
Listing 2.7. Metody cyklu ycia aktywnoci
protected
protected
protected
protected
protected
protected
protected
void
void
void
void
void
void
void
onCreate(Bundle savedInstanceState);
onStart();
onRestart();
onResume();
onPause();
onStop();
onDestroy();
Na listingu 2.7 zostay wypisane metody, ktre s wywoywane podczas cyklu ycia aktywnoci.
Dla stworzenia stabilnej struktury aplikacji istotne jest zrozumienie, kiedy dana metoda jest
wywoywana przez system. Nie wszystkie metody musz by implementowane. Jeeli zostan uyte
wszystkie wywoania, naley rwnie stworzy analogiczne wersje dla superklas. Na rysunku
2.15 zostay pokazane przejcia pomidzy stanami aktywnoci.
System moe uruchamia oraz zatrzymywa aktywnoci w zalenoci od tego, co si w nim
dzieje. Metoda onCreate() jest wywoywana podczas pierwszego utworzenia aktywnoci.
Po tej metodzie zawsze pojawia si metoda onStart(), jednak wywoanie metody onCreate()
nie zawsze nastpuje przed wywoaniem onCreate(), gdy metoda ta moe zosta wywoana
w przypadku zatrzymania aplikacji. Po wywoaniu metody onStart() aktywno nie jest jeszcze
dostpna dla uytkownika. Po metodzie onStart() wywoywana jest metoda onResume(),
w momencie gdy aktywno znajduje si na pierwszym planie i jest dostpna dla uytkownika.
To wanie teraz uytkownik moe bezporednio korzysta z aplikacji.
80
81
// zmienna globalna
private static final String myGlobalVariable;
@Override
public void onCreate()
{
super.onCreate();
Do tej pory omwilimy podstawy tworzenia aplikacji w systemie Android, uruchamianie programu na emulatorze, ogln budow aplikacji oraz kilka najpowszechniejszych funkcji spotykanych w tych programach. Nie pokazalimy jednak, w jaki sposb naley rozwizywa problemy
pojawiajce si podczas pisania aplikacji. W ostatnim podrozdziale omwimy usuwanie bdw
z programu.
Rysunek 2.16. Narzdzia do usuwania bdw, ktre mona wykorzysta podczas tworzenia aplikacji
Jednym z takich narzdzi jest LogCat. Aplikacja ta wywietla komunikaty dziennika tworzone
podczas korzystania z klas android.util.Log, System.out.println, wyjtkw i tak dalej.
Podczas gdy klasa System.out.println dziaa i informacje s wywietlane w oknie LogCat,
82
83
gdzie port# jest numerem portu, na ktrym nasuchuje emulator. Warto tego parametru jest
zazwyczaj podana w pasku tytuowym emulatora i czsto wynosi ona 5554. Po uruchomieniu
konsoli emulatora moemy wpisywa polecenia pozwalajce na symulowanie zdarze zwizanych z systemem GPS, wiadomociami SMS, a nawet na zmian sieci i poziomu naadowania baterii.
Uruchamianie emulatora
Pokazalimy wczeniej, w jaki sposb mona uruchomi emulator z poziomu projektu w rodowisku Eclipse. W wikszoci przypadkw chcemy najpierw wczy emulator, a nastpnie wdroy i przetestowa aplikacj w ju uruchomionym emulatorze. Aby go uruchomi w dowolnym
momencie, musimy najpierw przej do narzdzia Android SDK and AVD Manager, albo uruchamiajc je bezporednio w katalogu tools pakietu Android SDK, albo wybierajc je w oknie
Window rodowiska Eclipse. Gdy ju uruchomimy meneder, klikamy zakadk Virtual devices,
widoczn w panelu po lewej stronie, wybieramy waciwe urzdzenie AVD z listy w prawym
oknie i klikamy przycisk Start.
Po jego wciniciu pojawi si okno dialogowe Launch Options (rysunek 2.17). Moemy w nim
definiowa rozmiar okna emulatora oraz zmienia opcje jego rozruchu i zamykania. Podczas
pracy z urzdzeniami AVD imitujcymi urzdzenia posiadajce mae lub rednie wywietlacze
bdziemy czsto ogranicza si do domylnego rozmiaru ekranu. Jednak w przypadku duych
i bardzo duych rozmiarw ekranu, na przykad takich jak w tabletach, domylne wymiary
wywietlacza mog nie pasowa do rozmiaru monitora stacji roboczej. W takim przypadku
moemy zaznaczy opcj Scale display to real size (skaluj wywietlacz do rzeczywistego rozmiaru) i wstawi odpowiedni warto. Nazwa tej opcji moe by nieco mylca, poniewa tablety
mog posiada inne gstoci wywietlacza od stacji roboczej, natomiast emulator nie potrafi
dokadnie odwzorowa fizycznych parametrw wywietlacza na ekranie monitora. Przykadowo na mojej stacji roboczej, podczas symulowania tabletu obsugujcego platform Honeycomb
84
StrictMode
Wraz z wydaniem Androida w wersji 2.3 zostaa wprowadzona nowa funkcja debugowania,
nazwana StrictMode. Opcja ta wedug firmy Google zostaa wykorzystana do wprowadzenia setek usprawnie w aplikacjach tej firmy stworzonych z myl o tym systemie. Do
czego wic waciwie ona suy? Bdzie powiadamiaa o naruszeniach zasad powizanych
z wtkami oraz wirtualn maszyn. Po wykryciu naruszenia zasad funkcja wywietli alert
z odniesieniem do stosu, w ktrym znajdowaa si aplikacja w momencie naruszenia zabezpiecze. Za pomoc alertu mona wymusi zamknicie aplikacji lub jedynie zapisa tre
alertu w dzienniku i pozwoli aplikacji na dalsze dziaanie. Obecnie trudno okreli szczegy
wspomnianych zasad, spodziewamy si te, e firma Google bdzie dodawaa kolejne zasady
wraz z rozwojem Androida.
85
Obecnie s dostpne dwa rodzaje zasad w obrbie funkcji StrictMode. Pierwszy z nich jest
zwizany z wtkami i jego podstawowym zadaniem jest wsppraca z gwnym wtkiem (zwanym rwnie wtkiem interfejsu uytkownika). Prowadzenie zapisu oraz odczytu danych dyskowych w obrbie gwnego wtku nie jest dobrym rozwizaniem, podobnie jak uzyskiwanie
za jego pomoc dostpu do sieci. Firma Google dodaa punkty zaczepienia funkcji StrictMode
do kodu odpowiedzialnego za operacje zapisu-odczytu oraz operacje sieciowe. Jeeli uruchomimy funkcj StrictMode w jednym z wtkw, ktry prbuje uzyska dostp do przestrzeni
dyskowej lub sieci, zostaniemy o tym powiadomieni. Musimy wybra, ktre aspekty zasad
ThreadPolicy spowoduj wywoanie alertu, oraz rodzaj alertu. Wrd narusze zasad, na ktre
moemy zwraca uwag, znajdziemy takie jak niestandardowo powolne wywoania, odczyty
danych z dysku, zapisy danych na dysku oraz dostp do sieci. Spord rodzajw alertw mamy
do wyboru zapis komunikatu w narzdziu LogCat, wywietlenie okna dialogowego, bynicie
wywietlacza, zapis komunikatu w pliku dziennika DropBox lub zawieszenie dziaania aplikacji.
Najczciej spotykamy si z zapisywaniem informacji w narzdziu LogCat oraz zawieszeniem
dziaania aplikacji. Na listingu 2.9 przedstawiono przykadowy sposb konfigurowania funkcji
StrictMode pod ktem zasad dotyczcych wtkw.
Listing 2.9. Konfigurowanie zasad ThreadPolicy funkcji StrictMode
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build());
Zwrmy uwag, e za pomoc klasy Builder mona w naprawd prosty sposb ustanowi
funkcj StrictMode. Wszystkie metody tej klasy, definiujce zasady, zwracaj odniesienie do
obiektu Builder, zatem mona stworzy z nich acuch, podobnie jak na listingu 2.9. Ostatnia
z wywoywanych metod, build(), zwraca obiekt ThreadPolicy, ktry jest argumentem oczekiwanym przez metod setThreadPolicy() funkcji StrictMode. Zwrmy uwag, e metoda
setThreadPolicy() jest statyczna, zatem tak naprawd nie musimy tworzy obiektu StrictMode.
Metoda setThreadPolicy() bada biecy wtek pod ktem zasad, zatem wszystkie nastpne
dziaania wtku zostan porwnane z obiektem ThreadPolicy i w razie potrzeby zostanie wywietlony alert. W powyszym kodzie zasady s tak zdefiniowane, e alert zostanie wygenerowany w przypadku odczytywania i zapisywania danych dyskowych oraz dostpu do sieci
i zostanie on zapisany w dzienniku LogCat. Zamiast wypisywania poszczeglnych metod
wykrywania moemy zastosowa metod detectAll(). Nie ma rwnie przeszkd, by dodawa lub wymienia metody odpowiedzialne za ostrzeenia. Na przykad moemy wprowadzi metod penaltyDeath(), ktra spowoduje zawieszenie dziaania aplikacji zaraz po zapisaniu komunikatu w narzdziu LogCat (z kolei za to zdarzenie jest odpowiedzialna metoda
penaltyLog()).
Poniewa ten rodzaj funkcji StrictMode dotyczy wtku, dla danego wtku funkcja ta uruchamia si jednorazowo. Z tego powodu mona uruchomi funkcj StrictMode na pocztku
metody onCreate(), przypisanej do gwnej aktywnoci, dziaajcej w gwnym wtku, i od tego
momentu funkcja ta ledziaby wszystkie dziaania przeprowadzane w tym wtku. W zalenoci
od rodzaju poszukiwanego naruszenia pierwsza aktywno moe do szybko uruchomi funkcj StrictMode. Moemy j rwnie uruchomi dla aplikacji poprzez rozszerzenie klasy
Application i dodanie konfiguracji funkcji StrictMode do metody onCreate() caej aplikacji.
86
Potencjalnie kady element obecny w wtku moe uruchomi funkcj StrictMode, zdecydowanie jednak nie musimy wywoywa kodu konfiguracyjnego we wszystkich miejscach; jeden raz
cakowicie wystarczy.
Analogicznie do zasad ThreadPolicy, funkcja StrictMode zawiera zasady VmPolicy. Su one
do sprawdzania wyciekw pamici, w przypadku gdy obiekt bazy SQLite lub dowolny inny
obiekt typu Closeable zostanie zakoczony przed zamkniciem. Zasady VmPolicy s tworzone
w podobny sposb za pomoc klasy Builder, co zostao przedstawione na listingu 2.10. Jedyna
rnica pomidzy zasadami ThreadPolicy a VmPolicy polega na niemonoci wywietlenia
alertu jako okna dialogowego w tym drugim przypadku.
Listing 2.10. Konfigurowanie zasad VmPolicy funkcji StrictMode
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDeath()
.build());
Poniewa proces konfiguracji jest przeprowadzany w wtku, funkcja StrictMode bdzie wykrywaa naruszenia nawet w przypadku kontrolnego przepywu pomidzy obiektami. Po
wystpieniu naruszenia zasad bezpieczestwa moemy si zdziwi, gdy zauwaymy, e kod
jest cigle przetwarzany w gwnym wtku, ale otrzymujemy te lad stosu, dziki ktremu
moemy odkry, co si stao. Nastpnie mona sprbowa rozwiza problem poprzez przeniesienie danego fragmentu kodu do osobnego wtku. Mona rwnie pozostawi kod bez
zmian. Wszystko zaley od programisty. Oczywicie, najprawdopodobniej bdzie trzeba wyczy funkcj StrictMode tu przed wydaniem aplikacji na rynek; nie byoby korzystne, aby
programy zawieszay si uytkownikom z powodu alertw.
Istnieje kilka sposobw wyczenia funkcji StrictMode przed wdroeniem aplikacji do uytkowania. Najprostszym rozwizaniem jest usunicie wywoa, jednak w pniejszych etapach staje
si ono coraz bardziej skomplikowane. Mona rwnie wprowadzi logik dwuwartociow na
poziomie aplikacji i przeprowadzi test przed wywoaniem kodu funkcji StrictMode. W takim
przypadku ustanowienie wartoci false tu przed okazaniem aplikacji wiatu w skuteczny
sposb wyczyoby t funkcj. Bardziej eleganckim rozwizaniem jest wykorzystanie trybu
debugowania aplikacji, zdefiniowanego w pliku AndroidManifest.xml. Jednym z atrybutw
znacznika <application> w tym pliku jest android:debuggable. Jego warto mona ustawi
jako true w trakcie debugowania aplikacji, w wyniku czego na obiekcie ApplicationInfo
zostaje ustanowiona flaga, co mona nastpnie odczyta w kodzie. Na listingu 2.11 pokazano,
w jaki sposb mona skorzysta z tej informacji, aby w trybie debugowania aplikacja posiadaa
aktywn funkcj StrictMode (a jeli aplikacja nie bdzie w trybie debugowania, funkcja ta
zostanie zdezaktywowana).
Listing 2.11. Ustanawianie funkcji StrictMode wycznie w trybie debugowania
// Wraca tutaj, jeli aplikacja nie znajduje si w trybie debugowania
ApplicationInfo appInfo = context.getApplicationInfo();
int appFlags = appInfo.flags;
if ((appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
87
Podczas pisania aplikacji w rodowisku Eclipse wtyczka ADT automatycznie ustanawia atrybut
debugowania, co stanowi spore uatwienie. W trakcie wdraania aplikacji ze rodowiska Eclipse do emulatora lub bezporednio do urzdzenia fizycznego atrybut ten otrzyma warto
true, co spowodowaoby uruchomienie kodu funkcji StrictMode w powyszym kodzie.
Podczas eksportowania aplikacji do wersji przeznaczonej do uytkowania warto atrybutu
debuggable zostanie zmieniona na false. Naley jednak pamita, e po rcznej zmianie
tego atrybutu nie bdzie on automatycznie modyfikowany.
Wszystko to brzmi bardzo adnie i elegancko, ale nie zadziaa na wersji Androida starszej od
2.3. Aby mc jawnie uywa funkcji StrictMode, musimy wykorzystywa rodowisko obsugujce Androida w wersji 2.3 lub nowsze. W przypadku wprowadzenia powyszych kodw
do rodowiska starszego od wersji 2.3 zaczn powstawa bdy weryfikacji, poniewa ta klasa po
prostu nie istnieje w tym rodowisku.
Aby wykorzystywa funkcj StrictMode w starszych wersjach Androida (do wersji 2.3), naley
zastosowa mechanizm refleksji. Dziki temu mona wywoa metody tej funkcji w sposb
poredni, jeli s dostpne. Jeli nie s dostpne, to moesz ponie sromotn porak. Najprostsze rozwizanie zostao przedstawione na listingu 2.12; wywoujemy specjaln metod,
stworzon wycznie dla starszych wersji Androida.
Listing 2.12. Wykorzystanie funkcji StrictMode za pomoc refleksji
try {
Class sMode = Class.forName("android.os.StrictMode");
Method enableDefaults = sMode.getMethod("enableDefaults");
enableDefaults.invoke(null);
}
catch(Exception e) {
W ten sposb mona okreli, czy klasa StrictMode istnieje. Jeeli istnieje, nastpi wywoanie
metody enableDefaults(). Jeeli klasa ta nie zostanie znaleziona, zostaje wywoany nasz
blok catch wraz z wyjtkiem ClassNotFoundException. Jeeli funkcja StrictMode istnieje, nie
powinny pojawia si wyjtki, poniewa jedn z jej metod jest enableDefaults(). Metoda
ta sprawia, e funkcja StrictMode wyapuje wszystkie naruszenia zasad i zapisuje je w dzienniku LogCat. Poniewa ta metoda jest statyczna, pierwszy argument przyjmuje warto null
podczas jej wywoywania.
Mog si zdarza sytuacje, w ktrych zapisywanie wszystkich narusze jest niepodane. Nic nie
stoi na przeszkodzie, aby docza funkcj StrictMode do wtkw innych od gwnego, i to
wanie wtedy moemy ustanowi mniejsz liczb alertw. Dobrym przykadem byoby monitorowanie wtku, ktry suy do odczytu danych. W takim przypadku moemy albo nie wywoywa metody detectDiskReads() w obiekcie Builder, albo wywoa metod detectAll(), a nastpnie permitDiskReads() w tym obiekcie. Istniej rwnie analogiczne metody zezwole
dla pozostaych opcji zasad. Ale gdybymy chcieli dokona czego podobnego w wersjach
Androida starszych od 2.3, to czy istnieje na to jaki sposb? Oczywicie, e tak!
Jeeli funkcja StrictMode nie jest dostpna dla danej aplikacji, w przypadku prby jej aktywowania zostanie wywietlony komunikat o bdzie VerifyError. Jeli umiecimy t funkcj
w klasie i nastpnie otrzymamy taki komunikat o bdzie, nie musimy si przejmowa, gdy nie
88
bdzie ona dostpna, a gdy bdzie dostpna wykorzystamy j. Na listingu 2.13 widzimy
przykadow klas StrictModeWrapper, ktr mona doda do aplikacji, natomiast listing
2.14 przedstawia kod wewntrz aplikacji sucy do konfigurowania funkcji StrictMode.
Listing 2.13. Stosowanie funkcji StrictMode w Androidzie starszym od wersji 2.3
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.StrictMode;
public class StrictModeWrapper {
public static void init(Context context) {
Jak wida, mamy tu do czynienia z takim samym kodem jak wczeniej, tutaj jednak czymy
w cao wszystkie zdobyte wczeniej informacje. Wreszcie, aby skonfigurowa funkcj
StrictMode w aplikacji, wystarczy doda do niej kod widoczny na listingu 2.14:
Listing 2.14. Wywoywanie funkcji StrictMode w Androidzie starszym od wersji 2.3
try {
StrictModeWrapper.init(this);
}
catch(Throwable throwable) {
Log.v("StrictMode", "... jest nieosigalna. Zaniechanie...");
}
Zwrmy uwag, e this jest lokalnym kontekstem dowolnego obiektu, ktrym si zajmujemy,
na przykad z wntrza metody onCreate() bdcej czci gwnej aktywnoci. Kod z listingu
2.14 bdzie dziaa z dowoln wersj systemu Android.
W ramach wiczenia Czytelnik moe uruchomi rodowisko Eclipse i stworzy kopi aplikacji
Notepad, wygenerowanej we wczeniejszej czci rozdziau. Nastpnie mona doda now
klas w katalogu src, wykorzystujc kod z listingu 2.13. Wewntrz metody onCreate() w pliku
NotesList.java naley teraz doda taki kod jak na listingu 2.14, po czym uruchomi program
na emulatorze obsugujcym Androida w wersji starszej od 2.3. To samo mona nastpnie
sprawdzi dla wersji 2.3 lub pniejszej. Jeeli funkcja StrictMode bdzie niedostpna, w oknie
89
LogCat pojawi si informacja o jej braku, jednak aplikacja powinna dalej nieprzerwanie
dziaa. W przypadku obecnoci funkcji StrictMode w oknie LogCat powinny od czasu do czasu
pojawia si informacje o naruszeniach zasad podczas korzystania z aplikacji Notepad.
Odnoniki
Poniej przedstawiamy pomocne odnoniki do tematw, ktre Czytelnik moe zechcie pozna
dokadniej.
http://developer.motorola.com/docstools/ jest witryn firmy Motorola, na ktrej
mona znale dodatki do urzdze oraz inne narzdzia programistyczne przystosowane
do mikrotelefonw tego producenta, w tym takie jak MOTODEV Studio alternatyw
dla rodowiska Eclipse.
http://developer.htc.com/ to witryna firmy HTC przeznaczona dla programistw
w systemie Android.
http://innovator.samsungmobile.com/platform.main.do?platformId=1 stanowi stron
firmy Samsung dla programistw w systemie Android, na ktrej mona znale dodatek
zestawu Android SDK dla tabletu Samsung Galaxy Tab.
http://developer.android.com/guide/developing/tools/index.html zawiera dokumentacj
programistyczn dla uprzednio opisanych narzdzi debugujcych.
http://appinventor.googlelabs.com/about/index.html jest stron rodowiska App
Inventor, kolejnej alternatywy sucej do tworzenia aplikacji dla systemu Android.
Za stworzenie tego rodowiska odpowiada firma Google Labs i jest ono przeznaczone
dla osb niebdcych programistami. Aplikacje s tutaj tworzone w sposb graficzny,
podobnie jak logika stojca za interfejsem uytkownika.
http://code.google.com/p/android-ui-utils/ zawiera cza do uytecznych narzdzi,
takich jak Android Asset Studio, ktre jest aplikacj sieciow suc do tworzenia
rnorodnych rodzajw ikon dla systemu Android. Warto zwrci uwag, e do
obsugi aplikacji Android Asset Studio wymagane jest uruchomienie przegldarki
Google Chrome.
http://www.droiddraw.org/ narzdzie do projektowania interfejsw uytkownika,
w ktrym do tworzenia ukadw graficznych jest wykorzystywana funkcja przecigania.
Podsumowanie
W tym rozdziale zademonstrowalimy, w jaki sposb naley skonfigurowa rodowisko
projektowe do tworzenia aplikacji dla systemu Android. Opisalimy podstawowe elementy
budulcowe interfejsu API Androida, a take wprowadzilimy pojcia widokw, aktywnoci,
intencji, dostawcw treci oraz usug. W dalszej czci przeanalizowalimy struktur aplikacji
Notepad pod ktem wspomnianych ju blokw budulcowych oraz skadnikw aplikacji. Nastpnie omwilimy istot cyklu ycia aplikacji pisanych na Androida. Na kocu wspomnielimy
o narzdziach do usuwania bdw zaimplementowanych w zestawie Android SDK, zintegrowanych ze rodowiskiem Eclipse.
A teraz wprowadzimy podstawy projektowania dla Androida. Nastpny rozdzia zosta powicony zasobom.
90
R OZDZIA
3
Korzystanie z zasobw
Zasoby
Zasoby odgrywaj kluczow rol w architekturze Androida. W Androidzie zasobem moe by plik (na przykad plik muzyczny) lub warto (przykadowo nazwa
okna dialogowego), powizane z wykonywaln aplikacj. Te pliki i wartoci s z ni
powizane w sposb umoliwiajcy ich modyfikowanie bez koniecznoci ponownego kompilowania aplikacji.
Znanymi Czytelnikowi rodzajami zasobw s cigi znakw, kolory oraz mapy bitowe.
Zamiast umieszcza na przykad cigi znakw w kodzie aplikacji, mona wykorzysta ich identyfikatory. W ten sposb moliwe staje si zmienianie tekstu w zasobie
bez potrzeby ingerowania w kod rdowy.
Istnieje bardzo wiele rnorodnych rodzajw zasobw w Androidzie. W tym rozdziale postaramy si omwi wikszo z nich. Rozpocznijmy od przedyskutowania
bardzo powszechnego rodzaju zasobw: cigu znakw.
92
lub
<resources xmlns="domylna przestrze nazw" >
Po utworzeniu lub zaktualizowaniu takiego pliku narzdzie ADT automatycznie utworzy lub
zaktualizuje klas Java, umieszczon w gwnym pakiecie aplikacji nazwanym R.java, o unikalne
identyfikatory dwch widocznych na listingu cigw znakw.
Zwrmy uwag na lokalizacj pliku R.java w poniszym przykadzie. Utworzylimy wysokopoziomow struktur katalogw dla projektu nazwanego MyProject:
\MyProject
\src
\com\mycompany\android\my-root-package
\com\mycompany\android\my-root-package\another-package
\gen
\com\mycompany\android\my-root-package\R.java
\assets
\res
\AndroidManifest.xml
...itd.
Bez wzgldu na liczb plikw zasobw istnieje tylko jeden plik R.java.
Plik R.java zaktualizowany o zasoby z listingu 3.1 zostaby wzbogacony o wpisy widoczne na listingu 3.2:
93
Przede wszystkim naley zwrci uwag, w jaki sposb zdefiniowano szczytow klas gwnego
pakietu w pliku R.java: public static final class R. W tej zewntrznej klasie R Android
definiuje klas wewntrzn, dokadniej static final class string. Klasa ta suy plikowi
R.java jako przestrze nazw do przechowywania identyfikatorw zasobw typu string.
Dwie klasy static final ints, okrelone nazwami zmiennych hello oraz app_name, s identyfikatorami zasobw reprezentujcymi odpowiednie zasoby typu string. Mona stosowa
te identyfikatory w dowolnym miejscu kodu rdowego za pomoc nastpujcej struktury:
R.string.hello
Naley zwrci uwag, e te wygenerowane identyfikatory wskazuj typ danych int, a nie
string. Wikszo metod korzystajcych z cigw znakw uznaje take identyfikatory zasobw
za dane wejciowe. W razie koniecznoci Android przeksztaci dane int w dane typu string.
Jest jedynie kwesti ustalonej konwencji, e wikszo przykadowych aplikacji zestawu Android
SDK definiuje cigi znakw w jednym pliku strings.xml. Android radzi sobie z dowoln liczb
takich plikw, pod warunkiem e ich struktura wyglda tak, jak przedstawiono na listingu 3.1,
oraz e znajduj si w podkatalogu /res/values.
Mona atwo przeledzi struktur takiego pliku. Obecny jest gwny wze <resources>, pod
ktrym umieszczane s podrzdne elementy <string>. Kady element <string> lub wze
posiada waciwo name, ktra staje si atrybutem id w pliku R.java.
eby si przekona, co si dzieje z plikami zasobw typu string w tym podkatalogu, mona
w nim umieci plik zawierajcy kod pokazany poniej i nazwa go strings1.xml (listing 3.3):
Listing 3.3. Przykad dodatkowego pliku strings1.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello1">Witaj 1</string>
<string name="app_name1">Witaj, nazwo aplikacji 1</string>
</resources>
94
Plik ukadu graficznego zaprezentowany na listingu 3.5 definiuje gwny wze, nazwany
LinearLayout, w ktrym umieszczony jest element TextView, a po nim Button. Wze ten
rozmieszcza elementy podrzdne w pionie lub w poziomie w tym przypadku w pionie.
95
Dla kadego ekranu (lub aktywnoci) trzeba definiowa oddzielne pliki ukadu graficznego.
Gwoli cisoci, kady ukad graficzny wymaga oddzielnego pliku. W przypadku tworzenia
dwch ekranw prawdopodobnie potrzebne bd dwa pliki ukadu graficznego, na przykad
/res/layout/screen1_layout.xml oraz /res/layout/screen2_layout.xml.
Kady plik umieszczony w podkatalogu /res/layout/ generuje unikatow sta na podstawie
nazwy tego pliku (rozszerzenie zostaje pominite). W przypadku zasobw typu layout
istotna jest liczba plikw, w przypadku zasobw typu string wana jest liczba
stanowicych zasoby poszczeglnych cigw znakw, znajdujcych si
wewntrz plikw.
Jeeli na przykad w podkatalogu /res/layout/ zostay utworzone dwa pliki o nazwach file1.xml
oraz file2.xml, w pliku R.java pojawi si nastpujce wpisy (listing 3.6):
Listing 3.6. Kilka staych dla rnych plikw ukadu graficznego
public static final class layout {
...jakie inne pliki
public static final int file1=0x7f030000;
public static final int file2=0x7f030001;
}
Zdefiniowane w tych plikach widoki, na przykad TextView (listing 3.5), dostpne s w kodzie
Java poprzez wygenerowane w pliku R.java identyfikatory ich zasobw:
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Wpisz tu jaki tekst");
W tym przykadzie widok TextView lokalizowany jest za pomoc metody findViewById klasy
Activity. Staa R.id.text1 nawizuje do identyfikatora zdefiniowanego dla widoku TextView.
Identyfikator ten w pliku ukadu graficznego wyglda nastpujco:
<TextView android:id="@+id/text1"
...
</TextView>
Warto atrybutu id wskazuje na to, e staa text1 zostanie uyta do jednoznacznego rozpoznawania tego widoku wrd innych widokw obsugiwanych przez t aktywno. Znak
+ w wyraeniu @+id/text1 oznacza, e identyfikator text1 zostanie utworzony, w przypadku
gdy jeszcze nie istnieje. Skadnia identyfikatora zasobu jest bardziej skomplikowana. Zajmiemy
si ni w nastpnym punkcie.
96
Skadnik type odnosi si do jednej z przestrzeni nazw okrelonych dla zasobw, dostpnych
w pliku R.java. Wrd nich znajduj si takie jak:
R.drawable,
R.id,
R.layout,
R.string,
R.attr,
R.plural,
R.array.
Odpowiadajcymi im typami w skadni odniesienia do zasobw XML s odpowiednio:
drawable,
id,
layout,
string,
attr,
plurals,
string-array.
Element name w skadni @[package:]type/name to nazwa nadawana zasobowi (na przykad
text1 na listingu 3.5); jest ona reprezentowana rwnie jako staa int w pliku R.java.
Jeeli nie zostanie zdefiniowany aden pakiet w skadni @[package:]type/name, to para
zostanie przetworzona na podstawie lokalnych zasobw oraz lokalnego pakietu
R.java aplikacji.
type/name
97
<TextView android:id="@android:id/text">
W skadni @+id/text symbol + posiada specjalne znaczenie. System zostaje w ten sposb poinformowany, e identyfikator text moe jeszcze nie istnie w takim przypadku naley go
utworzy oraz nada nazw text.
Element type dotyczy rodzaju zasobu w tym przypadku atrybutu id. Kiedy ten atrybut
znajduje si na miejscu, powinna dziaa definicja widoku, widoczna na listingu 3.9:
Listing 3.9. Wykorzystywanie predefiniowanego identyfikatora
<TextView android:id="@id/text">
...
</TextView>
98
Jak zostao wspomniane w tabeli 2.1 (rozdzia 2.), pliki zasobw s przechowywane w rnych
podkatalogach, w zalenoci od ich typw. Poniej wypisalimy kilka istotnych podkatalogw
wza res wraz z rodzajami przechowywanych w nich zasobw:
anim skompilowane pliki animacji;
drawable mapy bitowe;
layout definicje widoku bd interfejsu UI;
values tablice, kolory, wymiary, cigi znakw oraz style;
xml skompilowane wasne pliki XML;
raw nieskompilowane nieskompresowane pliki.
Kompilator zasobw w narzdziu AAPT kompiluje wszystkie zasoby poza znajdujcymi si
w katalogu raw i umieszcza je w kocowym pliku .apk. Plik ten zawiera kod i zasoby aplikacji.
Jest powizany z plikiem .jar rodowiska Java (skrt apk rozwija si jako Android Package,
czyli pakiet systemu Android). To wanie plik .apk jest instalowany w urzdzeniu.
99
Lokalizacja
Opis
Kolory
/res/values/any-file
Cigi znakw
/res/values/any-file
Tablice cigw
znakw
/res/values/any-file
Wielokrotnoci
/res/values/any-file
Wymiary
/res/values/any-file
Lokalizacja
Opis
Obrazy
/res/drawable/
multiple-files
Kolorowe
obiekty
rysowane
/res/values/any-file
take
/res/drawable/
multiple-files
/res/xml/*.xml
Wasne,
nieskompresow
ane zasoby
/res/raw/*.*
Wasne,
nieskompresow
ane pliki
dodatkowe
/assets/*.*/*.*
101
Po zdefiniowaniu takiego zasobu tablicy cigw znakw moemy pobra t tablic w kodzie
Java, co zostao pokazane na listingu 3.11.
Listing 3.11. Odczytywanie tablicy cigw znakw w kodzie Java
//Uzyskuje dostp do obiektu zasobw z poziomu aktywnoci
Resources res = your-activity.getResources();
String strings[] = res.getStringArray(R.array.test_array);
Wielokrotnoci
Zasb plurals skada si ze zbioru cigw znakw. Te cigi znakw stanowi rnorodne
sposoby numerycznego okrelenia liczby jakich elementw, na przykad jajek w gniedzie.
Rozwamy poniszy przykad:
Jest 1 jajko.
Zwrmy uwag, e zdania s identyczne dla liczb 0 i 100, jednak wygldaj inaczej w przypadku liczb 1 i 2. Taka odmienno zapisu zda moe zosta odwzorowana za pomoc zasobu
plurals. Na listingu 3.12 widzimy, w jaki sposb mona wewntrz pliku zasobu zaprezentowa
te trzy odmiany zdania na podstawie liczby elementw.
Listing 3.12. Definiowanie wielokrotnoci w pliku zasobw
<resources>
<plurals name="eggs_in_a_nest_text">
<item quantity="one">Jest 1 jajko.</item>
<item quantity="few1">S %d jajka.</item>
<item quantity="other">Jest %d jajek.</item>
</plurals>
</resources>
Zauwamy, w jaki sposb te trzy odmiany zostay zdefiniowane jako pi elementw jednej
wielokrotnoci. Teraz moemy wykorzysta pokazany na listingu 3.13 kod Java do wywietlenia cigu znakw odnoszcego si do liczby jakich elementw, o ktrych mowa w zdaniu. Pierwszym parametrem metody getQuantityString() jest identyfikator zasobu wielokrotnoci. Za pomoc drugiego parametru wybieramy potrzebny cig znakw. Jeeli
warto liczby elementw wynosi 1, 2, 3 lub 4, nie modyfikujemy cigu znakw w aden
sposb. Jeeli ta warto bdzie inna, naley wprowadzi trzeci parametr, ktrego warto
bdzie zastpowaa zmienn %d. Za kadym razem, gdy bdziemy chcieli formatowa cigi
znakw w zasobie wielokrotnoci, wymagane bd przynajmniej te trzy parametry.
Listing 3.13. Wywietlanie cigw znakw zawartych w zasobie wielokrotnoci
Resources
String s1
String s2
String s3
String s4
res = your-activity.getResources();
= res.getQuantityString(R.plurals.eggs_in_a_nest_text,
= res.getQuantityString(R.plurals.eggs_in_a_nest_text,
= res.getQuantityString(R.plurals.eggs_in_a_nest_text,
= res.getQuantityString(R.plurals.eggs_in_a_nest_text,
0,0);
1,1);
2,2);
10,10);
Dziki temu fragmentowi kodu podanie dowolnej wartoci jako liczby elementw spowoduje wywietlenie odpowiedniego cigu znakw, stanowicego zdanie we waciwej formie
gramatycznej.
Czy istniej jednak jakie inne zastosowania atrybutu quantity wystpujcego w wle item?
eby zrozumie zastosowanie tych zasobw, zalecamy przejrzenie kodw rdowych plikw
Resources.java i PluralRules.java, ktre s dostpne w kodzie systemu Android. Wrd zamieszczonych na kocu rozdziau odnonikw mona znale odniesienia do wycigw z tych
plikw rdowych.
Warto few odnosi si do gramatyki jzyka polskiego, gdzie oddzieln odmian uzyskuj zdania
zawierajce cyfry 2, 3, 4 oraz wszelkie liczby, ktre kocz si cyframi 2, 3, 4 (za wyjtkiem cyfr 12,
13, 14) przyp. tum.
103
string
Naley zauway, e cytowane cigi znakw musz zosta wstawione pomidzy znaki cytowania
lub zacytowane w alternatywny sposb. Definicje typu string pozwalaj take na stosowanie
standardowych sekwencji formatowania w jzyku Java.
Android dopuszcza rwnie stosowanie wewntrz wza XML <string> takich elementw, jak
<b> czy <i>, oraz innych prostych znacznikw formatowania tekstu w jzyku HTML. Mona
utworzy taki zoony cig znakw do sformatowania tekstu przed jego wstawieniem do
widoku tekstu.
Kada z tych metod zostaa zaprezentowana na listingu 3.15.
Listing 3.15. Stosowanie zasobw typu string w kodzie Java
//Odczytuje prosty cig znakw i wstawia go do widoku tekstu
String simpleString = activity.getString(R.string.simple_string);
textView.setText(simpleString);
Po zdefiniowaniu cigw znakw jako zasobu mona umieci go bezporednio w takim widoku, jak TextView, w definicji ukadu graficznego XML tego widoku. Na listingu 3.16 zosta pokazany przykad, w ktrym cig znakw sformatowany za pomoc znacznikw HTML jest
skonfigurowany jako zawarto tekstowa widoku TextView.
Listing 3.16. Stosowanie zasobw typu string w jzyku XML
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAlign="center"
android:text="@string/tagged_string"/>
105
Android definiuje rwnie podstawowy zestaw kolorw we wasnych plikach zasobw. Ich
identyfikatory dostpne s w przestrzeni nazw android.R.color. Lista staych kolorw dostpnych w przestrzeni nazw android.R.color zostaa umieszczona pod nastpujcym adresem (w jzyku angielskim):
http://developer.android.com/reference/android/R.color.html
Listing 3.17 prezentuje przykady okrelania koloru w pliku zasobw XML.
Listing 3.17. Skadnia jzyka XML do definiowania zasobw typu Color
<resources>
<color name="red">#f00</color>
<color name="blue">#0000ff</color>
<color name="green">#f0f0</color>
<color name="main_back_ground_color">#ffffff00</color>
</resources>
Wpisy przedstawione na listingu 3.17 musz si znajdowa w pliku umieszczonym w podkatalogu /res/values. Nazwa pliku moe by dowolna. Android odczyta wszystkie pliki, a nastpnie je
przetworzy oraz odszuka oddzielne wzy, takie jak <resources> oraz <color>, w celu okrelenia
identyfikatorw.
Na listingu 3.18 zosta pokazany sposb zastosowania zasobu typu color w kodzie Java.
Listing 3.18. Zasoby typu color w kodzie Java
int mainBackGroundColor
= activity.getResources.getColor(R.color.main_back_ground_color);
Z kolei na listingu 3.19 zaprezentowano przykad wykorzystania zasobu typu color w definicji
widoku.
Listing 3.19. Zastosowanie kolorw w definicji widoku
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="@color/ red"
android:text="Przykadowy tekst napisany czerwon czcionk."/>
dimension
w jzyku
W metodzie tej stosowana jest pena nazwa Dimension, podczas gdy w przestrzeni nazw
pliku R.java uywana jest skrcona forma dimen.
Podobnie jak w jzyku Java, wobec odniesienia do zasobu w rodowisku XML stosuje si nazw
dimen zamiast penej nazwy dimension (listing 3.22).
Listing 3.22. Uywanie zasobw typu dimension w kodzie XML
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/medium_size"/>
107
Jeeli dwa pliki bd miay takie same nazwy, system wywietli komunikat o bdzie. Poza tym
podkatalogi umieszczone w wle /res/drawable bd ignorowane. aden plik umieszczony
w takim podkatalogu nie bdzie odczytywany.
Mona take uzyska dostp do obrazu programowo za pomoc kodu Java i okreli go
jako obiekt interfejsu uytkownika, na przykad jako przycisk (listing 3.24).
Listing 3.24. Uywanie zasobw typu image w rodowisku Java
// Wywouje funkcj getDrawable, eby pobraa obraz
BitmapDrawable d = activity.getResources().getDrawable(R.drawable.sample_image);
Android obsuguje rwnie specjalny format obrazu, zwany obrazem rozcigalnym. Jest to
rodzaj obrazu .png, w ktrym mona definiowa fragmenty obrazu jako statyczne lub rozcigalne.
Do okrelania tych rejonw suy narzdzie Draw9-patch, znajdujce si w pakiecie Android
SDK (wicej informacji na jego temat w jzyku angielskim mona znale na stronie
http://developer.android.com/guide/developing/tools/draw9patch.html).
Po przygotowaniu obrazu .png moe by on uywany tak samo jak kady inny typ obrazu. Ten
szczeglny typ przydaje si jako to dla przyciskw, ktre ulegaj rozcigniciu w celu dopasowania do tekstu.
Listingi 3.26 oraz 3.27 przedstawiaj kolejno zastosowanie zasobu typu color-drawable w jzyku
Java oraz jzyku XML.
Listing 3.26. Zastosowanie zasobw typu color-drawable w kodzie Java
// Wczytuje obiekt rysowany
ColorDrawable redDrawable =
(ColorDrawable)
activity.getResources().getDrawable(R.drawable.red_rectangle);
W celu zaokrglenia rogw obiektu Drawable mona zastosowa pominity w dokumentacji znacznik <shape>. Musi by on jednak umieszczony w oddzielnym pliku, w katalogu
/res/drawable. Sposb uycia znacznika <shape> umieszczonego w pliku /res/drawable/my_
rounded_rectangle.xml zosta zaprezentowany na listingu 3.28.
109
Mona nastpnie wykorzysta ten zasb jako to, tak jak we wczeniejszym przykadzie z widokiem tekstu, co zostao pokazane na listingu 3.29:
Listing 3.29. Wykorzystanie obiektu Drawable w kodzie Java
// Wczytuje obiekt rysowany
GradientDrawable roundedRectangle =
(GradientDrawable)
activity.getResources().getDrawable(R.drawable.my_rounded_rectangle);
Na listingu 3.32 mona zobaczy, w jaki sposb uzyska obiekt XmlPullParser i wykorzysta go
do nawigacji wrd elementw dokumentu XML oraz w jaki sposb zastosowa dodatkowe
metody tego obiektu do otrzymania szczegowych informacji na temat tych elementw.
111
Jeeli powyszy kod ma zadziaa, naley utworzy wspomniany wczeniej plik XML i wywoa funkcj getEventsFromAnXMLFile z dowolnego elementu menu lub za pomoc kliknicia przyciskiem myszy. Otrzymamy cig znakw, ktry mona nastpnie skopiowa do
dziennika za pomoc metody Log.d.
Jedynie katalog /assets moe posiada wasne drzewo podkatalogw, gdy nie jest
podkatalogiem wza /res. aden inny podkatalog nie moe mie plikw na niszym
poziomie struktury. Jest to spowodowane sposobem, w jaki plik R.java generuje
identyfikatory dla plikw.
113
Nawet jeli utworzymy trzy oddzielne pliki ukadw graficznych i kady umiecimy w osobnym katalogu, wszystkie wygeneruj tylko jeden wsplny identyfikator ukadu graficznego
w pliku R.java. Identyfikator ten bdzie wyglda nastpujco:
R.layout.main_layout
Jeeli jednak wczytamy ukad graficzny odpowiadajcy temu identyfikatorowi, uzyskamy ukad
optymalnie odpowiadajcy uoeniu urzdzenia.
W powyszym przykadzie rozszerzenia port i land katalogu nosz nazw kwalifikatorw
konfiguracji. Kwalifikatory te s niezalene od wielkoci liter oraz s oddzielone mylnikiem
(-) od nazwy katalogu zasobu. Zasoby definiowane w takich katalogach zawierajcych kwalifikatory konfiguracji s nazywane alternatywnymi zasobami. Z kolei obiekty znajdujce si w katalogu zasobw pozbawionym kwalifikatorw okrelane s jako zasoby domylne.
Poniej zamieszczono spis dostpnych kwalifikatorw konfiguracji.
mccAAA AAA jest kodem MCC (ang. Mobile Country Code kod kraju w telefonii
mobilnej),
mncAAA AAA jest kodem operatora/sieci,
pl-rPL jzyk i region,
small, normal, large, xlarge rozmiar ekranu,
long, notlong typ ekranu,
port, land tryb portretowy lub krajobrazowy,
car, desk rodzaj dokowania,
night, notnight noc lub dzie,
ldpi, mdpi, hdpi, xhdpi, nodpi gsto ekranu,
notouch, stylus, finger reakcja ekranu na dotyk,
keysexposed, keyssoft, keyshidden rodzaj klawiatury,
nokeys, qwerty, 12key liczba przyciskw,
navexposed, navhidden przyciski nawigacji odsonite lub ukryte,
nonav, dpad, trackball, wheel rodzaj urzdzenia sterujcego,
v3, v4, v7 poziom interfejsu API.
Moemy sprawdzi aktualny region jzykowy poprzez uruchomienie aplikacji Custom Locale, dostpnej w emulatorze. Oto cieka dostpu: Ekran startowy/lista aplikacji/Custom
Locale.
System Android, korzystajc z danego identyfikatora, stosuje algorytm sucy do wybrania
waciwego zasobu. Aby lepiej zrozumie reguy rzdzce tym procesem, Czytelnik moe
przejrze odpowiednie adresy URL, zamieszczone w podrozdziale Odnoniki, postaramy
si jednak ju teraz wyjani niektre z nich.
Podstawowa zasada polega na tym, e kwalifikatory wystpujce na listingu 3.37 s przetwarzane w kolejnoci ich wystpowania. Spjrzmy na katalogi zamieszczone na listingu 3.38.
Listing 3.38. Rne odmiany plikw ukadw graficznych
\res\layout\main_layout.xml
\res\layout-port\main_layout.xml
\res\layout-en\main_layout.xml
Na listingu 3.38 wida, e plik main_layout.xml jest dostpny w dwch dodatkowych wersjach:
dla jzyka oraz dla orientacji wywietlacza. Sprawdmy teraz, ktr wersj ukadu graficznego
wybierze system, gdy urzdzenie znajduje si w trybie portretowym. Nawet jeeli ustawimy
urzdzenie w orientacji pionowej, Android wybierze ukad graficzny z katalogu layout-en,
poniewa wrd kwalifikatorw konfiguracji wersja dotyczca jzyka ma wyszy priorytet od
wersji zwizanej z uoeniem ekranu. W podrozdziale Odnoniki umiecilimy cza do
zasobw SDK, gdzie mona znale pen list kwalifikatorw konfiguracji oraz kolejno ich
przetwarzania.
Przyjrzyjmy si dokadniej reguom pierwszestwa, przeprowadzajc eksperymenty na kilku
zasobach cigw znakw. Naley zwrci uwag, e cigi znakw opieraj si na pojedynczych
identyfikatorach, podczas gdy zasoby ukadw graficznych s zalene od plikw. W celu przetestowania kolejnoci przetwarzania kwalifikatorw konfiguracji wobec cigw znakw stworzymy
pi identyfikatorw zasobw, ktre mog wystpowa w nastpujcych konfiguracjach: default,
en, en_us, port oraz en_port. Te identyfikatory to:
teststring_all identyfikator ten znajdzie si we wszystkich odmianach katalogu
values, wcznie z domyln.
testport_port ten identyfikator bdzie obecny w konfiguracji domylnej
oraz w odmianie port.
t1_enport zostanie umieszczony w konfiguracji domylnej oraz w odmianach
en i port.
t1_1_en_port
en-port.
t2
115
// values-en/strings_en.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">teststring-en</string>
<string name="t1_enport">t1_en</string>
<string name="t1_1_en_port">t1_1_en</string>
</resources>
// values-en-rUS/strings_en_us.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-us</string>
</resources>
// values-port/strings_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-us-port</string>
<string name="testport_port">testport-port</string>
<string name="t1_enport">t1_port</string>
<string name="t1_1_en_port">t1_1_port</string>
</resources>
// values-en-port/strings_en_port.xml
<resources xmlns="http://schemas.android.com/apk/res/android">
<string name="teststring_all">test-en-port</string>
<string name="t1_1_en_port">t1_1_en_port</string>
</resources>
Odnoniki
W trakcie poznawania tajnikw zasobw Androida mog si przyda ponisze odnoniki;
opisalimy, co mona znale po ich klikniciu.
http://developer.android.com/guide/topics/resources/index.html ten adres URL
stanowi map po dokumentacji dotyczcej zasobw.
http://developer.android.com/guide/topics/resources/available-resources.html
znajdziemy tu opisane rnorodne rodzaje zasobw.
http://developer.android.com/reference/android/content/res/Resources.html
umieszczono tutaj opis rnorodnych metod sucych do odczytywania zasobw.
http://developer.android.com/reference/android/R.html opis zasobw zdefiniowanych
w rdzeniu systemu Android.
http://www.androidbook.com/item/3542 nasze badania dotyczce zasobw
wielokrotnoci, tablic cigw znakw oraz zasobw alternatywnych, jak rwnie
odniesienia do innych materiaw.
ftp://ftp.helion.pl/przyklady/and3ta.zip z tego adresu moemy pobra projekt
rodowiska Eclipse, w ktrym zostao ukazanych wiele koncepcji zawartych w tym
rozdziale. Waciwy plik znajdziesz w katalogu o nazwie ProAndroid3_R03_Zasoby.
117
Podsumowanie
Podsumujmy ten rozdzia poprzez wyliczenie opisanych tematw. Czytelnik mg pozna rodzaje zasobw obsugiwanych przez Androida oraz metody ich tworzenia w plikach XML.
Mona si byo dowiedzie, w jaki sposb s tworzone identyfikatory zasobw oraz jak je
umieci w kodzie Java. Czytelnicy przekonali si take, e tworzenie identyfikatorw zasobw
jest wygodn metod, uatwiajc zarzdzanie zasobami w Androidzie. Poza tym mona byo
zrozumie, w jaki sposb naley pracowa z zasobami nieskompresowanymi oraz dodatkowymi
plikami. Poruszylimy rwnie do oglnie zagadnienie alternatywnych zasobw, zasobw
wielokrotnoci oraz tablic cigw znakw.
Majc tak wiedz, w nastpnym rozdziale mona zaj si dostawcami treci.
R OZDZIA
4
Dostawcy treci
Koncepcja dostawcw treci w Androidzie oznacza abstrakcyjn warstw uatwiajc usugom dostp do danych. Dziki dostawcom treci dostp do rde
danych jest podobny do takiego jak w przypadku architektury REST. Dobrym
przykadem s strony WWW.
Witryna internetowa przekazuje do przegldarki informacje na temat danych
dostpnych pod okrelonym adresem URL. Podobnie dostawca treci zapewnia
opis danych przekazywanych obsugiwanej aktywnoci. W tym znaczeniu dostawca treci suy jako osona danych. Przykadem rda danych, ktre mona
umieci w dostawcy treci, jest baza danych SQLite.
Skrt REST oznacza REpresentational State Transfer, czyli reprezentacyjny
transfer stanu. Jest to skomplikowana nazwa dla bardzo prostej koncepcji,
z ktr wszyscy (na przykad uytkownicy sieci WWW) s dobrze zaznajomieni.
Kiedy kto wpisuje adres URL w przegldarce i otrzymuje w odpowiedzi
usug sieciow (wywietlenie strony), wykonuje operacj zapytania
wobec tej usugi. Zapytanie to jest oparte wanie na architekturze REST.
Innym przykadem jest wypenienie formularza na stronie WWW.
Przesanie tego formularza do serwera moe spowodowa zmian stanu tego
serwera albo zaktualizowanie jego zawartoci. Operacje te rwnie s
oparte na architekturze REST. Zazwyczaj koncepcji tej jest przeciwstawiane
pojcie usug sieciowych SOAP (ang. Simple Object Access Protocol
protok wywoywania zdalnego dostpu do obiektw). Wicej informacji
na temat architektury REST mona znale na stronie Wikipedii
http://en.wikipedia.org/wiki/Representational_State_Transfer.
Aby odczyta dane zawarte w dostawcy treci lub je w nim zapisa, naley skorzysta
z zestawu identyfikatorw URI, rwnie zgodnych z zaoeniami architektury REST.
eby na przykad odczyta zbir tytuw ksiek znajdujcych si w dostawcy treci,
ktry stanowi opakowanie bazy danych o ksikach, potrzebny byby nastpujcy
identyfikator URI:
content://com.android.book.BookProvider/books
121
Albums (Albumy)
Artists (Wykonawcy)
Genres (Gatunki)
Playlists (Listy odtwarzania)
Images (Obrazy)
Thumbnails (Miniatury)
Video (Wideo)
Settings (Ustawienia)
W zalenoci od uywanej wersji Androida lista dostawcw moe si skada z innych
elementw. Zadaniem powyszej listy jest zaprezentowanie dostpnych elementw,
jednak nie naley jej uznawa za bezwzgldny punkt odniesienia.
Bazy danych s elementami najwyszego poziomu, natomiast elementami na niszych poziomach s tabele. Zatem pozycje Browser, CallLog, Contacts, MediaStore oraz Settings s oddzielnymi bazami danych SQLite, zdefiniowanymi jako dostawcy. Bazy te zazwyczaj posiadaj
rozszerzenie .db i s dostpne jedynie z poziomu pakietu implementacyjnego. Kada prba uzyskania dostpu spoza tego pakietu musi nastpi poprzez interfejs dostawcy treci.
System Android posiada aplikacj wiersza polece znan jako Android Debug Bridge (adb), ktr
mona znale w katalogu jako plik:
platform-tools\adb.exe
Jest to specjalne narzdzie, z ktrym wikszo pozostaych aplikacji musi si poczy, zanim
uzyska dostp do urzdzenia. eby jednak zadziaao, trzeba najpierw uruchomi emulator lub
podczy urzdzenie. Za pomoc poniszego polecenia mona sprawdzi, czy jest uruchomiony
jaki emulator lub urzdzenie:
adb devices
Na ekranie pojawi si lista dostpnych urzdze AVD. Jeeli w rodowisku Eclipse zaprojektowano i uruchomiono przynajmniej jedn aplikacj, musiao rwnie zosta utworzone przynajmniej jedno urzdzenie AVD. Powysze polecenie spowoduje wywietlenie nazwy przynajmniej tego urzdzenia.
Poniej zaprezentowano dane wywietlane po wpisaniu powyszego polecenia (niektre mog
ulec zmianie w zalenoci od cieki do katalogu tools na przykad i:\android oraz od
wersji Androida):
I:\android\tools>android list avd
Available Android Virtual Devices:
Name: avd
Path: I:\android\tools\..\avds\avd3
Target: Google APIs (Google Inc.)
Based on Android 1.5 (API level 3)
Skin: HVGA
Sdcard: 32M
--------Name: titanium
Path: C:\Documents and Settings\Satya\.android\avd\titanium.avd
Target: Android 1.5 (API level 3)
Skin: HVGA
Pod widocznym poniej adresem zostaa zamieszczona lista wielu opcji rozruchowych narzdzia adb:
http://developer.android.com/guide/developing/tools/adb.html
123
Jest to powoka ash systemu Unix, pozbawiona jest jednak kilku polece. Dostpne
jest polecenie ls, brakuje natomiast instrukcji find, grep oraz awk.
Symbol # jest znakiem zachty powoki. W celu zachowania zwizoci bdziemy go pomija
w nastpnych przykadach. Po wpisaniu powyszego polecenia pojawi si lista polece przedstawionych w tabeli 4.1. (Naley mie na uwadze fakt, e tabela ta jest zaprezentowana wycznie
w celach demonstracyjnych i nie zostay w niej wymienione wszystkie polecenia. W zalenoci
od wersji zestawu Android SDK na licie mog wystpowa rne elementy).
eby zobaczy katalogi i pliki podstawowego poziomu, wystarczy wpisa:
ls -l
Zostanie wywietlony plik contacts.db, bdcy baz danych SQLite (plik ten oraz cieka do
niego zale od rodzaju urzdzenia oraz wersji systemu).
Powinnimy wspomnie, e w Androidzie mona tworzy bazy danych podczas pierwszej
prby uzyskania do nich dostpu. Oznacza to, e powyszy plik moe nie by widoczny,
w przypadku gdy aplikacja korzystajca z kontaktw nie zostaa jeszcze uruchomiona.
Gdyby w powoce ash byo dostpne polecenie find, istniaaby moliwo wyszukiwania
wszystkich plikw *.db. Nie mona tego wykona w prosty sposb za pomoc samego polecenia
ls. Najprostszym sposobem jest wpisanie:
ls -R /data/data/*/databases
Dziki temu poleceniu dowiadujemy si, e Android zawiera nastpujce bazy danych (jak
zwykle liczba i rodzaje elementw listy mog si rni w zalenoci od edycji zestawu Android SDK):
alarms.db
contacts.db
downloads.db
internal.db
settings.db
mmssms.db
telephony.db
sh
date
am
hciattach
dd
dumpstate
sdptool
cmp
input
logcat
cat
itr
servicemanager
dmesg
monkey
dbus-daemon
df
pm
debug_tool
getevent
svc
flash_image
getprop
ssltest
installd
hd
debuggerd
dvz
id
dhcpcd
hostapd
ifconfig
hostapd_cli
htclogkernel
insmod
fillup
mountd
ioctl
linker
qemud
kill
logwrapper
radiooptions
ln
telnetd
toolbox
log
iftop
hcid
lsmod
mkdosfs
route
ls
mount
setprop
mkdir
mv
sleep
dumpsys
notify
setconsole
service
netstat
smd
playmp3
printenv
stop
sdutil
reboot
top
rild
ps
start
dalvikvm
renice
umount
dexopt
rm
vmstat
surfaceflinger
rmdir
wipe
app_process
rmmod
watchprops
mediaserver
system_server
sendevent
sync
schedtop
netcfg
ping
chmod
Istnieje moliwo otwarcia bazy danych za pomoc aplikacji sqlite3 w powoce adb, jeli
wpisze si nastpujcy wiersz:
sqlite3 /data/data/com.android.providers.contacts/databases/contacts.db
125
Naley zwrci uwag, e znakiem zachty aplikacji adb jest #, natomiast w przypadku sqlite3
jest to sqlite>. Informacje dotyczce rnych polece aplikacji sqlite3 dostpnych wewntrz
powoki adb mona znale pod adresem http://www.sqlite.org/sqlite.html, jednak omwimy tu
kilka waniejszych polece, dziki czemu odwiedziny tej witryny nie bd konieczne. Lista tabel
zostanie wywietlona po wpisaniu:
sqlite> .tables
Jak mona si domyli, element sqlite_master jest gwn tabel, zarzdzajc pozostaymi
tabelami i widokami bazy danych. Poniszy wiersz wywouje instrukcj create dla tabeli
people, znajdujcej si w pliku contacts.db:
.schema people
Jest to jeden ze sposobw uzyskania nazw kolumn tabeli SQLite. Zostan rwnie wywietlone
typy danych zawartych w kolumnach. Podczas pracy z dostawcami usug typy danych bd
peniy wan funkcj, gdy od nich zale metody dostpu do bazy danych.
Jednak analizowanie wynikw instrukcji create jedynie w celu poznania nazw kolumn oraz
typw danych w nich zawartych jest dosy mudnym zajciem. Na szczcie istnieje inny sposb:
mona wyizolowa plik contacts.db z pakietu, a nastpnie obejrze tabel za pomoc dowolnego
interfejsu GUI (ang. Graphical User Interface graficzny interfejs uytkownika) obsugujcego
baz danych SQLite w wersji 3. Dziki poniszemu wierszowi, wpisanemu w oknie polece systemu operacyjnego, moliwe jest uzyskanie pliku contacts.db:
adb pull /data/data/com.android.providers.contacts/databases/contacts.db
c:/somelocaldir/contacts.db
127
Upowanienie peni funkcj nazwy domeny dla danego dostawcy treci. Na podstawie powyszych przykadw rejestracji upowanie dostawcy treci bd honorowa adresy URL
rozpoczynajce si od prefiksu upowanienia:
content://com.your-company.SomeProvider/
content://com.google.provider.NotePad/
Wida wic, e dostawcy treci, na przykad strony WWW, posiadaj podstawow nazw
domeny, zachowujc si jak pocztek adresu URL.
Naley zwrci uwag, e dostawcy treci w Androidzie nie musz posiada penej,
zoonej nazwy upowanienia. Obecnie jest ona zalecana wycznie dla dostawcw
treci wydawanych przez niezalenych producentw. Dlatego wanie czasami niektrzy
dostawcy treci opisywani s jednym sowem, na przykad contacts, w przeciwiestwie
do com.google.android.contacts (w przypadku dostawcy treci od niezalenego
producenta).
Dostawcy treci zapewniaj take adresy URL oparte na architekturze REST, suce do odczytywania lub modyfikowania danych. Na bazie powyszej rejestracji identyfikator URI sucy
do rozpoznawania katalogu lub zbioru notatek bazy danych NotePadProvider bdzie wyglda
nastpujco:
content://com.google.provider.NotePad/Notes
lub
content://authority-name/path-segment1/path-segment2/itd...
Po czonie content: umieszczono niepowtarzalny identyfikator upowanienia, ktry jest uywany do zlokalizowania dostawcy w rejestrze dostawcw. W powyszym przykadzie czonem stanowicym upowanienie jest com.google.provider.NotePad.
Czon /notes/23 jest sekcj okrelajc ciek, inn dla kadego dostawcy. Czony notes oraz 23
nazywane s segmentami cieki. Zadaniem dostawcy jest okrelenie oraz zinterpretowanie
sekcji oraz segmentw cieki w danym identyfikatorze URI.
Proces projektowania dostawcy treci polega przewanie na deklarowaniu staych w klasie Java
lub interfejsie Java, umieszczonych w tej samej implementacji pakietu, w ktrej znajduje si
dostawca. Co wicej, pierwszy czon sekcji cieki moe wskazywa na zbir obiektw. Na
przykad czon /notes wskazuje zbir lub katalog notatek, a segment /23 precyzuje interesujc
nas notatk.
Po otrzymaniu identyfikatora URI zadaniem dostawcy treci jest odczytanie wierszy wskazywanych przez ten identyfikator. Zadaniem dostawcy jest rwnie zmiana zawartoci takiego identyfikatora za pomoc ktrej z metod zmiany stanu: wstawiania, aktualizowania lub usuwania.
129
Pen list zarejestrowanych typw i podtypw mona przejrze w witrynie organizacji IANA
(ang. Internet Assigned Numbers Authority Urzd Przydzielania Numerw Internetowych):
http://www.iana.org/assignments/media-types/
Podstawowymi zarejestrowanymi typami treci s:
application
audio
example
image
message
model
multipart
text
video
Kady gwny typ posiada podtypy. Jeeli jednak producent wykorzystuje zastrzeony format
danych, nazwa podtypu rozpoczyna si od vnd. Na przykad arkusze kalkulacyjne Microsoft
Excel s identyfikowane jako podtyp o nazwie vnd.ms-excel, podczas gdy standard pdf nie
jest zastrzeony, dlatego jego identyfikator nie posiada adnego przedrostka okrelajcego
producenta.
Pewne podtypy posiadaj przedrostek x- w nazwie; s to podtypy niestandardowe, ktrych nie
trzeba rejestrowa. S one uznawane za prywatne wartoci, ktre s dwustronnie definiowane
pomidzy dwoma wsppracujcymi agentami. Poniej znajduje si kilka przykadw:
application/x-tar
audio/x-aiff
video/x-msvideo
Android posuguje si konwencj podobn do definiowania typw MIME. Prefiks vnd wskazuje
na to, e typy i podtypy s niestandardowymi formami okrelonymi przez producenta. W celu
zapewnienia niepowtarzalnoci Android idzie dalej w kierunku rozgraniczenia wieloczciowych typw i podtypw przypominajcych specyfikacj domenow. Co wicej, typ MIME
w Androidzie przybiera dwie formy dla kadej treci: jedn dla okrelonego rekordu, a drug dla
wielu rekordw.
Dwa przykady:
// Pojedyncza notatka
vnd.android.cursor.item/vnd.google.note
131
Dostawca MediaStore definiuje dwa identyfikatory URI, a dostawca Contacts okrela jeden
identyfikator. Mona zauway, e te stae s definiowane za pomoc drzewa hierarchii.
Przykadowy identyfikator URI dla kontaktw jest zdefiniowany jako Contacts.People.
CONTENT_URI. Spowodowane jest to faktem, e bazy danych kontaktw mog korzysta z wielu
tablic do reprezentowania jednostek w dostawcy Contacts. Kategoria People jest jedn z tablic
lub zbiorw. Kada podstawowa jednostka bazy danych moe posiada wasny identyfikator
URI, wszystkie s jednak zakorzenione za pomoc podstawowej nazwy upowanienia (w przypadku dostawcy kontaktw jest to contacts://contacts).
W odniesieniu Contacts.People.CONTENT_URI element Contacts jest pakietem Java,
a skadnik People stanowi interfejs wewntrz tego pakietu. Zwrmy rwnie uwag
na fakt, i identyfikatory Contacts oraz Contacts.people stay si przestarzae w wersji 2.0
Androida, a ich nowe odpowiedniki zostay omwione w rozdziale 27. Jednak identyfikatory
te cigle s przydatne, zwaszcza do omawiania koncepcji dostawcw treci.
Biorc pod uwag takie identyfikatory URI, kod sucy do odczytania jednego wiersza dostawcy
treci zawierajcego imiona ludzi wyglda nastpujco:
Uri peopleBaseUri = Contacts.People.CONTENT_URI;
Uri myPersonUri = Uri.withAppendedId(Contacts.People.CONTENT_URI, 23);
// klauzula WHERE
// klauzula sortowania
Zwrmy uwag, e obiekt projection jest jedynie tablic cigw znakw, reprezentujc
nazwy kolumn. Zatem bez znajomoci nazw tych kolumn trudno bdzie go utworzy. Ich nazwy
mona znale w tej samej klasie, ktra dostarcza identyfikator URI, w tym przypadku w klasie
People. Zobaczmy, jakie s w niej zdefiniowane nazwy pozostaych kolumn:
CUSTOM_RINGTONE
DISPLAY_NAME
LAST_TIME_CONTACTED
NAME
NOTES
PHOTO_VERSION
SEND_TO_VOICE_MAIL
STARRED
TIMES_CONTACTED
Informacje na temat tych kolumn mona znale w dokumentacji pakietu SDK dotyczcej
klasy android.provider.Contacts.PeopleColumns. Dokumentacja ta jest dostpna pod adresem:
http://developer.android.com/reference/android/provider/Contacts.PeopleColumns.html
Jak ju wczeniej zasugerowalimy, baza danych typu contacts zawiera wiele tabel, z ktrych
kada jest reprezentowana przez klas lub interfejs, co umoliwia opisanie tych kolumn oraz ich
typw. Przyjrzyjmy si pakietowi android.providers.Contacts, ktrego kod rdowy mona
przejrze na stronie:
http://developer.android.com/reference/android/provider/Contacts.html
Pakiet zawiera nastpujce zagniedone klasy lub interfejsy:
ContactMethods
Extensions
Groups
Organizations
People
133
Phones
Photos
Settings
Kada z tych klas reprezentuje nazw tabeli w bazie danych contacts.db, natomiast wszystkie
tabele maj opisa struktur swoich wasnych identyfikatorw URI. W dodatku dla kadej klasy
jest zdefiniowany odpowiedni interfejs Columns, umoliwiajcy identyfikacj nazw kolumn, na
przykad PeopleColumns.
Spjrzmy jeszcze na otrzymany obiekt Cursor moe nie zawiera adnych rekordw. Nazwy, typ oraz kolejno kolumn s okrelone dla kadego dostawcy. Jednak kady zwrcony
wiersz posiada domyln kolumn _id, stanowic niepowtarzalny identyfikator tego wiersza.
Mona je rwnie wykorzystywa w ptli for (listing 4.3) zamiast w przedstawionej na listingu
4.2 ptli while.
Listing 4.3. Sterowanie kursorem za pomoc ptli for
//Najpierw uzyskujemy indeksy spoza ptli
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
135
// Klauzula WHERE
// Klauzula sortowania
null);
Pozostawilimy warto null w argumencie klauzuli WHERE, bdcej czci metody managedQuery,
poniewa w tym przypadku zaoylimy, e dostawca notatek jest w stanie sam okreli warto
obiektu id podanej notatki. Warto ta jest umieszczona w identyfikatorze URI. Uylimy
identyfikatora URI jako pojemnika do wprowadzenia klauzuli WHERE. Staje si to zrozumiae,
gdy si zwrci uwag, w jaki sposb dostawca treci implementuje zwizan z nim metod kwerendy. Poniej umiecilimy fragment kodu takiej metody:
// Uzyskuje identyfikator notatki z przychodzcego identyfikatora uri, wygldajcego jak
//content://.../notes/23
int noteId = uri.getPathSegments().get(1);
Zauwamy, w jaki sposb obiekt id notatki jest uzyskiwany z identyfikatora URI. Klasa Uri, reprezentujca nadchodzcy element uri, posiada metod pozwalajc na wydobycie fragmentw
identyfikatora nastpujcych po gwnej czci content://com.google.provider.NotePad.
Fragmenty te nosz nazw segmentw cieki; s to cigi znakw oddzielonych znakiem /
na przykad /seg1/seg3/seg4/ indeksowane na podstawie pozycji. Dla naszego identyfikatora URI pierwszym segmentem moe by 23. Segment ten naley doda do klauzuli WHERE,
okrelonej w klasie QueryBuilder. Ostatecznie rwnowana instrukcja wyboru wygldaaby
nastpujco:
select * from notes where _id = 23
Zwrmy uwag na argument selection, ktrego zadeklarowanym typem jest String. Ten
cig znakw peni funkcj filtra (w istocie klauzuli WHERE) okrelajcego wiersze, ktre zostan
otrzymane i sformatowane do postaci klauzuli WHERE jzyka SQL (sama klauzula WHERE zostaje
pominita). Parametr null zwrci wszystkie krotki ze wskazanego identyfikatora URI. W cigu
znakw wyboru mona wstawia znaki zapytania, ktre bd zastpowane przez wartoci argumentu selectionArgs w kolejnoci ich pojawiania si. Typem wartoci bdzie String.
Poniewa istniej dwie metody okrelania klauzuli WHERE, mog pojawi si problemy ze stwierdzeniem, w jaki sposb dostawca treci wykorzysta te klauzule oraz ktra z nich uzyskuje pierwszestwo, jeeli s wykorzystywane obydwa mechanizmy.
Na przykad mona wysa kwerend dotyczc notatki numer 23 na obydwa sposoby:
// metoda URI
managedQuery("content://com.google.provider.NotePad/notes/23"
,null
,null
,null
,null);
lub
// jawna klauzula WHERE
managedQuery("content://com.google.provider.NotePad/notes"
,null
,"_id=?"
,new String[] {23}
,null);
Zgodnie z konwencj metod URI stosuje si zawsze tam, gdzie istnieje taka moliwo, natomiast jawne definiowanie klauzuli WHERE uywane jest w specjalnych przypadkach.
Wstawianie rekordw
Do tej pory zajmowalimy si odczytywaniem danych z dostawcw treci za pomoc identyfikatorw URI. Teraz omwimy techniki wstawiania, aktualizowania oraz usuwania danych.
W trakcie objaniania zagadnienia dostawcw treci szeroko wykorzystujemy przykady
zaczerpnite z prototypowej aplikacji Notepad, ktr firma Google zamiecia jako cz
samouczka. Nie jest jednak wymagana doskonaa znajomo tego programu. Nawet osoby,
ktre go nie znaj, powinny bez wikszego problemu zrozumie przytaczane tu przykady.
W dalszej czci rozdziau zaprezentujemy peny kod przykadowego dostawcy treci.
137
Teraz wystarczy wskaza identyfikator URI klasie ContentResolver, eby wstawi wiersz. Identyfikatory te s zdefiniowane w klasie odpowiadajcej tabeli Notes. Na przykadzie aplikacji Notepad identyfikatorem jest:
Notepad.Notes.CONTENT_URI
Moemy teraz wykorzysta posiadany identyfikator URI oraz klas ContentValues i utworzy
danie wstawienia wiersza:
Uri uri = contentResolver.insert(Notepad.Notes.CONTENT_URI, values);
Otrzymujemy identyfikator URI wskazujcy na nowo wstawiony rekord. Identyfikator ten bdzie
posiada nastpujc struktur:
Notepad.Notes.CONTENT_URI/new_id
Po uzyskaniu identyfikatora URI rekordu poniszy kod zostanie uyty do zadania od klasy
ContentResolver odniesienia do wyjciowego strumienia danych:
...
Oczywicie w metodzie delete argument ContentValues staje si zbdny, gdy nie ma potrzeby
okrelania kolumn podczas usuwania rekordu.
Niemal wszystkie wywoania z klas managedQuery oraz ContentResolver s ostatecznie
kierowane do klasy provider. Wiedza, w jaki sposb dostawca implementuje kad z tych
metod, pozwala wysnu wnioski na temat techniki wykorzystywania ich przez klienta.
W nastpnej sekcji omwimy od podstaw implementacj przykadowego dostawcy treci,
nazwanego BookProvider.
139
Przed implementacj tych metod naley skonfigurowa kilka rzeczy. Omwimy implementacj
dostawcy treci w formie szczegowego opisu kadego z nastpujcych etapw:
1. Zaplanuj baz danych, identyfikatory URI, nazwy kolumn i tak dalej. Utwrz klas
metadanych, w ktrej bd zdefiniowane stae dla wszystkich elementw metadanych.
2. Rozszerz abstrakcyjn klas ContentProvider.
3. Zaimplementuj metody: query, insert, update, delete oraz getType.
4. Zarejestruj dostawc w pliku manifecie.
// typ string
public static final String BOOK_ISBN = "isbn";
// typ string
public static final String BOOK_AUTHOR = "author";
141
/**
* Konfiguracja/tworzenie bazy danych.
* Klasa ta pomaga otwiera, tworzy i aktualizowa plik bazy danych.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, BookProviderMetaData.DATABASE_NAME, null
, BookProviderMetaData.DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG,"wywolana wewnetrzna metoda oncreate");
db.execSQL("CREATE TABLE " + BookTableMetaData.TABLE_NAME + " ("
+ BookProviderMetaData._ID + " INTEGER PRIMARY KEY,"
+ BookTableMetaData.BOOK_NAME + " TEXT,"
+ BookTableMetaData.BOOK_ISBN + " TEXT,"
+ BookTableMetaData.BOOK_AUTHOR + " TEXT,"
143
145
if (values.containsKey(BookTableMetaData.BOOK_NAME) == false) {
throw new SQLException(
" Nieudana prba wstawienia wiersza z powodu braku nazwy ksiki " + uri);
}
if (values.containsKey(BookTableMetaData.BOOK_ISBN) == false) {
values.put(BookTableMetaData.BOOK_ISBN, "Nieznany numer ISBN");
}
if (values.containsKey(BookTableMetaData.BOOK_AUTHOR) == false) {
values.put(BookTableMetaData.BOOK_ISBN, "Nieznany autor");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(BookTableMetaData.TABLE_NAME
, BookTableMetaData.BOOK_NAME, values);
if (rowId > 0) {
Uri insertedBookUri = ContentUris.withAppendedId(
BookTableMetaData.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(insertedBookUri, null);
return insertedBookUri;
}
throw new SQLException("Nieudana prba umieszczenia wiersza w " + uri);
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.delete(BookTableMetaData.TABLE_NAME, where, whereArgs);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.delete(BookTableMetaData.TABLE_NAME
, BookTableMetaData._ID + "=" + rowId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : "")
, whereArgs);
break;
default:
throw new IllegalArgumentException("Nieznany ident. URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs)
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.update(BookTableMetaData.TABLE_NAME,
values, where, whereArgs);
147
Po utworzeniu rejestracji mona zobaczy, jak rol odgrywa klasa UriMatcher w implementacji
metody query:
switch (sUriMatcher.match(uri)) {
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
...
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
...
default:
throw new IllegalArgumentException("Nieznany ident. URI " + uri);
}
Zwrmy uwag na sposb przekazywania przez metod match tej samej liczby, ktra zostaa
wczeniej zarejestrowana. Konstruktor klasy UriMatcher wykorzystuje liczb cakowit wzgldem
gwnego identyfikatora URI. UriMatcher przekazuje t warto, w przypadku gdy w adresie
URL nie ma ani segmentw cieki, ani upowanie. Warto NO_MATCH jest zwracana rwnie
w przypadku niepasujcych wzorcw identyfikatorw. Mona utworzy klas UriMatcher
nieposiadajc wzorcw dopasowania; w takim przypadku Android inicjalizuje wewntrznie warto NO_MATCH w klasie UriMatcher. Kod z listingu 4.7 mona zatem napisa rwnie
w nastpujcy sposb:
static {
sUriMatcher = new UriMatcher();
sUriMatcher.addURI(BookProviderMetaData.AUTHORITY
149
, "books"
, INCOMING_BOOK_COLLECTION_URI_INDICATOR);
sUriMatcher.addURI(BookProviderMetaData.AUTHORITY
, "books/#",
INCOMING_SINGLE_BOOK_URI_INDICATOR);
}
Rejestrowanie dostawcy
Ostatnim etapem jest rejestracja dostawcy treci w pliku AndroidManifest.xml za pomoc
struktury ukazanej na listingu 4.8:
Dodawanie ksiki
Kod z listingu 4.9 powoduje wstawienie nowej ksiki do bazy danych.
Listing 4.9. Testowanie funkcji dodawania za pomoc dostawcy treci
public void addBook(Context context)
{
String tag = "Testowanie dostawcy BookProvider";
Log.d(tag,"Dodawanie ksiazki");
ContentValues cv = new ContentValues();
cv.put(BookProviderMetaData.BookTableMetaData.BOOK_NAME, "book1");
cv.put(BookProviderMetaData.BookTableMetaData.BOOK_ISBN, "isbn-1");
cv.put(BookProviderMetaData.BookTableMetaData.BOOK_AUTHOR, "author-1");
ContentResolver cr = context.getContentResolver();
Uri uri = BookProviderMetaData.BookTableMetaData.CONTENT_URI;
Log.d(tag,"identyfikator uri wstawianej ksiazki:" + uri);
Uri insertedUri = cr.insert(uri, cv);
Log.d(tag,"identyfikator Uri wstawiania:" + insertedUri);
}
Usuwanie ksiki
Kod widoczny na listingu 4.10 powoduje usunicie ostatniego rekordu z ksikowej bazy danych.
Z kolei listing 4.11 ukazuje przykad dziaania metody getCount() widocznej w poniszym
kodzie.
151
Zauwamy, e mamy tu do czynienia z krtkim przykadem, pokazujcym mechanizm usuwania za pomoc identyfikatora URI. Algorytm uzyskiwania ostatniego identyfikatora URI nie
zawsze musi by stuprocentowo skuteczny. Powinien jednak poprawnie dziaa w przypadku
dodawania piciu rekordw, a nastpnie usuwania ich od koca, jednego po drugim. W typowej aplikacji dylibymy do wywietlenia rekordw w formie listy oraz poproszenia uytkownika o zaznaczenie rekordu przeznaczonego do usunicia. W ten sposb uzyskalibymy
dokadny identyfikator tego rekordu.
Zliczanie ksiek
Kod z listingu 4.11 pobiera kursor i zlicza zawarte w nim rekordy.
Listing 4.11. Zliczanie rekordw w tabeli
private int getCount(Context context)
{
Uri uri = BookProviderMetaData.BookTableMetaData.CONTENT_URI;
Activity a = (Activity)context;
Cursor c = a.managedQuery(uri,
null, //projekcja
null,
//Raportowanie indeksw
Log.d(tag,"name,isbn,author:" + iname + iisbn + iauthor);
//Zbiera wartoci
String
String
String
String
id = c.getString(1);
name = c.getString(iname);
isbn = c.getString(iisbn);
author = c.getString(iauthor);
//Zamyka kursor
//W idealnym przypadku powinno to zosta wykonane w
//bloku finally
c.close();
}
Odnoniki
Poniej przedstawiamy kilka dodatkowych zasobw, ktre mog pomc w zdobywaniu
wiedzy na tematy przedstawione w tym rozdziale:
http://developer.android.com/guide/topics/providers/content-providers.html
znajdziemy tu dokumentacj dotyczc dostawcw treci.
153
http://developer.android.com/reference/android/content/ContentProvider.html
zamieszczono tutaj opis interfejsu API klasy ContentProvider, z ktrego
moemy si dowiedzie o kontraktach tej klasy.
http://developer.android.com/reference/android/content/UriMatcher.html ten adres
URL prowadzi do strony zawierajcej uyteczne informacje o klasie UriMatcher.
http://developer.android.com/reference/android/database/Cursor.html dziki
informacjom tu zawartym nauczymy si odczytywa informacje z dostawcy treci
lub bezporednio z bazy danych.
http://www.sqlite.org/sqlite.html strona domowa silnika SQLite, na ktrej znajdziemy
wiele informacji na jego temat oraz narzdzi pozwalajcych na prac z bazami
danych SQLite.
ftp://ftp.helion.pl/przyklady/and3ta.zip z tego adresu moemy pobra testowy
projekt stworzony specjalnie z myl o niniejszym rozdziale. Waciwy plik znajdziesz
w katalogu o nazwie ProAndroid3_R04_DostawcyTreci.
Podsumowanie
W niniejszym rozdziale opisalimy najwaniejsze kwestie dotyczce identyfikatorw URI, typw
MIME oraz dostawcw treci. Nauczylimy si wykorzystywa baz danych SQLite do tworzenia
dostawcw zwizanych z identyfikatorami URI. Kiedy dane zostan w taki sposb odsonite,
kada aplikacja systemu Android moe je wykorzysta.
Taka zdolno uzyskiwania dostpu do danych oraz ich aktualizowania za pomoc identyfikatorw URI, bez wzgldu na granice procesw, wpisuje si znakomicie w technologi rodowiska
zorientowanego na usugi oraz przetwarzanie rozproszone, co opisalimy w rozdziale 1.
W kolejnym rozdziale omwimy pojcie intencji, ktre poprzez identyfikatory URI danych oraz
identyfikatory URI typu MIME s zwizane z dostawcami treci (jak rwnie z innymi skadnikami Androida). Wiedza zdobyta w tym rozdziale bardzo si przyda do zrozumienia zagadnienia intencji identyfikatory URI danych odgrywaj tu kluczow rol.
R OZDZIA
5
Intencje
Na najprostszym poziomie intencja jest dziaaniem, ktre Android moe przeprowadzi lub przywoa. Przywoywane dziaanie zaley od tego, co jest dla niego
zarejestrowane. Wyobramy sobie nastpujc aktywno:
public class BasicViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.some-view);
}
}//klasa eof
W powyszym procesie rejestracji bierze udzia nie tylko aktywno, lecz rwnie dziaanie, dziki
ktremu mona t aktywno wywoa. Projektant aktywnoci przewanie wybiera nazw dla
dziaania i definiuje je jako cz filtra intencji dla tej aktywnoci. W dalszej czci rozdziau
pojawi si wicej informacji na temat filtrw intencji.
Po zdefiniowaniu aktywnoci oraz jej zarejestrowaniu wobec dziaania mona uy intencji do
wywoania klasy BasicViewActivity:
public static invokeMyApplication(Activity parentActivity)
{
String actionName= " com.androidbook.intent.action.ShowBasicView ";
Intent intent = new Intent(actionName);
parentActivity.startActivity(intent);
}
Po przywoaniu aktywnoci BasicViewActivity posiada ona zdolno do rozpoznania wywoujcej j intencji. Poniej przedstawiono kod tej aktywnoci zmodyfikowany w taki sposb,
aby zostaa wczytana intencja wywoujca:
public class BasicViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.some_view);
Intent intent = this.getIntent();
if (intent == null)
{
Log.d("test tag", "Ta aktywnosc jest wywolywana bez uzycia intencji");
}
}
}//klasa eof
Rozdzia 5 Intencje
157
Taki kod jest przydatny w przypadku prostej aktywnoci, ktra zawiera element menu przywoujcy metod tryOneOfThese(activity). Utworzenie prostego menu jest banalne (listing 5.2).
Listing 5.2. rodowisko testowe suce do zbudowania prostego menu
public class MainActivity extends Activity
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Witaj, Androidzie. Przywitaj si");
setContentView(tv);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
int base=Menu.FIRST; // warto wynosi 1
MenuItem item1 = menu.add(base,base,base,"Test");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == 1) {
IntentUtils.tryOneOfThese(this);
}
else {
return super.onOptionsItemSelected(item);
}
return true;
}
}
Rozdzia 5 Intencje
159
W tej intencji czci danych zwizan z dziaaniem jest cig znakw lub staa typu string,
ktre przewanie zawieraj przedrostek z nazw pakietu Java.
Tej czci danych w intencji nie stanowi rzeczywiste dane, tylko wskanik do tych danych,
ktrym jest cig znakw bdcy identyfikatorem URI. Identyfikator URI intencji moe zawiera
argumenty, ktre mona uznawa za dane, na przykad adres URL.
Format tego identyfikatora moe by inny dla kadej aktywnoci wywoywanej przez to dziaanie.
W tym przypadku dziaanie CALL decyduje o tym, jakiego identyfikatora URI naley si spodziewa. Z tego identyfikatora uzyskiwany jest numer telefonu.
Wywoywana aktywno moe rwnie wykorzysta identyfikator URI jako wskanik
do rda danych, wydoby te dane ze rda i wykorzysta je. Przydaje si to w przypadku
plikw multimedialnych, na przykad muzyki, wideo i obrazw.
Dziaania oglne
Dziaania Intent.ACTION_CALL oraz Intent.ACTION_DIAL mog doprowadzi do bdnego
wniosku, e istnieje wzajemnie jednoznaczny zwizek pomidzy dziaaniem oraz wynikiem tego
dziaania. eby udowodni, e jest inaczej, rozpatrzmy kontrprzykad fragmentu IntentUtils
umieszczonego na listingu 5.1:
public static void invokeWebBrowser(Activity activity)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.google.com"));
activity.startActivity(intent);
}
Zwrmy uwag, e dziaanie jest okrelone jedynie jako ACTION_VIEW. Skd wiadomo, ktr
aktywno naley przywoa na podstawie takiej oglnej nazwy dziaania? W takich przypadkach Android bada nie tylko nazw oglnego dziaania, ale take charakter identyfikatora URI.
Analizuje struktur identyfikatora URI, w tym przykadzie http, a nastpnie sprawdza kad
zarejestrowan aktywno pod ktem rozpoznawania tej struktury. Spord aktywnoci, ktre
mog odpowiedzie na ten identyfikator, system wyszukuje tak, ktra moe odpowiedzie na
intencj VIEW, i wanie ona zostaje wywoana. eby ten mechanizm mg dziaa, aktywno
przegldarki powinna mie zarejestrowan intencj VIEW wobec schematu danych http. W pliku
manifecie taka deklaracja intencji moe wyglda nastpujco:
<activity...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
</activity>
Wicej informacji na temat opcji danych mona zdoby, przegldajc definicj XML elementu data filtru intencji na stronie http://developer.android.com/guide/topics/manifest/
data-element.html. Elementy lub atrybuty wza data s nastpujce:
host
mimeType
path
pathPattern
pathPrefix
port
scheme
Atrybut mimeType jest powszechnie uywany. Na przykad przedstawiony poniej filtr intencji dla
aktywnoci wywietlajcej list notatek wskazuje typ MIME jako katalog tych notatek:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
Deklaracj tego filtru intencji mona odczyta jako: Przywoaj t aktywno, aby przejrze list
notatek.
Rozdzia 5 Intencje
161
Z drugiej strony ekran wywietlajcy jedn notatk posiada filtr intencji zadeklarowany
za pomoc typu MIME wskazujcego pojedynczy element:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
Deklaracj tego filtru intencji mona odczyta jako: Przywoaj t aktywno, aby obejrze
pojedyncz notatk.
Intent,
Funkcja getExtras jest zrozumiaa: przekazuje obiekt klasy Bundle zawarty w intencji. putExtras
sprawdza, czy intencja zawiera aktualnie obiekt Bundle. Jeeli tak jest w istocie, funkcja ta przenosi dodatkowe klucze i wartoci z nowego obiektu klasy Bundle do ju istniejcego. W przeciwnym razie funkcja putExtras utworzy taki obiekt i skopiuje pary klucz warto z tego
obiektu do istniejcego ju wystpienia klasy Bundle.
Funkcja putExtras tworzy repliki przychodzcego obiektu Bundle, a nie odniesienia
do nich. Zatem podczas pniejszej modyfikacji przychodzcej klasy Bundle nie
trzeba zmienia pakietu znajdujcego si w intencji.
Istnieje wiele metod dodawania podstawowych typw do obiektu klasy Bundle. Poniej zaprezentowano kilka metod dodajcych proste typy danych do dodatkowych danych:
putExtra(String
putExtra(String
putExtra(String
putExtra(String
name,
name,
name,
name,
boolean value);
int value);
double value);
String value);
// Obiekty serializowalne
putExtra(String name, Serializable value);
// Obsuga parcelowania
putExtra(String name, Parcelable value);
Po stronie odbiorcy rwnowane metody pobierajce (typu get) rozpoczynaj dziaanie od odczytywania dodatkowych danych na podstawie kluczowych nazw.
Klasa Intent definiuje dodatkowe, kluczowe cigi znakw, zwizane z konkretnymi dziaaniami.
Pod adresem http://developer.android.com/reference/android/content/Intent.html#EXTRA_
ALARM_COUNT mona si zapozna z du liczb tych zawierajcych dodatkowe informacje
staych.
Przyjrzyjmy si dostpnym pod powyszym adresem URL przykadom dodatkowych danych,
zwizanych z wysyaniem wiadomoci e-mail:
EXTRA_EMAIL. Klucz ten suy do przechowywania grupy adresw e-mail. Jego warto
to android.intent.extra.EMAIL. Powinien wskazywa tablic cigw znakowych,
zawierajc wpisane adresy e-mail.
EXTRA_SUBJECT. Dziki temu kluczowi moliwe jest przechowywanie nazwy tematu
wiadomoci e-mail. Wartoci tego klucza jest android.intent.extra.SUBJECT.
Powinien wskazywa cig znakw stanowicy temat wiadomoci.
Stosowanie skadnikw
do bezporedniego przywoywania aktywnoci
Przeledzilimy kilka sposobw uruchamiania aktywnoci za pomoc intencji. Pokazalimy, jak
jawne dziaanie uruchamia aktywno oraz jak mona tego dokona, stosujc oglne dziaanie
za pomoc identyfikatora URI. Istnieje rwnie bardziej bezporedni sposb uruchomienia
aktywnoci: mona okreli jej klas ComponentName, stanowic abstrakcj powizan z nazw
pakietu danego obiektu oraz nazw klasy. Istnieje wiele metod klasy Intent pozwalajcych
na okrelenie skadnika:
Rozdzia 5 Intencje
163
setComponent(ComponentName name);
setClassName(String packageName, String classNameInThatPackage);
setClassName(Context context, String classNameInThatContext);
setClass(Context context, Class classObjectInThatContext);
Obiekt klasy ComponentName czy ze sob nazw pakietu oraz nazw klasy. Na przykad poniszy kod wywouje dostpn w emulatorze aktywno Contacts:
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"com.android.contacts"
,"com.android.contacts.DialContactsEntryActivity");
startActivity(intent)
Zauwamy, e nazwy pakietu oraz klasy s w peni kwalifikowane i zostaj uyte do skonstruowania obiektu klasy ComponentName, zanim przejd do klasy Intent.
Mona rwnie wykorzysta nazw klasy bezporednio, bez tworzenia obiektu ComponentName.
Rozwamy ponownie fragment kodu BasicViewActivity:
public class BasicViewActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.some-view);
}
}//eof-class
Jeeli jednak kady rodzaj intencji ma uruchamia aktywno, naley j zarejestrowa w pliku
AndroidManifest.xml w nastpujcy sposb:
<activity android:name=".BasicViewActivity"
android:label="Aktywno testowa">
Do bezporedniego wywoania aktywnoci za pomoc nazwy klasy lub nazwy skadnika nie
s potrzebne filtry intencji. Jak ju wczeniej wyjanilimy, jest to tak zwana intencja jawna.
Poniewa taka intencja definiuje do wywoania w peni kwalifikowany skadnik systemu
Android, podczas przywoywania tego skadnika ignorowane s pozostae elementy intencji.
Kategorie intencji
Aktywnoci mona dzieli na kategorie, eby dao si je wyszukiwa na podstawie nazwy kategorii.
Na przykad podczas rozruchu system Android szuka aktywnoci znajdujcych si w kategorii
nazwanej CATEGORY_LAUNCHER. Pobiera nastpnie nazwy oraz ikony tych aktywnoci i umieszcza
je na ekranie startowym.
PackageManager
jest kluczow klas, pozwalajc odkrywa aktywnoci dopasowane do okrelonych intencji bez koniecznoci ich wywoywania. Interfejs ResolveInfo pozwala na wymienianie otrzymywanych aktywnoci oraz wywoywanie ich w miar potrzeby. Poniej prezentujemy rozwinicie wczeniejszego kodu, w ktrym sprawdzana jest lista dostpnych aktywnoci
oraz wywoywana jest jedna z nich, jeli nazwy bd si zgadzay. W celach testowych wykorzystalimy wasn nazw.
//ri.activityInfo.
Log.d("test",ri.toString());
String packagename = ri.activityInfo.packageName;
Rozdzia 5 Intencje
165
Opis
CATEGORY_DEFAULT
CATEGORY_BROWSABLE
CATEGORY_TAB
CATEGORY_ALTERNATIVE
CATEGORY_SELECTED
_ALTERNATIVE
CATEGORY_LAUNCHER
CATEGORY_HOME
CATEGORY_PREFERENCE
CATEGORY_GADGET
CATEGORY_TEST
CATEGORY_EMBED
Do intencji bdzie pasowao kilka aktywnoci. Zatem ktr z nich wybierze Android? System
rozwizuje ten problem poprzez wywietlenie okna dialogowego Complete action using (dokocz
dziaanie za pomoc), w ktrym s widoczne wszystkie dopuszczalne aktywnoci. Naley wybra
jedn z nich.
A to inny przykad zastosowania intencji sucej do wywoywania ekranu startowego:
// Przechodzi do ekranu startowego
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_HOME);
startActivity(mainIntent);
Jeeli naley uy ekranu startowego innego ni domylny, mona napisa swj wasny i zadeklarowa aktywno do kategorii HOME. W tym przypadku powyszy kod da moliwo otwarcia
swojego ekranu startowego, poniewa zarejestrowano kilka jego aktywnoci:
// Podmienia ekran startowy na utworzony przez programist
<intent-filter>
<action android:value="android.intent.action.MAIN" />
<category android:value="android.intent.category.HOME"/>
<category android:value="android.intent.category.DEFAULT" />
</intent-filter>
Rozdzia 5 Intencje
167
Dziaanie
Jeeli intencja zawiera w sobie dziaanie, filtr intencji musi posiada je na swojej licie dziaa
lub nie posiada adnej listy dziaa. Jeli zatem filtr intencji nie zdefiniuje adnego dziaania,
bdzie on dopasowany do dowolnego przychodzcego dziaania.
Jeeli w filtrze intencji zostanie zdefiniowane przynajmniej jedno dziaanie, musi ono odpowiada przychodzcemu dziaaniu intencji.
Dane
Jeeli w filtrze intencji nie zostan zdefiniowane charakterystyki danych, nie bdzie on dopasowany do przychodzcej intencji, przenoszcej jakiekolwiek dane lub ich atrybuty. Oznacza
to, e bdzie wyszukiwa wycznie intencje niepowizane z danymi.
Brak danych i brak dziaania (w filtrze) maj zupenie odwrotne dziaanie. Jeeli w filtrze nie
zostanie zdefiniowane adne dziaanie, kady obiekt bdzie dopasowany. Jeli w filtrze nie zostan umieszczone dane, kady bit informacji z intencji bdzie niedopasowany.
Typ danych
Aby typ danych przychodzcej intencji by dopasowany, musi si znajdowa na licie typw
danych okrelonych w filtrze intencji. Typ danych zamieszczony w intencji musi by rwnie
obecny w filtrze intencji.
Typ danych z przychodzcej intencji moe zosta okrelony na jeden z dwch sposobw. Po
pierwsze, jeli identyfikator URI danych jest identyfikatorem URI pliku lub treci, dostawca
treci lub sam system automatycznie rozpoznaj typ danych. Drugie rozwizanie polega na
sprawdzeniu jawnego typu danych intencji. Aby to si powiodo, przychodzca intencja nie
moe posiada ustanowionego identyfikatora URI danych, poniewa jest on nadawany automatycznie w momencie wywoania metody setType wobec intencji.
Jako cz specyfikacji typw MIME Android pozwala rwnie na wprowadzenie symbolu
gwiazdki (*), zastpujcego wszystkie moliwe podtypy.
Ponadto typ danych rozrnia wielko liter.
Schemat danych
Aby schemat danych by dopasowany, musi si znajdowa na licie schematw w filtrze intencji oraz odpowiada schematowi znajdujcemu si w przychodzcej intencji. Innymi sowy,
przychodzcy schemat danych musi by odzwierciedlony w filtrze intencji.
W przychodzcej intencji schemat stanowi pierwszy czon identyfikatora URI danych. W przypadku intencji nie ma adnego sposobu na ustanowienie schematu. Wywodzi si on wprost
z identyfikatora URI danych intencji i wyglda mniej wicej tak: http://www.jakasstrona.com/
jakassciezka.
Jeeli schemat danych z przychodzcego identyfikatora URI intencji rozpoczyna si od czonw
content: lub file:, jest on dopasowany bez wzgldu na schemat filtra, domen czy ciek.
Zgodnie z dokumentacj zestawu SDK wynika to z faktu, i kady skadnik powinien mc odczytywa dane z tych dwch rodzajw adresw URL, ktre w swej istocie s lokalne. Inaczej
mwic, od wszystkich skadnikw oczekuje si obsugi tych dwch typw adresw URL.
Schemat rwnie rozrnia wielko liter.
Uprawnienia do danych
Jeeli w filtrze nie zostan uwzgldnione adne uprawnienia, wszelkie uprawnienia (lub nazwy
domenowe) przychodzcych identyfikatorw URI bd dopasowane. Jeeli w filtrze zdefiniowano jakie uprawnienie, na przykad www.jakasstrona.com, to do danego identyfikatora URI
intencji powinien pasowa jeden schemat oraz jedno uprawnienie.
Jeli na przykad uprawnieniem okrelonym w filtrze intencji jest www.jakasstrona.com,
a schematem jest https, adres http://www.jakasstrona.com/jakassciezka bdzie niedopasowany, poniewa http nie jest w tym przypadku obsugiwanym formatem.
Uprawnienie take rozrnia wielko liter.
cieka danych
Brak cieek w filtrze intencji oznacza dopasowanie do kadej cieki znajdujcej si w przychodzcym identyfikatorze URI. Jeeli w filtrze okrelono ciek, na przykad jakassciezka,
przychodzcemu identyfikatorowi URI danych w intencji powinien odpowiada jeden schemat,
jedno uprawnienie i jedna cieka danych.
Innymi sowy, schemat, uprawnienie i cieka wsppracuj ze sob w celu sprawdzenia poprawnoci przychodzcego identyfikatora URI, na przykad http://www.jakasstrona.com/
jakassciezka. Zatem elementy path, authority i scheme nie dziaaj oddzielnie, lecz
wsppracuj ze sob.
Podobnie jak we wczeniejszych przypadkach, cieka rozrnia wielko liter.
Kategorie intencji
Kada kategoria zawarta w przychodzcej intencji musi by wymieniona na licie kategorii
filtru intencji. Wiksza liczba kategorii w filtrze nie jest niczym zym. Jeeli filtr nie zawiera
adnych kategorii, dopasowana bdzie wycznie intencja nieposiadajca adnej zadeklarowanej kategorii.
Istnieje jednak pewne zastrzeenie. Android uwzgldnia wszystkie niejawne intencje przekazane do metody startActivity(), tak jakby posiaday przynajmniej jedn kategori:
android.intent.category.DEFAULT. Kod tworzcy t metod bdzie wyszukiwa jedynie te
aktywnoci, dla ktrych zdefiniowano kategori DEFAULT, ale tylko wtedy, gdy przychodzca
intencja jest niejawna. Zatem kada aktywno, ktra bdzie wywoana za pomoc niejawnej
intencji, musi zawiera domyln kategori w filtrze intencji.
Nawet jeli aktywno nie posiada domylnej kategorii zadeklarowanej w filtrze intencji, w przypadku gdy znamy jej jawne nazwy skadnikw, bdziemy mogli j uruchomi tak jak program
wywoujcy. Jeeli samodzielnie wyszukujemy w sposb jawny pasujce intencje, bez posiadania
domylnej kategorii jako kryterium wyszukiwania, to w ten sposb uruchomimy aktywnoci.
W tym sensie kategoria DEFAULT jest artefaktem implementacyjnym metody startActivity,
a nie naturalnym skadnikiem filtra intencji.
Istnieje jeszcze dodatkowy problem. System Android uznaje, i domylna kategoria jest niepotrzebna, w przypadku gdy aktywno ma by uruchamiana jedynie z poziomu ekranw programw wywoujcych. Zatem w filtrach intencji takich aktywnoci zazwyczaj umieszczane
s wycznie kategorie MAIN i LAUNCHER. Jednak kategoria DEFAULT moe rwnie zosta
opcjonalnie zdefiniowana dla tych aktywnoci.
Rozdzia 5 Intencje
169
Dziaanie ACTION_PICK
Jak na razie zajmowalimy si intencjami lub dziaaniami, ktre w gwnej mierze wywoyway
inn aktywno bez uzyskiwania wynikw. Przyjrzymy si nieco bardziej zaawansowanemu dziaaniu, w ktrym po jego wywoaniu otrzymujemy warto. Takim dziaaniem jest ACTION_PICK.
Dziaanie ACTION_PICK polega na uruchomieniu aktywnoci, ktra wywietla list elementw.
Aktywno powinna nastpnie umoliwi uytkownikowi wybranie jednego z elementw. Gdy
tak si stanie, aktywno przekae programowi dajcemu identyfikator URI wybranego elementu. Dziki temu mona wielokrotnie korzysta z funkcjonalnoci interfejsu uytkownika
do wybierania elementw okrelonego typu.
Naley wskaza zbir wybieranych elementw za pomoc typu MIME, ktry okrela kursor treci w Androidzie. Typ MIME takiego identyfikatora URI powinien wyglda mniej wicej
nastpujco:
vnd.android.cursor.dir/vnd.google.note
Aktywno powinna uzyska dane z tego dostawcy treci na podstawie identyfikatora URI.
Z tego wanie powodu dane powinny by umieszczane w dostawcach treci wszdzie, gdzie jest
to moliwe.
W adnym dziaaniu, ktre przekazuje dane w ten sposb, nie mona zastosowa metody
startActivity(), poniewa nie przekazuje ona adnego wyniku. Wynika to z faktu, e ta metoda
otwiera now aktywno, ktra przyjmuje posta modalnego okna dialogowego w oddzielnym
wtku, i pozostawia gwny wtek dla innych zdarze. Innymi sowy, metoda startActivity()
jest wywoaniem asynchronicznym, nieposiadajcym adnych wywoa zwrotnych, ktre
wskazywayby na to, co si stao z przywoan aktywnoci. Jeli przewidujemy otrzymywanie danych wynikowych, mona wykorzysta wariant metody startActivity(), nazwany
startActivityForResult(), ktry zawiera wywoanie zwrotne.
Przyjrzyjmy si sygnaturze metody startActivityForResult() klasy Activity:
public void startActivityForResult(Intent intent, int requestCode)
Metoda ta uruchamia aktywno, ktra ma wywietli wynik. Po zakoczeniu aktywnoci jej rdowa metoda onActivityResult() zostanie wywoana wraz z danym argumentem requestCode.
Sygnatur tej metody wywoywania zwrotnego jest:
protected void onActivityResult(int requestCode, int resultCode, Intent data)
// Wywietla notatk
outputIntent.setAction(Intent.ACTION_VIEW);
startActivity(outputIntent);
}
Stae
RESULT_OK = -1;
RESULT_CANCELED = 0;
RESULT_FIRST_USER = 1;
s zdefiniowane w klasie
Rozdzia 5 Intencje
171
Aby funkcja PICK dziaaa, element, ktry jej odpowiada, powinien zawiera kod jawnie odpowiadajcy wymaganiom intencji PICK. Zobaczymy, w jaki sposb cel ten osignito w przykadowej aplikacji Notepad. Po wybraniu elementu z listy system sprawdza, czy intencja,
ktra wywoaa aktywno, jest intencj PICK. Jeli tak jest, w nowej intencji ustanawia si
identyfikator URI danych, przekazywany poprzez metod setResult():
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
String action = getIntent().getAction();
if (Intent.ACTION_PICK.equals(action) ||
Intent.ACTION_GET_CONTENT.equals(action))
{
Dziaanie ACTION_GET_CONTENT
Dziaanie ACTION_GET_CONTENT jest podobne do dziaania ACTION_PICK. W przypadku tego
drugiego okrela si identyfikator URI, ktry wskazuje zbir elementw, na przykad kolekcj
notatek. Dziaanie ma pobra jedn z notatek i przekaza j programowi wywoujcemu.
W przypadku dziaania ACTION_GET_CONTENT potrzebny jest element okrelonego typu MIME.
Android przeszukuje wtedy zarwno aktywnoci zdolne do utworzenia takich elementw, jak
i aktywnoci, w ktrych mona wybiera elementy speniajce warunek waciwego typu MIME.
Za pomoc dziaania ACTION_GET_CONTENT mona wybra notatk ze zbioru notatek obsugiwanych przez aplikacj Notepad w nastpujcy sposb:
public static void invokeGetContent(Activity activity)
{
Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT);
int requestCode = 2;
pickIntent.setType("vnd.android.cursor.item/vnd.google.note");
activity.startActivityForResult(pickIntent, requestCode);
}
Zwrmy uwag na sposb, w jaki typ intencji zostaje dopasowany do typu MIME pojedynczej notatki. Porwnamy to z kodem ACTION_PICK, w ktrym na wejciu jest identyfikator
URI danych. Kod znajduje si poniej:
public static void invokePick(Activity activity)
{
Intent pickIntent = new Intent(Intent.ACTION_PICK);
int requestCode = 1;
pickIntent.setData(Uri.parse(
"content://com.google.provider.NotePad/notes"));
activity.startActivityForResult(pickIntent, requestCode);
}
Reszta kodu obsugujcego metod onActivityResult() jest taka sama jak w przykadzie
z dziaaniem ACTION_PICK. Jeeli istnieje wiele aktywnoci mogcych przekaza ten sam typ
MIME, zostanie wywietlony ekran wyboru, na ktrym mona wskaza jedn z nich.
Rozdzia 5 Intencje
173
startActivity(intent)
startService(intent)
sendBroadcast(intent)
W jaki sposb te metody pozwalaj na okrelenie, czy naley rozpocz aktywno, usug, czy
te przygotowa odbiorc wiadomoci na transmisj, jeli trzeba przechowa intencje do pniejszego wykorzystania? Wanie dlatego trzeba jawnie okreli przeznaczenie intencji oczekujcej w momencie jej tworzenia. Teraz ponisze trzy metody staj si zrozumiae:
PendingIntent.getActivity(context, 0, intent, ...)
PendingIntent.getService(context, 0, intent, ...)
PendingIntent.getBroadcast(context, 0, intent, ...)
Pozostaa nam jeszcze kwestia metody get. Android przechowuje intencje oraz umoliwia ich
ponowne zastosowanie. Jeeli dwukrotnie wywoamy oczekujc intencj za pomoc tego samego
obiektu intencji, otrzymamy identyczn intencj oczekujc. Stanie si to nieco bardziej zrozumiae, jeli przypatrzymy si penej sygnaturze metody PendingIntent.getActivity().
Oto ona:
PendingIntent.getActivity(Context context,
//pierwotny kontekst
int requestCode,
//1,2, 3 itd.
Intent intent, //oryginalna intencja
int flags ) //flagi
Jeeli naszym celem jest uzyskanie kolejnej kopii intencji oczekujcej, musimy dostarczy inn
warto argumentu requestCode. Konieczno ta zostaa wyjaniona o wiele dokadniej w rozdziale 15., podczas omawiania menederw alarmw. Dwie intencje s uznawane za identyczne, jeeli ich wewntrzne elementy s takie same. Powysze stwierdzenie nie dotyczy
dodatkowych obiektw.
Dla intencji oczekujcej rodzaj czynnoci jest definiowany za pomoc flag. Chodzi tu o takie
czynnoci, jak przekazanie wartoci null, nadpisanie elementw dodatkowych i tak dalej. Wicej
wiadomoci na temat istniejcych rodzajw flag znajdziemy pod nastpujcym adresem:
http://developer.android.com/reference/android/app/PendingIntent.html
Zazwyczaj, aby intencja zachowaa si w sposb domylny, przekazujemy warto
mentom requestCode oraz flagom.
argu-
Odnoniki
Pod poniszymi adresami mona znale wicej materiaw (w jzyku angielskim), uzupeniajcych informacje z tego rozdziau:
http://developer.android.com/reference/android/content/Intent.html pod tym
adresem znajdziemy oglne informacje dotyczce intencji. Poznamy tu najpopularniejsze
dziaania, obiekty dodatkowe itd.
http://developer.android.com/guide/appendix/g-app-intents.html znajduje si tu
lista intencji wykorzystywanych w rnych aplikacjach firmy Google. Dowiemy si,
w jaki sposb wywoa intencje takich aplikacji, jak Browser, Map, Dialer czy Google
Street View.
http://developer.android.com/reference/android/content/IntentFilter.html tu znajdziemy
informacje dotyczce filtrw intencji, przydatne podczas rejestrowania tych filtrw.
http://developer.android.com/guide/topics/intents/intents-filters.html tu omwiono
reguy okrelajce filtry intencji.
http://developer.android.com/resources/samples/get.html za pomoc tego cza
moemy pobra przykadowy kod aplikacji Notepad. Bez wczytania tego projektu
nie przetestujemy niektrych intencji.
http://developer.android.com/resources/samples/NotePad/index.html wersja online
kodu rdowego aplikacji Notepad.
http://www.openintents.org/ witryna, ktrej zadaniem jest prba zebrania intencji
tworzonych przez rnych wydawcw.
ftp://ftp.helion.pl/przyklady/and3ta.zip z tego adresu moemy pobra testowy
projekt, zaprojektowany specjalnie na potrzeby niniejszego rozdziau. Waciwy plik
znajdziesz w katalogu o nazwie ProAndroid3_R05_Intencje.
Podsumowanie
W tym rozdziale zdefiniowalimy najwaniejsze elementy dotyczce intencji w systemie Android. Przejrzelimy rnorodne scenariusze wykorzystania intencji, a take wyjanilimy
zwizki istniejce pomidzy intencjami a identyfikatorami URI treci. Wytumaczylimy
rwnie, w jaki sposb mona wykorzysta intencje do wywoywania aktywnoci przekazujcych wyniki. Wprowadzilimy take pojcie intencji oczekujcych. Pojcie to zostanie dokadniej przeanalizowane w rozdziaach 15. i 22.
R OZDZIA
6
Budowanie
interfejsw uytkownika
oraz uywanie kontrolek
Opis
Pojemnik, kontener
Ukad graficzny
Rysunek 6.1 przedstawia zrzut ekranu aplikacji, ktr wkrtce zaprojektujemy. Obok zrzutu
znajduje si schemat hierarchii kontrolek i pojemnikw aplikacji w ukadzie graficznym.
177
Jak wida na listingu 6.1, aktywno zawiera trzy ukady graficzne LinearLayout. Wspomnielimy wczeniej, e obiekty typu layout s przystosowane do pozycjonowania innych obiektw
w danej czci ekranu. Na przykad ukad graficzny LinearLayout okrela, czy obiekt ma by
umieszczony w pionie, czy w poziomie. W obiektach typu layout mona umieszcza dowolne typy widokw nawet inne ukady graficzne.
W obiekcie nameContainer s umieszczone dwie kontrolki TextView: jedna wywietla etykiet
Imi, nazwisko, a druga przechowuje dane imienia i nazwiska (w naszym przykadzie Gall
Anonim). Analogicznie obiekt addressContainer rwnie zawiera dwie kontrolki TextView.
Rnica pomidzy tymi pojemnikami jest taka, e ten pierwszy zosta umieszczony poziomo,
a obiekt drugi pionowo. Obydwa pojemniki znajduj si we wntrzu obiektu parentContainer,
stanowicego podstawowy widok aktywnoci. Po utworzeniu pojemnikw aktywno przypisuje
tre widoku do widoku gwnego poprzez wywoanie metody setContentView(parent
Container). Kiedy trzeba wywietli interfejs uytkownika tej aktywnoci, gwny widok jest
179
Inaczej mwic, parametr WRAP_CONTENT oznacza, e widok zajmuje w danym wymiarze wycznie tyle przestrzeni, ile jest konieczne, a poza tym jest ograniczony rozmiarem swojego widoku nadrzdnego. W przypadku kontrolki addressContainer oznacza to, e bdzie ona zajmowaa dwie linijki tekstu, poniewa potrzebuje tylko tyle miejsca.
W katalogu src tego projektu istnieje domylny plik .java, zawierajcy definicj klasy Activity.
Dwukrotne kliknicie nazwy tego pliku spowoduje wywietlenie jego treci. Zwrmy uwag na
instrukcj setContentView(R.layout.main). Fragment kodu XML umieszczony na listingu
6.2 w poczeniu z wywoaniem setContentView(R.layout.main) spowoduje wywietlenie
takiego samego interfejsu uytkownika jak w przypadku listingu 6.1. Nie trzeba omawia pliku
XML, warto jednak zwrci uwag, e zdefiniowano trzy widoki pojemnikw. Pierwszy, Linear
Layout, jest odpowiednikiem pojemnika nadrzdnego. Jego pooenie zostaje ustalone na pionowe poprzez zdefiniowanie odpowiedniej waciwoci android:orientation="vertical".
W pojemniku nadrzdnym s umieszczone dwa podrzdne elementy LinearLayout, reprezentujce, odpowiednio, pojemniki nameContainer oraz addressContainer.
Uruchomienie tej aplikacji spowoduje wygenerowanie takiego samego ukadu graficznego jak
w poprzednim przykadzie. Wywietlane etykiety i wartoci bd takie same jak na rysunku 6.1.
181
Poza dodaniem identyfikatorw do kontrolek TextView, ktre pniej wypenimy danymi z kodu,
moemy take uy etykietowych kontrolek TextView. Ich wartoci s wypeniane tekstem
z pliku zasobw. S to kontrolki TextView nieposiadajce identyfikatora zawartego w atrybucie
android:text. Jeeli przypomnimy sobie informacje z rozdziau 3., waciwe cigi znakw,
przeznaczone dla tych kontrolek, znajduj si w pliku strings.xml umieszczonym w folderze
/res/values. Listing 6.4 przedstawia taki wykorzystany w naszym przykadzie plik strings.xml.
Listing 6.4. Plik strings.xml wsppracujcy z kodem z listingu 6.3
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Kontrolki standardowe</string>
<string name="name_text">Imi i nazwisko:</string>
<string name="addr_text">Adres:</string>
</resources>;
Kod umieszczony na listingu 6.5 demonstruje, w jaki sposb uzyska odniesienia do kontrolek
zdefiniowanych w kodzie XML w celu skonfigurowania ich waciwoci. Moemy wstawi go
do metody onCreate() zawartej w naszej aktywnoci.
Powyszy kod nie jest skomplikowany, jednak naley zauway, e zanim wywoamy metod
findViewById(), wczytujemy zasb poprzez wywoanie setContentView(R.layout.main)
nie moemy odnie zasobw do widokw, jeeli zasoby nie zostay jeszcze zaadowane.
Twrcy systemu Android wykonali kawa dobrej roboty, umoliwiajc konfigurowanie waciwie kadego aspektu kontrolek za pomoc kodu lub jzyka XML. Preferowanym rozwizaniem
jest ustanawianie atrybutw kontrolek w pliku XML, bez koniecznoci uywania kodu. Jednak
bdzie jeszcze wiele okazji do wykorzystania kodu, na przykad w przypadku ustanawiania
wartoci, ktra bdzie wywietlana uytkownikowi.
FILL_PARENT a MATCH_PARENT
Staa FILL_PARENT zostaa wycofana w wersji 2.2 i zastpiono j sta MATCH_PARENT. Zmiana
dotyczy jednak wycznie nazwy. Warto tej staej cigle wynosi -1. Podobnie w przypadku
ukadw graficznych tworzonych w jzyku XML argument fill_parent zosta zastpiony
argumentem match_parent. Zatem jaka warto jest tu waciwie stosowana? Zamiast staej
FILL_PARENT lub MATCH_PARENT moglibymy po prostu wprowadzi warto -1 i nic by si nie
stao. Nie jest ona jednak odczytywana w prosty sposb, poza tym nie istnieje rwnowana,
nienazwana warto, ktr mona by zastosowa do ukadw graficznych tworzonych w jzyku
XML. Znamy lepsze rozwizanie.
W zalenoci od poziomu interfejsu API, ktry chcemy wykorzysta w aplikacji, moemy stworzy program wykorzystujcy wersj Androida starsz od 2.2 i liczy na kompatybilno
w przd albo przygotowa aplikacj pod ktem wersji co najmniej 2.2 i ustawi w argumencie
minSdkVersion najstarsz wersj systemu, na jakiej nasze dzieo bdzie pracowa. Jeli na
przykad wystarcz nam funkcje zawarte w wersji 1.6 Androida, to wanie dla niej tworzymy
aplikacj i wykorzystujemy argumenty FILL_PARENT oraz fill_parent. Powinna ona bez
problemu dziaa rwnie w nowszych wersjach systemu. Jeeli wymagana jest funkcjonalno
wersji 2.2 Androida, piszemy przystosowany do niej program, stosujemy MATCH_PARENT
i match_parent, natomiast w argumencie minSdkVersion wstawiamy warto starszej wersji
interfejsw API, na przykad 4 (odpowiada ona wersji 1.6 Androida). Moemy wdroy aplikacj napisan pod wersj 2.2 Androida do jego starszej wersji, musimy jednak uwaa na
stosowane klasy oraz (lub) metody, ktre s w niej niedostpne. Zawsze znajdzie si jakie rozwizanie, choby stosowanie refleksji lub klas osonowych, aby zniwelowa rnice pomidzy wersjami Androida. Nie zajmujemy si tu jednak tym zagadnieniem.
183
Kontrolki tekstu
Prawdopodobnie kontrolki tekstu s pierwszym rodzajem kontrolek, na ktre natykaj si
programici. Android posiada peny, lecz nieprzytaczajcy ogromem zestaw kontrolek tekstu.
W kolejnych podpunktach omwimy kontrolki TextView, EditText, AutoCompleteTextView
oraz MultiCompleteTextView. Na rysunku 6.2 pokazalimy dziaanie tych kontrolek.
TextView
Widzielimy ju prost specyfikacj kontrolki TextView w jzyku XML (listing 6.3) oraz sposb jej definiowania w kodzie (listing 6.4). Zwrmy uwag na sposb okrelania identyfikatora,
szerokoci, wysokoci oraz wartoci tekstu w pliku XML oraz mechanizm ustanawiania wartoci
za pomoc metody setText(). Kontrolka TextView wywietla tekst, jednak nie pozwala na jego
edycj. Mona by wysnu wniosek, e jest to jedynie zwyka etykieta. Nieprawda. Kontrolka ta
posiada kilka interesujcych waciwoci, dziki ktrym staje si bardzo przydatna. Jeeli na
przykad wiadomo, e zawartoci kontrolki TextView bdzie adres URL lub adres e-mail,
mona ustanowi waciwo autoLink wobec obiektu email|web, dziki czemu kontrolka
znajdzie i podwietli dany adres URL lub e-mail. Co wicej, kiedy uytkownik kliknie jeden z podwietlonych elementw, system uruchomi aplikacj pocztow z otwart do edycji wiadomoci
z ju wpisanym adresem e-mail lub przegldark stron WWW z wpisanym adresem URL. W jzyku XML atrybut ten znajdowaby si wewntrz znacznika TextView i wygldaby nastpujco:
<TextView ... android:autoLink="email|web" ... />
gdzie okrelamy ograniczony zbir wartoci, takich jak web, email, phone, map lub none (domylnie) albo all. Jeeli chcemy ustawi waciwo autoLink w kodzie, a nie w pliku XML,
odpowiednie wywoanie metody nosi nazw setAutoLinkMask(). Odczytuje ona wartoci
typu int, reprezentujce podobne do widzianego wczeniej poczenie wartoci, na przykad
Linkify.EMAIL_ADDRESSES|Linkify.WEB_ADDRESSES. W tym celu kontrolka TextView wykorzystuje klas android.text.util.Linkify. Na listingu 6.6 przedstawiono przykad automatycznego korzystania z czy za pomoc kodu.
Jeli chcemy, moemy przywoa statyczn metod addLinks() klasy Linkify w celu znalezienia czy oraz dodania ich do kontrolek TextView lub Spannable. Zamiast korzysta z metody
setAutoLinkMask(), mona wpisa poniszy wiersz ju po wstawieniu tekstu:
Linkify.addLinks(tv, Linkify.ALL);
Kliknicie takiego cza powoduje wywoanie domylnej intencji tego dziaania. Na przykad
po klikniciu adresu URL zostanie uruchomiona przegldarka internetowa z wklejonym adresem. Kliknicie numeru telefonu otworzy ekran wybierania numeru itd. Klasa Linkify moe
sprzga te czynnoci bez najmniejszego problemu.
Klasa Linkify umoliwia rwnie wykrywanie niestandardowych wzorcw, okrela, czy dany
obiekt moe zareagowa na kliknicie, a take decyduje, w jaki sposb uruchomi intencj wywoujc jakie dziaanie po klikniciu. Nie bdziemy wnika w szczegy, warto jednak wiedzie,
e takie opcje s dostpne.
Istnieje duo wicej funkcjonalnoci kontrolki TextView, takich jak atrybuty fontw, minLines
i maxLines, a take wiele, wiele innych. Wikszo z nich posiada do oczywiste przeznaczenie i zachcamy Czytelnikw do eksperymentowania z nimi. Naley jednak pamita, e niektre funkcje tej klasy nie znajd zastosowania dla pl tylko do odczytu. Do obsugi pl
edytowalnych su odpowiednie podklasy, z ktrych jedna zostaa omwiona poniej.
EditText
Kontrolka EditText jest kontrolk klasy TextView. Jak sugeruje jej nazwa, umoliwia
edytowanie tekstu. EditText nie jest tak rozbudowana jak niektre kontrolki tego typu dostpne w internecie, jednak uytkownicy urzdze obsugujcych Androida nie bd raczej
tworzyli w nich rozbudowanych dokumentw co najwyej kilka akapitw. Zatem klasa ta
posiada ograniczony, lecz rozsdnie dobrany zestaw funkcji, ktry moe nawet zaskoczy
niejedn osob. Przykadowo jedn z najwaniejszych waciwoci kontrolki EditText jest
inputType. Moemy skonfigurowa waciwo inputType w taki sposb, aby flaga textAutoCorrect poprawiaa pospolitsze bdy w trakcie pisania. Waciwo textCapWords powoduje przeksztacanie maych liter w due na pocztku zdania, wyrazw itd. Istniej rwnie
pewne opcje stosowane wycznie dla numerw telefonw, hase itp.
185
Istniej starsze, obecnie uznane za przestarzae, sposoby definiowania duych liter, tekstu mieszczcego si w wielu wierszach oraz wielu innych cech. Jeeli zostan one okrelone przy braku
waciwoci inputType, bd normalnie odczytywane, jednak jeli gdziekolwiek waciwo
inputType zostanie zdefiniowana, starsze typy waciwoci bd ignorowane.
Dawnym domylnym zachowaniem kontrolki EditText byo wywietlanie tekstu w jednym
wierszu oraz, w razie potrzeby, w kolejnych wierszach. Inaczej mwic, jeeli uytkownik
wypeni tekstem cay pierwszy wiersz, pojawia si wiersz drugi, trzeci i tak dalej. Mona
byo jednak wymusi korzystanie tylko z jednego wiersza poprzez ustawienie wartoci true we
waciwoci singleLine. W takim wypadku uytkownik musia zmieci cay tekst w jednym
wierszu. W przypadku waciwoci inputType, jeeli nie zdefiniujemy waciwoci textMultiLine,
kontrolka EditText bdzie domylnie ograniczona wycznie do jednej linii tekstu. Jeli wic
chcemy wprowadzi dawny typ domylnego zachowania, umoliwiajcy pisanie w wielu wierszach, musimy we waciwoci inputType ustawi flag textMultiLine.
Jedn z przyjemniejszych funkcji kontrolki EditText jest moliwo okrelenia tekstu podpowiedzi. Taka podpowied bdzie wywietlana nieco janiej i zniknie w momencie, gdy uytkownik zacznie wpisywa swj tekst. Zadaniem podpowiedzi jest powiadomienie uytkownika
o przeznaczeniu danego pola tekstowego, bez koniecznoci zaznaczania i usuwania domylnego
tekstu. W pliku XML atrybut ten wyglda nastpujco: android:hint="Tutaj wprowadzamy
tekst podpowiedzi" lub android:hint="@string/nazwa_podpowiedzi", gdzie nazwa_
podpowiedzi stanowi nazw cigu znakw umieszczonego w pliku /res/values/strings.xml. Piszc
kod, wywoujemy metod setHint(), ktrej argumentem jest cig znakw CharSequence lub
identyfikator zasobu.
AutoCompleteTextView
Kontrolka AutoCompleteTextView jest obiektem klasy TextView z funkcjonalnoci automatycznego wypeniania. Innymi sowy, podczas pisania tekstu przez uytkownika w oknie TextView
bd wywietlane sugestie dokoczenia wyrazu. Na listingu 6.7 zaprezentowano kod kontrolki
AutoCompleteTextView.
Listing 6.7. Stosowanie kontrolki AutoCompleteTextView
<AutoCompleteTextView android:id="@+id/actv"
android:layout_width="fill_parent" android:layout_height="wrap_content" />
AutoCompleteTextView actv = (AutoCompleteTextView) this.findViewById(R.id.actv);
ArrayAdapter<String> aa = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
new String[] {"Angielski", "Hebrajski", "Hindi", "Hiszpaski", "Niemiecki", "Grecki" });
actv.setAdapter(aa);
MultiAutoCompleteTextView
Osoby korzystajce z kontrolki AutoCompleteTextView wiedz, e oferuje ona jedynie podpowiedzi dla caego tekstu w oknie widoku. Inaczej mwic, podczas pisania zdania nie bd wywietlane sugestie dla kadego wyrazu oddzielnie. Do takich zastosowa jest przeznaczona
kontrolka MultiAutoCompleteTextView. Suy ona do wywietlania podpowiedzi w trakcie
wpisywania tekstu przez uytkownika. Na rysunku 6.2 pokazano, e uytkownik wpisa wyraz
Angielski, a po przecinku Ni, co powoduje wywietlenie sugerowanego wyrazu Niemiecki.
Jeeli uytkownik bdzie wypisywa nazwy innych jzykw, aplikacja wywietli kolejne sugestie.
MultiAutoCompleteTextView uywa si tak samo jak obiektu AutoComplete
TextView. Rnica polega na koniecznoci okrelenia miejsca wywietlania kolejnej podpo-
Kontrolki
Jedyn istotn rnic pomidzy listingiem 6.7 a 6.8 jest zastosowanie klasy MultiAuto
CompleteTextView oraz wywoanie metody setTokenizer(). Poniewa w tym wypadku
uwzgldniono obiekt CommaTokenizer, po wprowadzeniu przecinka w polu EditText znw pojawi si okno podpowiedzi korzystajce z tablicy cigw znakw. Inne znaki nie spowoduj
wywietlenia pola sugestii. Zatem jeli nawet uytkownik wpisze wyrazy Francuski Hiszpa,
nie pojawi si sugestia dokoczenia drugiego sowa, poniewa przed nim nie ma przecinka.
187
Innym rodzajem tokenizera obsugiwanego przez Androida jest ten przeznaczony dla adresw e-mail. Nosi on nazw Rfc822Tokenizer. W razie potrzeby zawsze mona utworzy
swj wasny tokenizer.
Kontrolki przyciskw
Przyciski s standardem w kadym rodowisku obsugujcym widety, a Android nie jest wyjtkiem. Do dyspozycji mamy typowy zestaw przyciskw oraz kilka dodatkw. W nastpnych
podpunktach zajmiemy si trzema rodzajami kontrolek przyciskw: przyciskiem podstawowym, przyciskiem obrazkowym oraz przyciskiem przeczania. Na rysunku 6.3 zosta pokazany
interfejs uytkownika zawierajcy wszystkie trzy rodzaje przyciskw. S to kolejno: przycisk
podstawowy, przycisk obrazkowy oraz przycisk przeczania.
Kontrolka Button
Klas przycisku podstawowego w Androidzie jest android.widget.Button. Niewiele mona
powiedzie na temat tego rodzaju przyciskw, poza omwieniem obsugi zdarze wyzwalanych
klikniciem. Na listingu 6.9 zosta zaprezentowany fragment ukadu graficznego zapisanego
w jzyku XML dla kontrolki Button, a take kod Java, ktry mona wprowadzi do metody
onCreate() naszej aktywnoci. Taki podstawowy przycisk bdzie wyglda tak jak grny
przycisk widoczny na rysunku 6.3.
Listing 6.9. Obsuga zdarze wyzwalanych klikniciem w obiekcie Button
<Button android:id="@+id/ccbtn1"
android:text="@string/basicBtnLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
Button btn = (Button)this.findViewById(R.id.ccbtn1);
btn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse(http://www.androidbook.com));
Na listingu 6.9 zaprezentowano sposb rejestrowania zdarzenia wywoanego klikniciem. Dokonuje si tego poprzez wywoanie metody setOnClickListener() wobec interfejsu onClick
Listener. Aby obsuy zdarzenia wywoywane klikniciem obiektu (przycisku) btn, na
bieco utworzono anonimowy obiekt nasuchujcy. Kliknicie przycisku powoduje wywoanie
metody onClick() obiektu nasuchujcego, co w naszym przypadku powoduje otwarcie okna
przegldarki.
Wraz z wydaniem rodowiska Android SDK w wersji 1.6 wprowadzono atwiejszy sposb konfigurowania obsugi kliknicia przycisku (przyciskw). Na listingu 6.10 zosta ukazany fragment
XML dla obiektu Button, w ktrym okrelamy atrybut procedury obsugi, a take kod Java stanowicy procedur obsugi kliknicia.
Listing 6.10. Konfigurowanie procedury obsugi kliknicia dla przycisku
<Button ... android:onClick="myClickHandler" ... />
public void myClickHandler(View target) {
switch(target.getId()) {
case R.id.ccbtn1:
...
Dla obiektu klasy View, reprezentujcego nacinity przycisk, nastpi wywoanie funkcji obsugi
kliknicia wraz z zestawem usug oczekiwanych od tej funkcji. Naley zwrci uwag, w jaki
sposb instrukcja switch zawarta w omawianej metodzie obsugi kliknicia wykorzystuje
identyfikatory zasobw przycisku do uruchomienia procesu. Stosowanie tej metody oznacza,
e obiekty klasy Button nie bd jawnie tworzone w kodzie oraz e ta sama metoda moe by
wykorzystywana take do obsugi wielu przyciskw. Dziki temu struktura interfejsu staje si
bardziej zrozumiaa i przejrzysta. Metoda ta dziaa rwnie w przypadku pozostaych typw
przyciskw. Nie zadziaa ona jednak w wersji 1.5 Androida i starszych. Nie pojawi si informacja
o bdzie; po prostu kliknicie przycisku nie wywoa adnej reakcji.
Kontrolka ImageButton
Przyciski obrazkowe s dostpne w Androidzie dziki klasie android.widget.ImageButton.
Uywanie tego rodzaju obiektu przypomina korzystanie z przycisku podstawowego (listing
6.11). Przycisk obrazkowy bdzie przypomina rodkowy przycisk, widoczny na rysunku 6.3.
Listing 6.11. Stosowanie kontrolki ImageButton
<ImageButton android:id="@+id/imageBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:onClick=myClickHandler
android:src=@drawable/icon />
ImageButton btn = (ImageButton)this.findViewById(R.id.imageBtn);
btn.setImageResource(R.drawable.icon);
189
W tym fragmencie kodu utworzylimy w pliku XML przycisk obrazkowy. Obraz dla tego przycisku
znajduje si w zasobach typu drawable. Plik z tym obrazem musi si znajdowa w katalogu
/res/drawable. W naszym przypadku wykorzystalimy domyln ikon Androida. Na listingu
6.11 pokazalimy rwnie dynamiczny sposb konfiguracji przycisku obrazkowego poprzez
wywoanie metody setImageResource() na przycisku i przekazanie jej identyfikatora zasobu.
Warto zapamita, e wystarczy zastosowa tylko jeden z tych dwch sposobw. Nie trzeba
definiowa przycisku obrazkowego jednoczenie w kodzie oraz w pliku XML.
Interesujc funkcj przycisku obrazkowego jest moliwo ustawienia przezroczystego ta.
W ten sposb dowolny obraz mona ustawi tak, aby zachowywa si jak przycisk.
Poniewa przycisk obrazkowy moe si zasadniczo rni od zwykego przycisku, mona dostosowa jego wygld, gdy znajduje si w dwch pozostaych stanach. Warto bowiem przypomnie, e oprcz normalnego stanu przyciski mog si znale w stanie uaktywnienia oraz
zosta wcinite. Stan uaktywnienia oznacza po prostu, e przycisk znajduje si w stanie gotowoci. Moemy uaktywni przycisk za pomoc klawiszy strzaek klawiatury lub D-pada1. Przycisk jest wcinity, gdy jego wygld zmienia si po wciniciu, ale uytkownik nie zdy go
jeszcze puci. Aby zdefiniowa trzy obrazy dla jednego przycisku oraz przypisa kady
z nich do okrelonego stanu, konfigurujemy selektor. Jest to niewielki plik XML, umieszczony
w katalogu /res/drawable projektu. Jest to zachowanie cokolwiek sprzeczne z logik, poniewa
w katalogu tym umieszczamy plik XML, a nie rysunek. Mimo to wanie tutaj musi si znale
selektor. Zawarto pliku selektora zostaa ukazana na listingu 6.12.
Listing 6.12. Wykorzystanie selektora wraz z kontrolk ImageButton
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@drawable/button_pressed" /> <!-- wcinity -->
<item android:state_focused="true"
android:drawable="@drawable/button_focused" />
<item android:drawable="@drawable/icon" />
</selector>
Kontrolka ToggleButton
Kontrolka ToggleButton, taka jak pole wyboru lub przycisk opcji, reprezentuje kategori przyciskw dwustanowych. Przycisk taki moe si znajdowa w stanie wczonym lub wyczonym.
Domylnym zachowaniem przycisku ToggleButton jest wywietlanie zielonego paska w stanie
wczonym i wyszarzonego w stanie wyczonym. Co wicej, tekst przycisku brzmi On, gdy
przycisk jest wczony, i zmienia si na Off po jego wyczeniu. Istnieje moliwo modyfikowania tekstu pojawiajcego si w poszczeglnych stanach kontrolki ToggleButton, jeeli domylne ustawienia nie pasuj do tworzonej aplikacji. Jeli na przykad taki przycisk ma umoliwia kontrol procesu przebiegajcego w tle, mona umieci wyrazy Uruchom oraz Zatrzymaj
poprzez zdefiniowanie waciwoci android:textOn oraz android:textOff.
Na listingu 6.13 ukazano przykad. Przycisk przeczania jest widoczny na dole rysunku 6.3
i znajduje si w pozycji On, wic na etykiecie umieszczonej pod nim widnieje napis Stop.
Listing 6.13. Przycisk przeczania w Androidzie
<ToggleButton android:id="@+id/cctglBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Przycisk przeczania"
android:textOn="Uruchom"
android:textOff="Zatrzymaj"/>
Kontrolka CheckBox
Kontrolka CheckBox jest kolejnym przyciskiem dwustanowym, umoliwiajcym uytkownikowi
wybranie stanu. Rnica polega na tym, e w wielu sytuacjach uytkownicy nie postrzegaj
jej jako przycisku bezporednio wywoujcego akcj. Jednak z punktu widzenia programisty
Androida takie pole wyboru jest przyciskiem i mona na nim wykonywa te same czynnoci
co na przycisku.
W Androidzie pole wyboru jest tworzone poprzez ustanowienie instancji klasy
android.
191
Kontrolka RadioButton
Kontrolki przyciskw opcji s integralnym elementem kadego rodowiska projektowego
interfejsw UI. Przycisk opcji daje uytkownikowi kilka moliwoci wyboru, ale tylko jedna
z nich moe zosta zaznaczona. eby taki model dziaa skutecznie, przyciski opcji przewanie
nale do grupy. W takiej grupie w danym momencie moe by zaznaczony tylko jeden przycisk opcji.
eby utworzy grup przyciskw opcji w Androidzie, naley najpierw stworzy element Radio
a nastpnie wypeni go tymi przyciskami. Na listingu 6.17 zosta zaprezentowany przykad, a rysunek 6.5 stanowi jego ilustracj.
Group,
193
Listing 6.18 stanowi dowd, e mona umieci kontrolki niebdce czci klasy RadioGroup
wewntrz grupy opcji. Powinnimy take wiedzie, e grupa opcji moe wymusza zaznaczenie
tylko jednego przycisku jedynie wobec przyciskw opcji znajdujcych si w tym pojemniku.
Inaczej mwic, przycisk opcji o identyfikatorze anotherRadBtn nie bdzie objty dziaaniem
grupy opcji przedstawionej na listingu 6.18, poniewa nie jest jej elementem podrzdnym.
Istnieje moliwo programowego sterowania obiektami klasy RadioGroup. Na przykad mona
w ten sposb uzyska odniesienie do grupy opcji oraz doda przycisk opcji (lub inny rodzaj
kontrolki). Koncepcja ta zostaa zademonstrowana na listingu 6.19.
Listing 6.19. Dodanie w kodzie kontrolki RadioButton do pojemnika RadioGroup
RadioGroup radGrp = (RadioGroup)findViewById(R.id.radGrp);
RadioButton newRadioBtn = new RadioButton(this);
newRadioBtn.setText("Wieprzowina");
radGrp.addView(newRadioBtn);
Po zaznaczeniu przez uytkownika przycisku opcji w grupie opcji nie bdzie mona usun
tego zaznaczenia za pomoc powtrnego kliknicia. Jedynym sposobem usunicia zaznaczenia wszystkich przyciskw opcji w tej grupie jest wywoanie metody clearCheck() w obiekcie
RadioGroup.
Oczywicie, Czytelnik moe zechcie wykorzysta klas RadioGroup do czego bardziej interesujcego. Prawdopodobnie nie chce za kadym razem sprawdza, czy kady przycisk RadioButton
jest zaznaczony. Na szczcie klasa RadioGroup posiada kilka metod, ktre mog si tu przyda.
Przedstawiamy je na listingu 6.20. Odpowiednik XML tego kodu znajduje si na listingu 6.18.
Listing 6.20. Wykorzystanie klasy RadioGroup w sposb programowy
public class RadioGroupActivity extends Activity {
protected static final String TAG = "RadioGroupActivity";
195
Kontrolka ImageView
Jedn z najwaniejszych kontrolek, ktrych jeszcze nie omwilimy, jest ImageView. Jest ona
stosowana do wywietlania obrazw, pochodzcych z plikw, dostawcw treci lub zasobw,
na przykad typu drawable. Mona rwnie zdefiniowa wycznie kolor, ktry kontrolka
ImageView bdzie wywietlaa. Na listingu 6.21 zaprezentowano kilka kontrolek ImageView,
a nastpnie przedstawiono przykadowy kod ukazujcy proces tworzenia tej klasy.
W tym przykadzie definiujemy cztery obrazy za pomoc jzyka XML. Pierwszy stanowi po prostu ikon naszej aplikacji. Drugi jest szarym paskiem, szerokim, ale niezbyt wysokim. Trzecia definicja nie wskazuje rda obrazu w kodzie XML, natomiast przypisuje identyfikator
(image3), za pomoc ktrego mona programowo ustawi obraz. Czwarty obraz jest kolejnym
z zasobw typu drawable, dla ktrego nie tylko okrelamy ciek do pliku rdowego, lecz
rwnie jego maksymalne rozmiary, a take wskazujemy, co ma si sta z tym obrazem, jeli
przekroczy naoone ograniczenia rozmiarw. W tym przypadku klasa ImageView wyrodkuje
go i przeskaluje do zaoonych rozmiarw.
W kodzie Java z listingu 6.21 widzimy kilka sposobw ustawiania obrazu image3. Oczywicie,
najpierw musimy uzyska odniesienie do kontrolki ImageView za pomoc identyfikatora
zasobw. Pierwsza metoda ustawiania, setImageResource(), zwyczajnie wykorzystuje identyfikator obrazu do jego zlokalizowania oraz dostarczenia go kontrolce ImageView. Druga metoda ustawiania korzysta z klasy BitmapFactory do wczytania zasobu obrazu do obiektu Bitmap,
a nastpnie ustanawia kontrolk ImageView wobec tego obiektu. Warto wiedzie, e w obiekcie
Bitmap mona wprowadza pewne modyfikacje przed wczytaniem go do kontrolki ImageView,
jednak w naszym przykadowym kodzie niczego nie zmieniamy. Ponadto klasa BitmapFactory
zawiera kilka metod sucych do tworzenia obiektu Bitmap, na przykad z tablicy bajtw albo
z klasy InputStream. Moglibymy wykorzysta metod InputStream do odczytania obrazu
z serwera sieciowego, utworzy obraz Bitmap, a nastpnie ustawi klas ImageView.
197
Trzecie ustawienie okrela obiekt Drawable jako rdo obrazu. W naszym przykadzie rdo
to wskazuje plik znajdujcy si na karcie SD. eby opisywany kod zadziaa, trzeba umieci
na karcie SD plik z odpowiedni nazw. Podobnie jak miao to miejsce w przypadku klasy
BitmapFactory, klasa Drawable posiada kilka metod pozwalajcych na tworzenie obiektw
typu Drawable, w tym ze strumienia XML.
Ostatnia metoda ustawiania obrazu polega na pobraniu identyfikatora URI obrazu i wykorzystaniu go jako rda obrazu. Nie naley jednak sdzi, e identyfikator URI kadego obrazu nadaje si do tego celu. Ta metoda suy do wykorzystywania obrazw dostpnych lokalnie, znajdujcych si w urzdzeniu, a nie obrazw wyszukiwanych w internecie. Aby takie obrazy internetowe
mogy by rdami dla kontrolki ImageView, najlepiej zastosowa klasy BitmapFactory oraz
InputStream.
199
Kod na listingu 6.23 ustawia dat na 10 grudnia 2008 roku. Zwrmy uwag, e dla nazw miesicy warto wewntrzna rozpoczyna si od zera, co oznacza, e stycze posiada warto 0,
a grudzie 11. W przypadku klasy TimePicker wybrano godzin 10:10. Warto wiedzie,
e kontrolka obsuguje wywietlanie czasu w formacie dwudziestoczterogodzinnym. Jeeli
w tych kontrolkach nie zostan ustawione adne wartoci, domylnymi bd aktualne data i czas,
skonfigurowane w urzdzeniu.
Android wykorzystuje rwnie te kontrolki jako okna dialogowe, na przykad DatePickerDialog oraz TimePickerDialog. Kontrolki te przydaj si, w przypadku gdy maj zosta wywietlone uytkownikowi w celu zmuszenia go do dokonania jakiego wyboru. Okna dialogowe
zostay szczegowo omwione w rozdziale 8.
DigitalClock
oraz
Jak wida, w zegarze cyfrowym mona dodatkowo odczyta sekundy. Zegar analogowy w Androidzie posiada dwie wskazwki, jedna wskazuje godziny, a druga minuty. Aby umieci te
zegary w ukadzie graficznym, moemy wykorzysta wzy XML widoczne na listingu 6.24.
Listing 6.24. Dodawanie obiektw DigitalClock i AnalogClock w jzyku XML
<DigitalClock
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<AnalogClock
android:layout_width="wrap_content" android:layout_height="wrap_content" />
Kontrolki te pozwalaj jedynie na wywietlanie biecego czasu, nie zapewniaj jednak moliwoci modyfikowania daty ani czasu. S to zatem zwyke zegary, ktrych jedyn funkcj jest
wywietlanie aktualnej godziny. Zatem w przypadku potrzeby zmiany daty lub czasu naley
stosowa kontrolki DatePicker i TimePicker lub DatePickerDialog i TimePickerDialog.
Przydatnym szczegem jest, e obydwie kontrolki DigitalClock oraz AnalogClock
bd automatycznie aktualizowa czas, bez koniecznoci ustawiania czegokolwiek. Oznacza
to, e w przypadku zegara cyfrowego sekundy bd same odliczane, a w zegarze analogowym
wskazwki bd si poruszay samoistnie, bez potrzeby zapewniania dodatkowej obsugi.
Kontrolka MapView
Kontrolka com.google.android.maps.MapView umoliwia wywietlanie mapy. Mona utworzy
egzemplarz tej kontrolki w pliku XML ukadu graficznego lub w kodzie Java, jednak wykorzystujca j aktywno musi rozszerzy klas MapActivity. Klasa ta obsuguje przetwarzanie
wielowtkowych da adowania mapy, przeprowadzanie procesu buforowania i tak dalej.
Na listingu 6.25 zosta zaprezentowany przykad utworzenia obiektu MapView.
Listing 6.25. Utworzenie kontrolki MapView w pliku XML ukadu graficznego
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.google.android.maps.MapView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="myAPIKey"
/>
</LinearLayout>
Szczegowe informacje na temat kontrolki MapView zostay zawarte w rozdziale 17., w ktrym
opiszemy usugi oparte na wyznaczaniu pooenia geograficznego. Znajduj si tam rwnie
informacje, w jaki sposb uzyska wasny klucz API mapowania.
Dziaanie adapterw
Zanim zajmiemy si kontrolkami listy w Androidzie, musimy przedstawi pojcie adaptera.
Kontrolki listy su do wywietlania zbiorw danych. Jednak zamiast uywa jednego typu kontrolki zarwno do obsugi wywietlania, jak i zarzdzania danymi, Android dzieli te dwa zadania
pomidzy kontrolki listy i adaptery. Kontrolki listy s rozszerzeniem klasy android.widget.
AdapterView i dziel si na nastpujce kategorie: ListView, GridView, Spinner oraz Gallery (rysunek 6.8).
Sama klasa AdapterView rozszerza klas android.widget.ViewGroup, co oznacza, e widoki
ListView, GridView i inne s kontrolkami-pojemnikami. Innymi sowy, kontrolki listy wywietlaj zbir widokw potomnych. Zadaniem adaptera jest zarzdzanie danymi pojemnika
AdapterView oraz dostarczenie mu potomnych widokw. Przyjrzyjmy si, jak to dziaa, analizujc
adapter SimpleCursorAdapter.
201
Aby odwzorowa dane w kontrolce ListView, klasa SimpleCursorAdapter musi posiada dostp do identyfikatora potomnego ukadu graficznego. Ten potomny ukad musi opisywa ukad
graficzny wszystkich elementw danych (widocznych po prawej stronie), ktre maj zosta wywietlone po lewej stronie. W tym przypadku ukad graficzny nie rni si od ukadw, ktre
prezentowalimy podczas omawiania aktywnoci, musi on tylko opisywa ukad graficzny pojedynczego wiersza z pojemnika ListView. Jeli na przykad posiadamy zestaw wynikowy dostarczony od dostawcy treci Contacts, a w pojemniku ListView chcemy wywietla wycznie
nazw danego kontaktu, trzeba okreli ukad graficzny opisujcy wygld takiego pola zawierajcego nazw kontaktu. Aby w kadym wierszu pojemnika ListView wywietla nazw i obraz,
ktre pochodz z zestawu wynikowego, taki ukad graficzny musiaby definiowa sposb wywietlania nazwy oraz obrazu.
Nie oznacza to wcale, e trzeba dostarcza oddzieln specyfikacj ukadu graficznego dla kadego
pola w zestawie wynikowym ani e w zestawie wynikowym maj znale si fragmenty danych, za
pomoc ktrych trzeba wypeni wszystkie wiersze pojemnika ListView. Jako przykad za moment pokaemy, w jaki sposb mona wybiera wiersze za pomoc pl wyboru umieszczonych
w widoku ListView, gdzie te pola nie musz by zestawami danych z zestawu wynikowego. Zademonstrujemy take, w jaki sposb uzyska dostp do danych z zestawu wynikowego, jeli te
dane nie s czci pojemnika ListView. A chocia cay czas rozmawiamy o widokach ListView,
oglniejszy charakter. Wracajc do rysunku 6.9, obszar po lewej stronie moe by galeri, natomiast prawa strona prost tabel z obrazami. Na razie nie utrudniajmy sobie jednak zadania
i przyjrzyjmy si dokadniej klasie SimpleCursorAdapter.
Konstruktor klasy SimpleCursorAdapter wyglda nastpujco:
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
Adapter ten przeksztaca krotk w kursorze do widoku podrzdnego wzgldem kontrolkipojemnika. Definicja widoku potomnego zostaa umieszczona w zasobie XML (parametr
childLayout). Zwrmy uwag, e krotka w kursorze moe zawiera wiele kolumn. Aby
wskaza, ktre maj zosta zaznaczone w adapterze SimpleCursorAdapter, definiuje si
tablic z nazwami kolumn. W tym celu stosuje si parametr from.
W podobny sposb, poniewa kada wybrana kolumna musi zosta odwzorowana w obiekcie
klasy View ukadu graficznego, naley utworzy identyfikatory dla parametru to. Pomidzy
wybran kolumn a kontrolk View wywietlajc dane w kolumnie istnieje odwzorowanie
typu jeden do jednego, zatem tablice wartoci parametrw from i to musz zawiera t sam
liczb elementw. Jak ju wczeniej wspomnielimy, widok potomny moe by innym typem
widoku; to wcale nie musi by kontrolka TextView. Mona zamiast tego wprowadzi na przykad kontrolk ImageView.
Widok ListView i nasz adapter wsppracuj ze sob w przemylany sposb. Kiedy pojemnik
ListView prbuje wywietli wiersz danych, wywouje metod getView() adaptera i przekazuje
pooenie wywietlanego wiersza. Adapter w odpowiedzi tworzy odpowiedni widok potomny
za pomoc ukadu graficznego ustanowionego w swoim konstruktorze, uwzgldniajc dane
pobrane z waciwego rekordu pochodzcego z zestawu wynikowego. Zatem widok ListView
nie musi obsugiwa danych po stronie adaptera. Widok ten wycznie wywouje potrzebne
potomne widoki. Jest to punkt krytyczny, gdy w ten sposb pojemnik ListView nie musi tworzy oddzielnego widoku potomnego dla kadego wiersza danych. Pojemnik ListView tworzy
tylko tyle widokw potomnych, ile trzeba wywietli. Z technicznego punktu widzenia, jeeli
przewidujemy wywietlanie tylko dziesiciu wierszy, widok ListView mgby utworzy tylko
dziesi potomnych ukadw graficznych, nawet jeli zestaw wynikowy skadaby si z setek rekordw. W rzeczywistoci system moe wywoywa wicej widokw potomnych, poniewa zazwyczaj Android przechowuje dodatkowe obiekty na wypadek potrzeby szybszego wywietlenia
nowego wiersza. Wniosek wynika z tego taki, e potomne widoki pojemnika ListView mog
ulega cigemu przetwarzaniu. Zajmiemy si tym dokadniej w dalszej czci ksiki.
Na rysunku 6.9 mona zauway pewn elastyczno w stosowaniu adapterw. Poniewa kontrolka listy korzysta z adaptera, mona podstawia rne rodzaje adapterw w zalenoci od
rodzaju danych oraz widokw podrzdnych. Jeeli na przykad klasa AdapterView nie bdzie
zapeniana danymi z dostawcy treci lub bazy danych, nie ma potrzeby, eby uywa adaptera
SimpleCursorAdapter. Mona wtedy zastosowa jeszcze prostszy adapter ArrayAdapter.
203
W dalszym cigu przekazujemy kontekst (np. this) oraz identyfikator zasobu potomnego ukadu graficznego. Zamiast jednak przekazywa tablic from specyfikacji pola danych, jako rzeczywiste dane przekazujemy tablic cigw znakw. Nie przekazujemy kursora ani tablicy identyfikatorw zasobw obiektu View. Zakadamy tutaj, e potomny ukad graficzny skada si
z pojedynczej kontrolki TextView oraz e bdzie on wykorzystywany przez klas ArrayAdapter
jako miejsce docelowe dla cigw znakw przechowywanych w tablicy danych.
Zaprezentujemy teraz przyjemny skrt dla identyfikatora zasobu childLayout. Zamiast tworzy
wasny plik ukadu graficznego do obsugi obiektw listy, moemy skorzysta z predefiniowanych ukadw graficznych Androida. Zauwamy, e przedrostkiem w identyfikatorze potomnego
ukadu graficznego jest android.. Zamiast przeszukiwa lokalny katalog /res, Android przeszukuje swj wasny. Mona przejrze ten folder poprzez otwarcie katalogu zawierajcego zestaw Android SDK i wybranie platforms/<wersja-androida>/data/res/layout. Znajdziemy tu
element simple_list_item_1.xml, w ktrym wida definicj prostej kontrolki TextView. To
wanie t kontrolk wykorzystuje klasa ArrayAdapter do utworzenia widoku (w metodzie
getView()), ktry zostanie przekazany pojemnikowi ListView. Warto przejrze zawarte tu
katalogi, eby znale predefiniowane ukady graficzne dla wszelakich rodzajw zastosowa.
W dalszej czci ksiki wykorzystamy jeszcze niektre z nich.
Klasa ArrayAdapter posiada rwnie inne konstruktory. Jeeli potomny ukad graficzny nie jest
prostym widokiem TextView, mona przekaza identyfikator ukadu graficznego wiersza oraz
identyfikator kontrolki TextView otrzymujcej dane. Jeli nie mamy przygotowanej do przekazania tablicy cigw znakw, moemy zastosowa metod createFromResource(). Listing 6.26
stanowi przykad, w ktrym tworzymy klas Adapter dla obiektu typu Spinner:
Listing 6.26. Utworzenie adaptera ArrayAdapter z pliku zasobw typu string
<Spinner android:id="@+id/spinner"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
Listing 6.26 skada si z trzech czci. Pierwsza stanowi ukad graficzny obiektu Spinner zapisany w pliku XML. Druga cz, napisana w jzyku Java, ukazuje nam, w jaki sposb mona
utworzy klas ArrayAdapter, ktrej rdo danych zostao zdefiniowane w pliku zasobw typu
String. Za pomoc tej metody moemy nie tylko uzewntrzni zawarto listy w pliku XML,
lecz rwnie korzysta ze zlokalizowanych wersji list. Obiektami typu Spinner zajmiemy si
nieco pniej, na razie wystarczy nam wiedza, e obiekt tego typu posiada widok pozwalajcy na
wywietlenie aktualnie wybranej wartoci oraz widok listy elementw, ktre mona wybra.
W zasadzie mamy tu do czynienia z list rozwijaln. Trzeci cz listingu 6.26 stanowi plik zasobw umieszczony w katalogu /res/values/planets.xml, ktry jest wczytywany w celu uruchomienia klasy ArrayAdapter.
Warto wspomnie, e klasa ArrayAdapter pozwala na dynamiczne modyfikowanie wykorzystywanych danych. Na przykad metoda add() dodaje now warto na kocu tablicy. Metoda
insert() wprowadza now warto w okrelonej pozycji tablicy, natomiast metoda remove()
usuwa obiekt z tablicy. Moemy take wywoa metod sort(), ktra uporzdkuje tablic.
Oczywicie, po wykonaniu tych wszystkich czynnoci tablica danych zostaje zdesynchronizowana z pojemnikiem ListView, zatem naley wtedy wywoa metod notifyDataSetChanged()
adaptera. W ten sposb zsynchronizujemy ponownie kontrolk ListView z adapterem.
Ponisza lista podsumowuje rodzaje adapterw dostpnych w systemie Android:
ArrayAdapter<T>. Adapter ten znajduje si na szczycie oglnej tabeli wasnych
obiektw. Jest przeznaczony do stosowania z kontrolkami ListView.
CursorAdapter. Adapter ten, rwnie uywany przy kontrolkach ListView,
dostarcza dane listy poprzez kursor.
SimpleAdapter. Jak sama nazwa sugeruje, mamy do czynienia z prostym adapterem.
Zazwyczaj uywany jest do zapeniania listy danymi statycznymi (rwnie z zasobw).
ResourceCursorAdapter. Ten adapter rozszerza klas CursorAdapter i tworzy
widoki z zasobw.
SimpleCursorAdapter. Adapter ten rozszerza klas ResourceCursorAdapter i tworzy
widoki TextView/ImageView z kolumn w kursorze. Widoki s zdefiniowane w zasobach.
Przedstawilimy wystarczajco dobrze zagadnienie adapterw, aby zaprezentowa rzeczywiste przykady korzystania z nich oraz z kontrolek listy (znanych take pod nazw kontrolek
AdapterView). Do dziea.
Wykorzystywanie adapterw
wraz z kontrolkami AdapterView
Po zapoznaniu si z tematyk adapterw czas zaprzc je do pracy i dostarczy im dane przesyane do kontrolek listy. W tym podrozdziale rozpoczniemy od omwienia pierwszej kontrolki
tego typu ListView. Nastpnie przyjrzymy si mechanizmowi tworzenia wasnego adaptera,
a na kocu opiszemy inne rodzaje kontrolek listy: GridView, obiekty typu Spinner i galerie.
205
W kolejnym wiczeniu wypenimy cay ekran kontrolk ListView, wic nie trzeba jej nawet
okrela w pliku ukadu graficznego main.xml. Na listingu 6.27 umieszczono kod Java kontrolki
ListActivity.
Listing 6.27. Dodawanie elementw do kontrolki ListView
public class ListDemoActivity extends ListActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Cursor c = managedQuery(People.CONTENT_URI,
null, null, null, People.NAME);
String[] cols = new String[] {People.NAME};
Kod z listingu 6.27 powoduje utworzenie kontrolki ListView, zapenionej pobran z urzdzenia list kontaktw. W tym przykadzie damy od urzdzenia listy kontaktw. W celach demonstracyjnych zaznaczamy wszystkie pola pojemnika Contacts (na przykad za pomoc pierwszego
parametru null w metodzie managedQuery()) i stosujemy sortowanie wedug wartoci pola
People.NAME (do czego suy ostatni parametr we wspomnianej metodzie managedQuery()).
Nastpnie tworzymy projekcj (kolumn), dziki ktrej wybieramy wycznie nazwy kontaktw
dla pojemnika ListView projekcja definiuje interesujce nas kolumny. Kolejnym etapem jest
utworzenie tablicy identyfikatorw zasobw (widokw), ktra pozwalaaby na odzwierciedlanie
nazwy kolumny (People.NAME) wobec kontrolki TextView (android.R.id.text1). W kolejnym etapie tworzymy adapter kursora i konfigurujemy adapter listy. Klasa adaptera jest przystosowana do przegldania krotek w danych rdowych i pobierania nazw kontaktw w sposb umoliwiajcy zapenienie interfejsu uytkownika.
Musimy wykona jeszcze jedn czynno, zanim aplikacja zadziaa. Poniewa w tym wiczeniu
aplikacja uzyskuje dostp do listy kontaktw telefonu, naley przydzieli jej odpowiednie uprawnienia. Informacje dotyczce zabezpiecze przedstawiono w rozdziale 10., teraz wic wyjanimy
jedynie, w jaki sposb udostpni dane kontrolce ListView. Naley dwukrotnie klikn nazw
pliku AndroidManifest.xml w projekcie, a nastpnie wybra zakadk Permissions. W dalszej
kolejnoci trzeba klikn przycisk Add, zaznaczy opcj Uses Permission i na kocu klikn OK. Naley przewin list Name a do pozycji android.permission.READ_CONTACTS.
Okno rodowiska Eclipse powinno wyglda tak jak na rysunku 6.11. Mona teraz zapisa
plik AndroidManifest.xml i uruchomi aplikacj w emulatorze. Moe zaistnie potrzeba dodania
kontaktw za pomoc aplikacji Kontakty, zanim jakiekolwiek nazwiska pojawi si w naszym
przykadowym programie.
Zauwamy, e metoda onCreate() nie ustanawia widoku treci danej aktywnoci. Poniewa
bazowa klasa ListActivity zawiera ju kontrolk ListView, naley jedynie zapewni jej dostp do danych. Wykorzystalimy w tym przykadzie kilka skrtw; pierwszy polega na zastosowaniu gwnego ukadu graficznego w pojemniku ListView. Wykorzystalimy rwnie predefiniowany ukad graficzny Androida w potomnym widoku (identyfikator android.R.layout.
simple_list_item_1), w ktrym znajduje si predefiniowana kontrolka TextView (android.
R.id.text1). Podsumowujc, ta konfiguracja wcale nie jest skomplikowana.
207
W efekcie nasza aktywno implementuje interfejs onItemClickListener, co oznacza, e bdziemy otrzymywa wywoanie zwrotne za kadym razem, gdy uytkownik kliknie jaki element pojemnika ListView. Jak wida po zapoznaniu si z metod onItemClick(), otrzymujemy
wiele informacji na temat kliknitego elementu, w tym takie jak kliknity widok, pooenie
kliknitego elementu w pojemniku ListView oraz zgodny z adapterem identyfikator tego elementu. Poniewa wiemy, e pojemnik ListView skada si z kontrolek TextView, zakadamy,
e otrzymalimy wanie tak kontrolk i e generujemy j przez wywoanie metody getText(),
sucej do odczytania nazwy kontaktu. Warto pooenia reprezentuje umiejscowienie elementu na penej licie obiektw w pojemniku ListView i jest ona liczona od zera. Zatem pierwszy element na licie posiada przypisan warto 0.
Warto identyfikatora cakowicie zaley od adaptera oraz rda danych. W naszym przykadzie wysyamy zapytania do dostawcy treci Contacts, zatem zgodnie z adapterem mamy
tu do czynienia z identyfikatorem _ID rekordu od dostawcy treci. Jednak w innych przypadkach rdo danych moe nie pochodzi od dostawcy treci, wic nie naley sdzi, e moemy
zawsze tworzy identyfikator URI, jak w omawianym przykadzie. Jeli korzystalimy z adaptera
ArrayAdapter odczytujcego wartoci z pliku XML zasobw, uzyskany przez nas identyfikator
bdzie prawdopodobnie stanowi pozycj danej wartoci w tablicy danych oraz, w istocie, moe
by dokadnie wartoci tej pozycji.
Podczas omawiania klasy ArrayAdapter wspominalimy, e metoda notifyDataSetChanged()
suy do synchronizowania adaptera z pojemnikiem ListView w przypadku modyfikowania
danych. Przeprowadmy may eksperyment na naszym przykadzie. Kliknijmy jeden z elementw
listy, co spowoduje wywietlenie aplikacji Kontakty. Edytujmy teraz ten kontakt i zmiemy jego
nazw. Naley klikn przycisk Gotowe, a nastpnie Cofnij, aby wrci do naszej aplikacji. Zauwaymy, e nazwa kontaktu w pojemniku ListView zostaa automatycznie zaktualizowana.
wietne, nieprawda? Pojemnik ListView zosta automatycznie zaktualizowany za pomoc klasy
SimpleCursorAdapter i dostawcy treci Contacts. Jednak w przypadku klasy ArrayAdapter
trzeba samodzielnie przywoa metod notifyDataSetChanged().
To nie byo wcale takie trudne. Utworzylimy wasny pojemnik ListView zawierajcy nazwy
kontaktw, a po klikniciu danego elementu zostaa uruchomiona aplikacja Kontakty z informacjami o wybranej osobie. A co w przypadku, gdy chcemy najpierw zaznaczy kilka nazwisk
i w jaki sposb dziaa na takiej grupie? W nastpnej aplikacji zmodyfikujemy ukad graficzny
listy i dodamy do niej pola wyboru, nastpnie za wprowadzimy do interfejsu uytkownika przycisk pozwalajcy na przetwarzanie podgrupy zaznaczonych elementw.
209
Rysunek 6.12. Dodatkowy przycisk umoliwiajcy uytkownikowi wysanie listy zaznaczonych elementw
Gwny ukad graficzny naszej aplikacji zosta umieszczony na listingu 6.29 i zawiera definicj
interfejsu aktywnoci kontrolek ListView oraz Button.
Listing 6.29. Przesonicie kontrolki ListView, do ktrej odnosi si klasa ListActivity
<?xml version="1.0" encoding="utf-8"?>
211
Wracamy tutaj do wywoywania metody setContentView() w celu ustawienia interfejsu uytkownika w aktywnoci. Natomiast w konfiguracji adaptera przekazujemy kolejny predefiniowany widok wobec elementu pojemnika ListView (android.R.layout.simple_list_item_
multiple_choice), w wyniku czego kady wiersz zawiera kontrolki TextView i CheckBox.
Jeeli zajrzymy do pliku zawierajcego ukad graficzny, zauwaymy kolejn podklas kontrolki
TextView, noszc nazw CheckedTextView. Ten specjalny rodzaj kontrolki TextView zosta
stworzony z myl o pojemnikach ListView. Mwilimy przecie, e w tym folderze, zawierajcym predefiniowane pliki ukadw graficznych, mona znale interesujce rzeczy! Warto
zauway, e identyfikator tej kontrolki posiada warto text1, ktr musielimy przekaza
w tablicy widokw konstruktorowi klasy SimpleCursorAdapter.
Poniewa chcemy, aby uytkownik mg zaznacza poszczeglne wiersze, wprowadzamy tryb
wybierania CHOICE_MODE_MULTIPLE. Domyln wartoci tego trybu jest CHOICE_MODE_NONE.
Ostatni moliwoci, jak moemy wybra, jest CHOICE_MODE_SINGLE. Aby uy tego ostatniego
trybu, trzeba by wprowadzi inny ukad graficzny, najprawdopodobniej android.R.layout.
simple_list_item_single_choice.
W tym przykadzie zaimplementowalimy podstawowy przycisk, wywoujcy metod doClick()
naszej aktywnoci. Aby nie utrudnia sprawy, nazwy elementw zaznaczanych przez uytkownika bd zapisywane w oknie LogCat. Dobr wieci jest, e wprowadzenie takiego rozwizania jest bardzo proste, z drugiej strony jednak Android wyewoluowa do tego stopnia, e jego
implementacja moe zalee od wersji systemu. Ukazane tu rozwizanie polegajce na wykorzystaniu pojemnika ListView dziaa od wersji 1 Androida (chocia przy wywoywaniu
zwrotnym przycisku korzystamy ze skrtu dostpnego od wersji 1.6 Androida). Oznacza to,
e metoda getCheckedItemPositions() jest stara, ale cigle skuteczna. W wyniku jej dziaania
otrzymujemy tablic okrelajc, czy dany element zosta zaznaczony, czy nie. Zatem teraz
mona sprawdzi wszystkie elementy za pomoc metody array.viewItems.get(i). Metoda
ta przekae warto true, jeli dany wiersz w pojemniku ListView zosta zaznaczony. Dostp
do danych mona uzyska za pomoc kursora. Zatem zamiast wyszukiwa dane w pojemniku
ListView, sprawdzamy informacje zawarte w kursorze. Widok ListView powie nam, w ktrym miejscu adaptera naley szuka.
Po uzyskaniu numeru pozycji zaznaczonego elementu moemy uy metody moveToPosition()
kursora, aby przygotowa aplikacj do odczytu danych. Istnieje inna metoda, speniajca niemal identyczne zadanie getItemAtPosition() klasy ListView. W naszym przypadku element przekazany przez t metod przeksztaciby si na obiekt CursorWrapper. Jak ju wczeniej stwierdzilimy, w innych przypadkach moemy otrzyma odmienne typy obiektw. Obiekt
CursorWrapper pojawia si tylko dlatego, e pracujemy z dostawc treci. Naley rozumie
rdo danych oraz adapter, eby wiedzie, czego si spodziewa.
Moemy nastpnie wykorzysta obiekt Cursor (lub CursorWrapper, jeli go otrzymalimy)
do odczytania informacji powizanych z zaznaczonym wierszem pojemnika ListView. Zauwamy, e w naszym przykadzie odczytujemy nie tylko nazw kontaktu, ale take uwagi na
jego temat, chocia nigdzie ich nie odzwierciedlalimy w tym kontenerze. Jest tak, poniewa
213
Kontrolka GridView
Wikszo narzdzi do tworzenia widetw ma przynajmniej jedn kontrolk definiujc siatk.
Android posiada kontrolk GridView, dziki ktrej dane s wywietlane w takiej siatce. Pamitajmy, e poprzez dane rozumiemy tu tekst, rysunki i tak dalej.
Kontrolka GridControl wywietla informacje w siatce. Algorytm wykorzystania tej kontrolki
polega na zdefiniowaniu siatki w pliku XML ukadu graficznego (listing 6.32), a nastpnie powizaniu danych z t siatk za pomoc klasy android.widget.ListAdapter. Naley te doda
etykiet Uses Permission do pliku AndroidManifest.xml, w przeciwnym wypadku przykadowy
kod nie zadziaa.
Na listingu 6.32 zdefiniowano prost kontrolk GridView w pliku XML ukadu graficznego.
Siatka ta zostaje wczytana do widoku treci aktywnoci. Wygenerowany interfejs uytkownika
mona ujrze na rysunku 6.13.
Siatka przedstawiona na rysunku 6.13 wywietla nazwy kontaktw przechowywanych w urzdzeniu. Postanowilimy umieci kontrolki TextView z tymi nazwami, jednak rwnie dobrze
mona w ich miejsce wstawi obrazy lub inne kontrolki. Ponownie skorzystalimy z moliwoci
predefiniowanych ukadw graficznych. W rzeczywistoci przykad ten jest bardzo podobny do
kodu zamieszczonego na listingu 6.27, istnieje jednak pomidzy nimi kilka istotnych rnic.
Po pierwsze, klasa GridViewActivity rozszerza klas Activity, nie ListActivity. Po drugie,
musimy wywoa metod setContentView() w celu ustanowienia ukadu graficznego dla pojemnika GridView nie ma tu adnych domylnych widokw. Na koniec warto zauway,
e w celu ustawienia adaptera wywoujemy metod setAdapter() na obiekcie GridView, a nie
metod setListAdapter() wobec klasy Activity.
215
Kontrolka Spinner
Kontrolka Spinner peni funkcj rozwijanego menu. Zazwyczaj stosuje si j do wybierania
opcji ze stosunkowo krtkiej listy. Jeeli lista jest zbyt duga do wywietlania, zostaje automatycznie dodany pasek przewijania. Mona j utworzy w pliku XML ukadu graficznego w tak
prosty sposb:
<Spinner
android:id="@+id/spinner" android:prompt="@string/spinnerprompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Chocia z technicznego punktu widzenia obiekt typu Spinner jest kontrolk listy, przypomina
on bardziej prosty widok TextView. Innymi sowy, kiedy kontrolka Spinner pozostaje nieaktywna, wywietlana jest tylko jedna warto. Zadaniem tej kontrolki jest umoliwienie uytkownikowi wyboru z zestawu predefiniowanych wartoci: po klikniciu niewielkiej strzaki lista
wywietla si i uytkownik moe wybra z niej jaki element. Lista ta jest zapeniana tak samo jak
w przypadku pozostaych kontrolek listy, to znaczy za pomoc adaptera. Poniewa kontrolka
typu Spinner czsto jest stosowana w formie rozwijanego menu, powszechnym rozwizaniem
jest pobieranie przez adapter listy opcji z pliku zasobw. Przykadowy sposb wykorzystania
kontrolki Spinner wraz z plikiem zasobw zosta pokazany na listingu 6.33. Zwrmy uwag
na nowy atrybut android:prompt, ustanawiajcy zacht na szczycie listy opcji. Waciwy tekst
zachty znajduje si w pliku /res/values/strings.xml. Jak mona si spodziewa, klasa Spinner
posiada rwnie odpowiedni metod, pozwalajc na umieszczenie zachty w kodzie.
By moe Czytelnik pamita, e plik planets.xml widnia rwnie na listingu 6.26. W omawianym przykadzie ukazujemy sposb utworzenia kontrolki Spinner. Po skonfigurowaniu adaptera zostaje on doczony do tego obiektu. Na rysunku 6.14 widzimy obiekt Spinner w akcji.
Jedn z cech odrniajcych ten obiekt od pozostaych kontrolek list jest obecno dodatkowego
ukadu graficznego, z ktrego naley korzysta podczas pracy z klas Spinner. Na lewym zrzucie
ekranu z rysunku 6.14 widzimy normalny tryb dziaania kontrolki Spinner widoczny jest tu
biecy wybr. W tym przypadku wybrano planet Saturn. Obok znajduje si strzaka informujca, e mamy do czynienia z kontrolk typu Spinner, a jej nacinicie spowoduje wywietlenie si listy dostpnych wartoci. Pierwszy ukad graficzny, dostarczany w postaci para-
217
Kontrolka Gallery
Kontrolka Gallery tworzy list przewijan w poziomie, ktra eksponuje elementy widoczne
w rodkowej czci tej listy. Kontrolka ta przewanie jest wykorzystywana do tworzenia galerii
obrazw, gdzie nawigacja midzy obrazami odbywa si w trybie dotykowym. Mona j utworzy
w pliku XML ukadu graficznego lub w kodzie Java:
<Gallery
android:id="@+id/gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Kontrolka typu Gallery jest najczciej uywana do wywietlania obrazw, zatem adapter
zostanie najprawdopodobniej dostosowany do ich obsugi. W nastpnym punkcie, dotyczcym
niestandardowych adapterw, zaprezentujemy adapter wyspecjalizowany do obsugi obrazw.
Wygld kontrolki Gallery zosta zaprezentowany na rysunku 6.15.
R.drawable.manatee02,
R.drawable.manatee05,
R.drawable.manatee08,
R.drawable.manatee11,
R.drawable.manatee14,
R.drawable.manatee17,
R.drawable.manatee20,
R.drawable.manatee23,
R.drawable.manatee26,
R.drawable.manatee29,
R.drawable.manatee32,
219
221
Chocia przedstawiony kod wydaje si wzgldnie prosty, wiele elementw wymaga tu objanienia. Zaczniemy od klasy Activity, bardzo przypominajcej te, z ktrymi pracowalimy
wczeniej w tym punkcie. Wida te gwny ukad graficzny z pliku gridviewcustom.xml, w ktrym zawarta jest wycznie definicja kontrolki GridView. Musimy uzyska odniesienie do tej
kontrolki z wntrza ukadu graficznego, zatem definiujemy i ustawiamy widok gv. Tworzymy
nasz obiekt ManateeAdapter, przekazujemy mu kontekst i ustanawiamy go wobec kontrolki
GridView. Na razie nie zrobilimy niczego odkrywczego, jednak bez wtpienia zauwaylimy,
e na etapie tworzenia nasz niestandardowy adapter nie wykorzystuje nawet czci tych parametrw co adaptery predefiniowane. Wynika to gwnie z faktu, e mamy cakowit kontrol
nad tym konkretnym adapterem i wykorzystujemy go tylko w tej jednej aplikacji. Gdybymy
chcieli utworzy adapter oglniejszego przeznaczenia, najprawdopodobniej wprowadzilibymy
wicej parametrw. Idmy zatem dalej.
Zadaniem adaptera jest zarzdzanie przekazywaniem danych do obiektw typu View Androida.
Obiekty te s wykorzystywane przez kontrolk listy (w tym przypadku GridView). Dane pochodz z jakiego rda danych. We wczeniejszych przykadach dane byy dostarczane poprzez
obiekt kursora, ktry by przekazywany adapterowi. W omawianym przypadku nasz niestandardowy adapter ma wszystkie informacje o danych oraz ich rdach. Jest w stanie skonstruowa interfejs uytkownika, jeli otrzyma takie danie od kontrolki. Moe rwnie przekaza do ponownego wykorzystania widoki, ktre przestan by potrzebne. Moe wydawa si
dziwne, e adapter musi mie zdolno konstruowania widokw, ale ostatecznie wszystko
razem nabiera sensu.
W trakcie tworzenia instancji omawianego niestandardowego adaptera ManateeAdapter zwyczajowo przekazuje si mu kontekst, ktry bdzie w nim przetrzymywany. Jego przechowywanie bardzo czsto okazuje si przydatne. Drugim zadaniem tego adaptera jest przechowanie
klasy Inflater. To pozwala na popraw wydajnoci w momencie utworzenia nowego widoku,
zwracanego kontrolce listy. Trzecim typowym zadaniem adaptera jest utworzenie obiektu
ViewHolder, przechowujcego obiekty typu View dla zarzdzanych danych. W omawianym
przykadzie przechowujemy po prostu widok ImageView, ale gdyby trzeba byo obsuy dodatkowe pola, wprowadzilibymy je do definicji obiektu ViewHolder. Gdybymy na przykad
posiadali pojemnik ListView, na ktrego kady wiersz skadaby si jeden widok ImageView
i dwie kontrolki TextView, obiekt ten przechowywaby dokadnie jeden widok ImageView
i dwie kontrolki TextView.
Poniewa omawiany adapter suy do obsugi obrazw krw morskich, ustanawiamy tablic
identyfikatorw tych zasobw, za pomoc ktrych zostan utworzone mapy bitowe. Definiujemy rwnie tablic map bitowych, ktre bd stanowiy list danych.
Jak wynika z kodu konstruktora klasy ManateeAdapter, zapisujemy kontekst, tworzymy i przechowujemy klas Inflater, a nastpnie iterujemy poprzez identyfikatory zasobw obrazw
i budujemy tablic bitmap. Ta ostatnia bdzie stanowia nasze dane.
Jak ju si wczeniej dowiedzielimy, ustanowienie adaptera spowoduje, e kontrolka GridView
bdzie wywoywaa wobec niego metody definiujce wywietlane w niej dane. Na przykad
kontrolka gv bdzie wywoywaa metod getCount() adaptera w celu okrelenia liczby wywietlanych obiektw. Bdzie take wywoywana metoda getViewTypeCount() suca do
okrelenia, jak wiele rnych typw widokw moe by wywietlanych wewntrz pojemnika
GridView. W naszym przykadzie przypisujemy jej warto 1. Jeeli jednak chcielibymy
uwzgldni kontener ListView i wprowadzi separatory pomidzy wiersze z danymi, potrzebne byyby dwa typy danych, a wtedy metoda getViewTypeCount() powinna zwraca warto 2.
223
getItemId().
Style i motywy
Android posiada kilka mechanizmw pozwalajcych na zmian stylu widokw w aplikacji.
Najpierw zajmiemy si znacznikami wprowadzanymi do cigw znakw, a nastpnie zaprezentujemy sposb uycia obiektw klasy Spannable, dziki ktrym zmienimy okrelone, wizualne atrybuty tekstu. Jednak co mona zrobi w razie potrzeby kontrolowania wygldu kontrolek za pomoc specyfikacji wsplnej dla wielu widokw lub dla caej aktywnoci czy aplikacji?
Odpowiedzi udzielimy w trakcie omawiania stylw i motyww stosowanych w systemie Android.
Stosowanie stylw
Czasami chcemy podwietli lub zaznaczy odmiennym stylem jaki fragment treci zawartej
w kontrolce klasy View. Mona tego dokona w sposb statyczny lub dynamiczny. Metoda statyczna polega na wstawieniu znacznikw bezporednio do cigu znakw w zasobach typu string,
na przykad:
<string name="styledText"> Styl <i>statyczny</i> w polu <b>TextView</b>.</string>
Moemy nastpnie utworzy odniesienie w pliku XML lub kodzie. Warto wiedzie, e w zasobach
typu string dostpne s znaczniki <i>, <b> oraz <u> jzyka HTML, odpowiadajce, kolejno,
pochyleniu czcionki, jej pogrubieniu oraz podkreleniu. Istniej take takie znaczniki, jak
<sup> (indeks grny), <sub> (indeks dolny), <strike> (przekrelenie), <big>, <small> oraz
<monospace>. Moemy nawet tworzy zagniedenia, na przykad pomniejszone indeksy dolne.
Style dziaaj nie tylko w kontrolce TextView, ale take w innych, na przykad w przyciskach.
Na rysunku 6.17 widzimy wygld tekstu zmodyfikowanego za pomoc stylw i motyww, na
ktrym mona zobaczy wiele przykadw omwionych w tym podrozdziale.
225
Te dwie techniki formatowania tekstu dziaaj tylko w stosunku do tego widoku, do ktrego je
zastosowano. W Androidzie istnieje rwnie mechanizm umoliwiajcy definiowanie oglnego
stylu, ktry bdzie wykorzystywany przez wiele widokw, a take mechanizm motyww, ktry
w oglnej zasadzie pozwala na zastosowanie danego stylu w obrbie caej aktywnoci lub aplikacji. Najpierw jednak musimy omwi style.
Stylem nazywamy zbir atrybutw obiektu klasy View, posiadajcy osobn nazw, moliwo
przypisywania do widokw oraz taki, do ktrego moemy si pniej odnosi. Na przykad na
listingu 6.36 widzimy plik XML, zapisany w katalogu res/values, ktry moe by stosowany dla
komunikatw o wszystkich rodzajach bdw.
Listing 6.36. Definiowanie stylu, ktry bdzie wykorzystywany w wielu widokach
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="ErrorText">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#FF0000</item>
<item name="android:typeface">monospace</item>
</style>
</resources>
Istotna jest informacja, e nazwa atrybutu dla stylu w tej definicji obiektu klasy View nie rozpoczyna si od czonu android:. Naley na to uwaa, poniewa wszystkie inne parametry zawieraj w sobie czon android:. Jeeli mamy w aplikacji wiele widokw wspdzielcych dany
styl, jego zmiana w jednym miejscu jest o wiele atwiejsza, wystarczy zmieni dane atrybuty
w pojedynczym pliku zasobw. Moemy, oczywicie, rwnie tworzy wiele rnych stylw
dla oddzielnych kontrolek. Na przykad przyciski mog korzysta z jednego stylu, ktry bdzie
si rni od stylu zastosowanego w tekcie z menu.
Jednym z najprzyjemniejszych aspektw stylw jest moliwo utworzenia ich hierarchii. Na
podstawie stylu ErrorText moemy utworzy oddzielny styl dla komunikatw o naprawd
gronych bdach. Na listingu 6.38 zaprezentowalimy jedn z propozycji.
Listing 6.38. Definiowanie stylu na podstawie stylu nadrzdnego
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="ErrorText.Danger" >
<item name="android:textStyle">bold</item>
</style>
</resources>
Przykad ten pokazuje, e moemy w prosty sposb nazwa nasz potomny styl, stosujc nazw
stylu nadrzdnego jako przedrostek. Zatem styl ErrorText.Danger jest potomny wobec
stylu ErrorText i dziedziczy atrybuty rodzica. Nastpnie dodaje now warto we waciwoci textStyle. W podobny sposb mona utworzy cae drzewo hierarchii stylw.
Podobnie jak mielimy do czynienia z ukadami graficznymi, system Android zosta wyposaony w spory zestaw predefiniowanych stylw. Aby wykorzysta ktry z nich, stosujemy nastpujc skadni:
style="@android:style/TextAppearance"
W ten sposb zdefiniowano domylny styl formatowania tekstu w Androidzie. Gwny plik stylw
styles.xml znajdziemy w katalogu Android SDK/platforms/<wersja-androida>/data/res/values/.
Wewntrz tego pliku znajdziemy kilka przygotowanych stylw, ktre moemy wykorzysta lub
rozszerza. Jestemy jeszcze winni Czytelnikowi ostrzeenie odnonie do rozszerzania predefiniowanych stylw: wspomniana wczeniej metoda dodawania przedrostka do nazwy w przypadku tych stylw nie zadziaa. Zamiast tego trzeba wykorzysta nadrzdny atrybut znacznika
style, na przykad tak:
227
Nie musimy zawsze stosowa caego stylu wobec widoku. Moemy w razie potrzeby wprowadzi tylko jego fragment. Jeeli na przykad chcemy, aby kolor tekstu w kontrolce TextView odpowiada kolorowi systemowemu, moemy tego dokona w poniszy sposb:
<EditText id="@+id/et2"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:textColor="?android:textColorSecondary"
android:text="@string/hello_world" />
Stosowanie motyww
Problemem dotyczcym stylw jest konieczno dodawania specyfikacji atrybutu style
="@style/..." do kadej definicji widoku, do ktrego dany styl ma zosta zastosowany. Jeeli
chcemy wprowadzi pewne elementy formatowania w zakresie caej aktywnoci lub aplikacji,
do tego celu najlepiej nadaje si motyw. Zasadniczo motyw jest stylem, ktry moe zosta
zastosowany w szerszym zakresie, natomiast pod ktem definiowania nie rni si niczym
od stylu. W rzeczywistoci style i motywy s do czsto stosowane zamiennie, poniewa mona
rozszerzy motyw o styl albo odnosi si w motywie do stylu. Zazwyczaj potrafimy rozpozna
jedynie po nazewnictwie, czy styl peni rol stylu, czy te motywu.
W celu zdefiniowania motywu dla aktywnoci lub aplikacji musimy doda odpowiedni atrybut
w znaczniku <activity> lub <application> w pliku AndroidManifest.xml danego projektu.
Ten kod moe wyglda nastpujco:
<activity android:theme="@style/MyActivityTheme">
<application android:theme="@style/MyApplicationTheme">
<application android:theme="@android:style/Theme.NoTitleBar">
Opis
LinearLayout
TableLayout
RelativeLayout
FrameLayout
229
Layout,
rodkowy interfejs na rysunku 6.18 zawiera domyln warto ciaru, ale parametry argumentu android:gravity zostay zdefiniowane dla kontrolek w kolejnoci: left, center, right.
W przykadzie po prawej atrybut android:layout_weight rodkowego elementu wynosi 1.0,
natomiast w pozostaych dwch kontrolkach nie zmieniono domylnej wartoci 1.0 (listing
6.41). Sprawiamy w ten sposb, e rodkowy element zajmie ca woln przestrze pojemnika
nadrzdnego, a dwie skrajne kontrolki pozostan przy swoich domylnych rozmiarach.
Listing 6.41. Konfigurowanie ciaru w menederze LinearLayout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText android:layout_width="fill_parent" android:layout_weight="0.0"
android:layout_height="wrap_content" android:text="raz"
android:gravity="left"/>
<EditText android:layout_width="fill_parent" android:layout_weight="1.0"
android:layout_height="wrap_content" android:text="dwa"
android:gravity="center"/>
<EditText android:layout_width="fill_parent" android:layout_weight="0.0"
android:layout_height="wrap_content" android:text="trzy"
android:gravity="right"
/>
</LinearLayout>
Analogicznie, jeeli chcemy, eby dwie kontrolki z trzech podzieliy midzy siebie pozosta
woln przestrze, wprowadzamy im warto ciaru rwn 1.0, w trzeciej wartoci natomiast
pozostawiamy ten argument niezmieniony (0.0). W kocu trzeci moliwoci jest podzielenie
ekranu midzy trzy kontrolki w rwnym stopniu, osignite poprzez przydzielenie kadej z nich
wartoci ciaru wynoszcej 1.0. W ten sposb kade pole tekstowe zostanie rozcignite w takim samym stopniu.
231
</TableRow>
<TableRow>
<TextView android:text="Nazwisko:"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<EditText android:text="Allan"
233
Na listingach 6.43 oraz 6.44 wypenilimy meneder TableLayout elementami TableRow. Chocia
jest to zwyczajne podejcie, mona take umieci dowolny element android.widget.View
jako potomka tabeli. Na przykad w listingu 6.45 utworzono tabel, w ktrej pierwszym wierszem jest kontrolka EditText (rysunek 6.22).
Listing 6.45. Zastosowanie kontrolki EditText zamiast TableRow
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="0,1,2">
<EditText
android:text="Imi i nazwisko:"/>
<TableRow>
<TextView android:text="Edgar"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="Allan"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:text="Poe"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</TableRow>
</TableLayout>
Interfejs uytkownika utworzony na listingu 6.45 zosta ukazany na rysunku 6.22. Zauwamy,
e kontrolka EditText zajmuje ca szeroko ekranu, chocia nawet nie zawarlimy odpowiedniego parametru w ukadzie graficznym XML. Wynika to z faktu, e wszystkie elementy
Na listingu 6.46 zostay wyznaczone odstpy rwne 40px. W ten sposb 40 pikseli biaej przestrzeni oddziela tre kontrolki EditText od jej kracw. Na rysunku 6.23 zademonstrowano
t sam kontrolk EditText, ktrej nadano dwie rne wartoci odstpw. Interfejs po lewej
stronie nie ma zdefiniowanych adnych odstpw, a interfejs po prawej posiada dopisan linijk
android:padding="40px".
235
Jak wida, interfejs uytkownika przypomina prosty formularz logowania. Etykieta nazwy uytkownika zostaa przypita do grnej czci pojemnika, poniewa atrybutowi android:layout_
alignParentTop przypisano warto true. W podobny sposb pole wpisywania nazwy uytkownika znalazo si poniej etykiety, gdy wprowadzono atrybut android:layout_below.
Etykieta hasa jest umieszczona jeszcze niej, pod ni za wprowadzono pole wpisywania
hasa. Ostrzeenie znalazo si na samym dole pojemnika, gdy w tej kontrolce atrybutowi
android:layout_alignParentBottom nadano warto true.
Poza trzema wymienionymi atrybutami dostpne s jeszcze inne, takie jak layout_above,
layout_toRightOf, layout_toLeftOf, layout_centerInParent i inne. Praca z menederem
RelativeLayout jest przyjemna z powodu atwoci jego obsugi. Istotnie, dla kadego, kto
zacznie go uywa, stanie si on ulubionym menederem ukadu graficznego bez przerwy
bdzie si do niego wraca.
237
Listing 6.48 przedstawia plik ukadu graficznego oraz metod onCreate() aktywnoci. Celem
wiczenia jest wczytanie dwch widokw ImageView w menederze FrameLayout w taki sposb,
eby w danym momencie by widoczny tylko jeden z nich. Na poziomie interfejsu uytkownika,
kiedy uytkownik kliknie widoczny obrazek, aplikacja go schowa i wywietli drugi obiekt.
Przyjrzyjmy si bliej kodowi zamieszczonemu na listingu 6.48, poczwszy od ukadu graficznego. Mona zauway, e definiujemy meneder FrameLayout zawierajcy dwie kontrolki
ImageView (s one odpowiedzialne za waciwe wywietlanie obrazw). Zwrmy uwag, e
widoczno drugiego obiektu ImageView przyjmuje warto gone, dziki czemu staje si on
niewidoczny. Spjrzmy teraz na metod onCreate(). Rejestrujemy w niej elementy nasuchujce, reagujce na kliknicia widokw ImageView. W procedurze obsugi kliknicia programujemy ukrycie jednego obiektu ImageView wraz z jednoczesnym wywietleniem drugiego obiektu.
Jak ju wczeniej wspomnielimy, menedera FrameLayout zazwyczaj uywa si podczas dynamicznego konfigurowania treci widoku w pojedynczej kontrolce. Chocia jest to standardowa praktyka, pokazalimy rwnie, e kontrolka akceptuje take wiele obiektw potomnych.
Na listingu 6.48 do ukadu graficznego dodano dwie kontrolki, jednak w danym momencie
239
widoczna jest tylko jedna z nich. Meneder FrameLayout nie wymusza jednak takiego rozwizania. Jeeli do ukadu graficznego dodamy wiele kontrolek, Android po prostu utworzy ich stos,
w ktrym jedna kontrolka bdzie naoona na drug, ostatnia natomiast bdzie si znajdowa
na jego szczycie. W ten sposb mona stworzy interesujcy interfejs uytkownika. Na przykad na rysunku 6.25 pokazano meneder FrameLayout, w ktrym s widoczne dwa widoki
ImageView. Wida, e kontrolki s uoone na stosie, a ta znajdujca si na wierzchu czciowo
zasania obiekt umieszczony za ni.
Kolejny interesujcy fakt dotyczcy menedera FrameLayout jest taki, e po dodaniu do ukadu
graficznego wicej ni jednej kontrolki rozmiar tego ukadu jest definiowany jako rozmiar
najwikszego elementu w pojemniku. Na rysunku 6.25 element znajdujcy si na wierzchu jest
w rzeczywistoci znacznie mniejszy od elementu umieszczonego pod spodem, jednak poniewa
ukad graficzny jest dopasowany do najwikszego obiektu, obraz z pierwszego planu zostaje
rozcignity.
Warto take pamita, e jeli umieci si w menederze FrameLayout wiele elementw, z ktrych cz zostanie zdefiniowana jako niewidoczne, mona rozway wykorzystanie metody
setMeasureAllChildren(true) na ukadzie FrameLayout. Skoro najwikszy element
podrzdny definiuje rozmiar caego ukadu graficznego, moe si pojawi problem, gdy takim
elementem okae si obiekt niewidoczny. To znaczy, e jeeli pojawi si na pierwszym planie, bdzie widoczna jedynie jego cz. Po wprowadzeniu metody setMeasureAllChildren() z wartoci true wszystkie elementy powinny by prawidowo wywietlane. Rwnowanym atrybutem
XML dla ukadu FrameLayout jest android:measureAllChildren="true".
241
Opis
Jzyk i region
Rozmiary ekranu
Szersze/wysze ekrany
Orientacja ekranu
Przyblione gstoci: ldpi (ok. 120), mdpi (ok. 160), hdpi (ok. 240)
oraz xhdpi (ok. 320). Android moe dopasowywa zasoby znalezione
w odpowiednich folderach, chyba e s one umieszczone w katalogu
zawierajcym kwalifikator nodpi.
Klawiatura
Powysze kwalifikatory mog by uywane w wielu kombinacjach, aby uzyska podane zachowanie. Nazwa katalogu zasobw moe nie zawiera adnej z wartoci kwalifikatorw lub
zawiera wiele takich wartoci oddzielonych mylnikami. Na przykad poniej pokazalimy
poprawn pod wzgldem technicznym (chocia niezalecan) nazw katalogu zasobw typu
drawable:
drawable-mcc310-en-rUS-large-long-port-mdpi-stylus-keyssoft-qwerty-dpad-v3
Mona jednak zapisa to rwnie w nastpujcy sposb:
drawable-en-rUS-land (obrazy dla wersji angielskiej ze Stanw Zjednoczonych,
w orientacji poziomej)
values-fr (cig znakw w jzyku francuskim)
Bez wzgldu na liczb kwalifikatorw wykorzystanych w zasobach aplikacji naley pamita, e
w kodzie cay czas naley odwoywa si do zasobw w postaci R.rodzaj_zasobu.nazwa, bez
kwalifikatorw. Jeli na przykad istnieje wiele rnych odmian pliku ukadu graficznego
main.xml w rnych kwalifikowanych katalogach zasobw, w kodzie nadal bdziemy si do
niego odwoywa za pomoc wyraenia R.layout.main. Android sam zajmuje si odnalezieniem waciwego pliku main.xml.
Jak wida na rysunku 6.26, narzdzie to ukazuje hierarchi widokw w formie drzewa. Koncepcja
dziaania narzdzia jest nastpujca: najpierw narzdzie wczytuje ukad graficzny, a nastpnie go
analizuje pod wzgldem okrelenia moliwych problemw lub prby zoptymalizowania ukadu
graficznego pod ktem minimalizacji liczby widokw (kwestia wydajnoci).
W celu wyszukania bdw w interfejsie uytkownika naley uruchomi emulator i wyszuka
interfejs UI, ktry zostanie sprawdzony. Nastpnie trzeba odnale narzdzie Hierarchy Viewer
w katalogu /tools rodowiska Android SDK. W przypadku systemu Windows bdzie si tam
znajdowa plik wsadowy hierarchyviewer.bat. Po jego uruchomieniu zostanie wywietlony ekran
urzdze (rysunek 6.27).
Okno Devices wywietla list uruchomionych urzdze (w naszym przypadku emulatorw). Po
rozwiniciu wza urzdzenia w prawym panelu ukae si lista ekranw dostpnych w tym
urzdzeniu. eby zobaczy hierarchi widokw dla danego ekranu, naley go zaznaczy (przewanie jest to pena nazwa aktywnoci, w ktrej przedrostkiem jest nazwa pakietu aplikacji),
a nastpnie klikn przycisk Load View Hierarchy.
243
W oknie View Hierarchy zostanie wywietlona hierarchia widokw w panelu po lewej stronie
(rysunek 6.26). Po zaznaczeniu elementu widoku w rodkowym panelu zostan wywietlone
jego waciwoci, a w obrazie szkieletowym po prawej stronie take relatywna wobec innych
widokw lokalizacja tego widoku. Zaznaczony widok bdzie podwietlony na czerwono. Majc
w ten sposb ukazan hierarchi widokw, moemy poszuka sposobu na zmniejszenie ich
liczby, co jest rwnoznaczne z przyspieszeniem dziaania aplikacji.
Na rysunku 6.27 mona dostrzec trzy przyciski w lewym dolnym rogu okna Hierarchy Viewer.
Przycisk po lewej wcza omwiony powyej widok drzewa. rodkowy przycisk odpowiada
za wywietlanie okna View Hierarchy. Przycisk po prawej wywietla biecy ukad graficzny
w widoku Pixel Perfect, jednak dopiero po zainicjalizowaniu tego widoku za pomoc przycisku
Inspect Screenshot, widocznego w grnej czci narzdzia. Widok ten jest bardzo interesujcy,
poniewa przedstawia ukad graficzny w siatce pikselowej (rysunek 6.28). Znajduje si tu kilka
ciekawych elementw. Po lewej stronie jest umieszczony widok eksploratora wszystkich skadnikw ekranu. Po klikniciu jednego z umieszczonych tu elementw zostanie on podwietlony
na czerwono w rodkowym panelu. Znajdujce si w nim celowniki pozwalaj wywietli
w prawym panelu przyblienie wybranego fragmentu ekranu (jest to lupa). Dostpna jest rwnie opcja powikszenia, pozwalajca na jeszcze wiksze przyblienie danego rejonu ekranu. Lupa
pokazuje rwnie dokadne wsprzdne (x, y) wybranego piksela oraz warto jego koloru.
Ostatni bardzo interesujc funkcj w tym oknie jest przycisk Load Overlay oraz suwak Overlay.
Istnieje moliwo zaadowania pliku obrazu pod wywietlanym ekranem w celu porwnania
go z tym obrazem (by moe stanowi makiet dla tworzonego ukadu graficznego) oraz zwikszania lub zmniejszania jego widocznoci za pomoc suwaka Overlay. Obraz ten pojawia si
w lewym dolnym rogu. Domylnie nie jest on pokazywany w widoku lupy, jednak mona to
zmieni poprzez zaznaczenie odpowiedniej opcji.
Wraz z wydaniem wersji 2.3 Androida aplikacja Hierarchy Viewer staa si dostpna w rodowisku Eclipse. Pojawiy si w niej nowe perspektywy: Hierarchy Viewer oraz Pixel Perfect, kada
posiadajca zestaw widokw rozwijajcych ich funkcje. Aplikacja ta dziaa w zasadzie tak samo
jak jej samodzielna wersja, omwiona powyej. Osoby majce problem z jej zainstalowaniem
znajd instrukcj, jak j odszuka i wdroy, w rozdziale 2.
Dziki takim narzdziom programista posiada wszechstronn kontrol nad wygldem oraz
dziaaniem aplikacji.
Odnoniki
Poniej prezentujemy odnoniki do zagadnie, z ktrymi warto si dokadniej zapozna.
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
stworzonych na potrzeby ksiki. Waciwy plik znajdziesz w katalogu o nazwie
ProAndroid3_R06_Kontrolki. Zebrane s w nim wszystkie projekty z niniejszego
rozdziau, rozmieszczone w oddzielnych katalogach. Umiecilimy w nim rwnie
plik Czytaj.TXT, w ktrym zamiecilimy dokadn instrukcj importowania projektw
z plikw ZIP do rodowiska Eclipse.
245
http://developer.android.com/reference/android/widget/LinearLayout.html#attr_andr
oid:gravity na tej stronie zostay omwione rnorodne wartoci parametru gravity,
stosowanego wraz z ukadem graficznym LinearLayout.
www.curious-creature.org/2010/08/15/scrollviews-handy-trick wpis Romain Guya
(z zespou Androida) wyjaniajcy, jak naley korzysta z kontrolki ScrollView.
http://developer.android.com/resources/articles/index.html na tej stronie zamieszczono
kilka technicznych artykuw pod wspln nazw Layout Tricks, ktrych przeczytanie
bardzo polecamy. Zajmuj si one zagadnieniem wydajnoci podczas projektowania
i programowania interfejsw uytkownika w Androidzie. Warto rwnie przejrze
pozostae artykuy dotyczce budowania interfejsw uytkownika.
Podsumowanie
W tym momencie Czytelnik powinien mie ju dobre pojcie na temat kontrolek dostpnych
w zestawie SDK. Nieobce powinny by take adaptery oraz menedery ukadu graficznego.
Znajc wymagania okrelonego typu ekranu, szybkie identyfikowanie kontrolek oraz menederw ukadw graficznych potrzebnych do skonstruowania wywietlanego ekranu nie powinno by teraz trudne.
W nastpnym rozdziale bdziemy si dalej zajmowa interfejsem uytkownika naszym celem
bd menu.
R OZDZIA
7
Praca z menu
Menu w Androidzie
Osoby pracujce w takim rodowisku jak Swing obsugiwane przez jzyk Java,
Windows Presentation Foundation (WPF) w systemie Windows lub w jakimkolwiek innym szkielecie interfejsu uytkownika, z pewnoci zetkny si z menu.
Najwaniejsz klas obsugujc menu w Androidzie jest klasa android.view.Menu.
Kada aktywno w Androidzie jest powizana z tego typu obiektem menu,
w ktrym mona zawrze wiele elementw menu oraz podmenu.
Elementy menu s reprezentowane przez klas android.view.MenuItem, a podmenu przez android.view.SubMenu. Zwizki pomidzy nimi zostay naszkicowane na
rysunku 7.1. cile rzecz biorc, nie jest to diagram klas, lecz diagram strukturalny,
zaprojektowany po to, aby pomc w dostrzeeniu powiza pomidzy rnymi
klasami i funkcjami dotyczcymi menu.
249
Tworzenie menu
W rodowisku Android SDK nie ma potrzeby tworzenia obiektu menu od podstaw. Poniewa
aktywno jest powizana z pojedynczym menu, jest ono tworzone dla tej aktywnoci i przekazywane do metody wywoawczej onCreateOptionsMenu klasy tej aktywnoci (jak wskazuje
nazwa metody, menu w Androidzie zwane s take menu opcji). Metoda ta umoliwia zapenienie przekazywanego jej menu zestawem elementw menu (listing 7.1).
Listing 7.1. Sygnatura metody onCreateOptionsMenu
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
Po zapenieniu menu elementami metoda powinna odesa warto true, co oznacza, e menu
stao si widoczne. Jeeli przekazan wartoci bdzie false, menu bdzie niewidoczne. Kod
przedstawiony na listingu 7.2 pokazuje, w jaki sposb doda trzy elementy do menu za pomoc
identyfikatora grupy oraz identyfikatorw elementw menu i identyfikatora kolejnoci o wartociach wzrastajcych.
Listing 7.2. Dodawanie elementw do menu
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Grupa
// identyfikator elementu
,0
// kolejno
,"Dodaj"); // tytu
,1
menu.add(0,2,1,"element2");
menu.add(0,3,2,"Wyczy");
// Wane jest, eby zostaa zwrcona warto true, co spowoduje wywietlenie menu
return true;
}
Naley take wywoa implementacj klasy podstawowej tej metody, aby system mia moliwo zapenienia menu elementami menu systemowego. eby elementy menu systemowego
byy oddzielone od pozostaych elementw, Android dodaje je, poczwszy od wartoci kolejnoci
rwnej 0x20000 (wspomnielimy wczeniej, e staa Menu.CATEGORY_SYSTEM definiuje identyfikator kolejnoci dla tego typu elementw. Jak na razie w adnej wersji Androida nie zostay
dodane nowe menu systemowe).
// Grupa 1
int group1 = 1;
menu.add(group1,1,1,"g1.item1");
menu.add(group1,2,2,"g1.item2");
// Grupa 2
int group2 = 2;
menu.add(group2,3,3,"g2.item1");
menu.add(group2,4,4,"g2.item2");
return true;
Zwrmy uwag, e identyfikatory elementw oraz kolejnoci s niezalene dla kadej grupy.
Jaki jest wic poytek z grupy? W klasie android.view.Menu dostpny jest zbir metod, korzystajcych z identyfikatorw grupy. Za ich pomoc mona kontrolowa elementy menu w danej grupie:
removeGroup(id)
setGroupCheckable(id, checkable, exclusive)
setGroupEnabled(id,boolean enabled)
setGroupVisible(id,visible)
251
Wiemy ju, w jaki sposb zapeni gwne menu aktywnoci elementami oraz pogrupowa je
zgodnie z ich przeznaczeniem. Teraz pokaemy, jak ustanowi reakcj systemu na wybr elementu menu.
// Wykonuje zadanie
return true;
}
}
// Etap 2
MyResponse myResponse = new MyResponse(...);
menuItem.setOnMenuItemClickListener(myResponse);
...
Metoda onMenuItemClick zostaje wywoana po wywietleniu elementu menu. Kod zostaje wykonany natychmiast po klikniciu elementu, jeszcze przed wywoaniem metody onOptions
ItemSelected. Jeeli metoda onMenuItemClick przekae warto true, nie zostan wykonane nastpne wywoania zwrotne w tym take metoda onOptionsItemSelected. Oznacza
to, e kod obiektu nasuchujcego ma pierwszestwo przed metod onOptionsItemSelected.
253
Celem tego wiczenia jest utworzenie prostej aktywnoci, w ktrej znajdzie si widok tekstowy.
Widok ten bdzie peni rol testera. Przy kadym menu bdziemy wypisywa nazw oraz identyfikator elementu menu w tym widoku tekstowym. Efekt kocowy bdzie wyglda tak jak
na rysunku 7.2.
Na rysunku 7.2 widoczne s dwa interesujce nas elementy: menu oraz widok tekstowy. Menu
pojawia si u spodu ekranu. Nie bdzie jednak widoczne po uruchomieniu aplikacji; konieczne
bdzie kliknicie przycisku Menu na emulatorze lub urzdzeniu, eby menu zostao wywietlone.
Drugim zajmujcym nas elementem jest widok tekstu na grze ekranu, w ktrym wywietlane s
wiadomoci dotyczce bdw. Podczas klikania elementw dostpnych w menu ich nazwy bd
wywietlane w widoku tekstowym. Po klikniciu elementu Wyczy program usunie zawarto
widoku tekstowego.
Na rysunku 7.2 nie jest przedstawiony pocztkowy stan aplikacji. Stanowi on ilustracj
omawianych typw menu w tym rozdziale.
Utworzenie aktywnoci
W drugim etapie tworzymy aktywno, co jest rwnie cakiem proste. Zaoywszy, e plik ukadu graficznego z pierwszego etapu jest dostpny w katalogu /res/layout/main.xml, mona
skorzysta z jego identyfikatora zasobw do zapenienia widokw aktywnoci (listing 7.7).
Listing 7.7. Klasa aktywnoci menu w rodowisku testowym
public class SampleMenusActivity extends Activity {
255
setContentView(R.layout.main);
}
}
Konfiguracja menu
Gdy ju mamy widok oraz aktywno, moemy przej do trzeciego etapu: przesonicia metody
onCreateOptionsMenu i skonfigurowania menu za pomoc kodu (listing 7.8).
Listing 7.8. Konfigurowanie menu za pomoc kodu
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
Kod z listingu 7.8 wywouje najpierw nadrzdn metod onCreateOptionsMenu, dziki czemu
uzyskuje ona moliwo dodania systemowych menu.
W dotychczasowych wersjach rodowiska Android SDK metoda onCreateOptionsMenu
nie dodaje nowych elementw menu. Jednak w kolejnych edycjach moe si to zmieni,
wic warto wywoywa metod nadrzdn.
Programujemy nastpnie obiekt Menu, gdy bdzie on pniej modyfikowany w celach demonstracyjnych. Nastpnym etapem jest dodanie kilku standardowych elementw menu oraz kilku
elementw drugorzdnych.
Klasa Menu definiuje kilka przydatnych staych, wrd nich sta Menu.FIRST. Mona j wykorzysta jako podstaw numeracji identyfikatorw menu oraz innych sekwencji liczbowych zwizanych z menu. Zauwamy, w jaki sposb moemy powiza identyfikator grupy z wartoci base
i zwiksza jedynie wartoci identyfikatorw kolejnoci oraz identyfikatorw poszczeglnych
elementw. Dodatkowo w kodzie w celach demonstracyjnych umieszczono kilka niestandardowych elementw menu, takich jak ukryj drugorzdny, wcz drugorzdny i par innych.
element
element
element
element
element
1");
2");
3");
4");
5");
257
// ukryj drugorzdny
this.appendMenuItemText(item);
this.myMenu.setGroupVisible(Menu.CATEGORY_SECONDARY,false);
}
else if (item.getItemId() == 5) {
// poka drugorzdny
this.appendMenuItemText(item);
this.myMenu.setGroupVisible(Menu.CATEGORY_SECONDARY,true);
}
else if (item.getItemId() == 6) {
// wcz drugorzdny
this.appendMenuItemText(item);
this.myMenu.setGroupEnabled(Menu.CATEGORY_SECONDARY,true);
}
else if (item.getItemId() == 7) {
// wycz drugorzdny
this.appendMenuItemText(item);
this.myMenu.setGroupEnabled(Menu.CATEGORY_SECONDARY,false);
}
else if (item.getItemId() == 8) {
// zaznacz drugorzdny
this.appendMenuItemText(item);
myMenu.setGroupCheckable(Menu.CATEGORY_SECONDARY,true,false);
}
else if (item.getItemId() == 9) {
259
<activity android:name=".SampleMenusActivity"
android:label="Przykadowa aplikacja menu">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Rozszerzone menu
Na rysunku 7.2 w prawym dolnym rogu ekranu widnieje element menu zatytuowany Wicej.
W adnym fragmencie kodu nie umiecilimy tego elementu, zatem skd on si tu wzi?
Jeeli aplikacja posiada wicej elementw menu, ni moe zaprezentowa na ekranie, Android
wywietla w menu element Wicej, dziki ktremu uytkownik moe przej do pozostaych
elementw. Takie rozszerzone menu pojawia si automatycznie, gdy trzeba wywietli zbyt du
liczb elementw na maej przestrzeni. Rozszerzone menu maj jednak ograniczenie: nie mog
przetwarza ikon. Po klikniciu przycisku Wicej zostanie wywietlone menu bez ikon.
Podczas dodawania elementw do menu rzadko kiedy trzeba pilnowa, eby lokalna zmienna bya
przekazywana przez metod menu.add. Jednak w tym przypadku otrzymany obiekt musi by zapamitany w celu dodania ikony do obiektu menu. Z powyszego przykadu wida take, e
typem zwracanym przez metod menu.add jest MenuItem.
Ikona bdzie widoczna tak dugo, jak dugo wywietlany bdzie obiekt menu w gwnym oknie
aplikacji. Jeeli obiekt ten bdzie wywietlany w rozszerzonym menu, ikona zostanie pominita
i widoczny stanie si sam tekst. Pokazany na rysunku 7.2 element menu, wywietlajcy ikon
przedstawiajc balony, jest przykadem omwionego rodzaju obiektu.
Praca z podmenu
Przyjrzyjmy si teraz podmenu w Androidzie. Na rysunku 7.1 zostay naszkicowane zwizki
strukturalne pomidzy obiektem klasy SubMenu a obiektami klas Menu i MenuItem. W obiekcie
Menu moe si znajdowa wiele obiektw klasy SubMenu. Kady obiekt SubMenu jest dodawany
do obiektu Menu poprzez wywoanie metody Menu.addSubMenu (listing 7.14). Elementy s
dodawane do listy podmenu tak samo jak w przypadku zwykego menu. Wynika to z faktu,
e obiekt SubMenu wywodzi si z obiektu klasy Menu. Nie mona jednak umieszcza podmenu w obiekcie SubMenu.
Listing 7.14. Dodawanie podmenu
private void addSubMenu(Menu menu)
{
261
Poniewa obiekt SubMenu jest podklas obiektu Menu, obsuguje metod addSubMenu.
Kompilator nie wywietli bdu przy prbie doczenia podmenu do innego podmenu,
pojawi si jednak wyjtek wykonawczy.
W dokumentacji zestawu Android SDK widnieje take informacja, e podmenu nie obsuguj
ikon elementw menu. Gdy do elementu menu zostanie dodana ikona, a nastpnie zostanie on
przeniesiony do podmenu, ikona ta zostanie zignorowana, nawet jeli nie pojawi si aden bd
kompilacji lub bd wykonawczy. Jednak samo podmenu moe posiada ikon.
widoku. Zatem aktywno moe zawiera tylko jedno menu opcji, ale wiele menu kontekstowych. Poniewa aktywno moe obejmowa wiele widokw, a kady z nich moe posiada
wasne menu kontekstowe, maksymalna liczba menu kontekstowych w aktywnoci jest
rwna liczbie zawartych w niej widokw.
Chocia wacicielem menu kontekstowego jest widok, metoda potrzebna do zapenienia takiego
menu znajduje si w klasie Activity. Nosi ona nazw activity.onCreateContextMenu()
i z dziaania przypomina metod activity.onCreateOptionsMenu(). Ta wywoywana metoda
przenosi ze sob rwnie widok, w ktrym menu kontekstowe bdzie zapenione.
Istnieje jeszcze jeden godny uwagi problem z menu kontekstowymi. Chocia metoda onCreate
OptionsMenu() jest automatycznie wywoywana dla kadej aktywnoci, nie dotyczy to metody
onCreateContextMenu(). Widok w aktywnoci nie musi posiada menu kontekstowego. Na
przykad mog by obecne trzy widoki w aktywnoci, lecz moe istnie potrzeba wczenia
menu kontekstowego tylko dla jednego z nich. Jeeli dany widok ma posiada menu kontekstowe,
musi on zosta zarejestrowany wraz z aktywnoci do penienia roli waciciela tego menu. Dokonuje si tego poprzez metod activity.registerForContextMenu(view), omwion
w podpunkcie Rejestrowanie widoku dla menu kontekstowego.
Zwrmy teraz uwag na przedstawion na rysunku 7.3 klas ContextMenuInfo. Obiekt tego typu
jest przekazywany do metody onCreateContextMenu. Jest to jeden ze sposobw przekazywania
przez widok dodatkowych informacji do tej metody. eby widok mg tego dokona, musi
przesoni metod getContextViewInfo() i zwrci pochodn klas ContextMenuInfo wraz
263
Skoro znamy ju ogln struktur menu kontekstowych, przyjrzyjmy si przykadowemu kodowi pokazujcemu, w jaki sposb krok po kroku zaimplementowa menu kontekstowe:
1. Zarejestruj widok dla danego menu kontekstowego w metodzie onCreate() aktywnoci.
2. Zapenij menu kontekstowe za pomoc metody onCreateContextMenu(). Musisz
dokoczy pierwszy etap, zanim ta metoda zostanie wywoana przez system Android.
3. Zdefiniuj odpowiedzi na kliknicia poszczeglnych elementw menu kontekstowego.
265
W szczeglnoci menu alternatywne pozwalaj na zamieszczanie menu jednej aplikacji wewntrz drugiej. Po wybraniu menu alternatywnego docelowa aplikacja lub aktywno uruchamia
si za porednictwem adresu URL w celu przekazania danych wymaganych przez dajc
aktywno. Nastpnie wywoana aktywno wykorzysta adres URL danych, ktre zostay przekazane za pomoc intencji. eby dobrze poj dziaanie menu alternatywnych, trzeba najpierw
zrozumie pojcia dostawcw treci, identyfikatorw URI treci, typw MIME treci oraz
intencji (rozdziay 4. i 5.).
Oglna zasada dziaania jest nastpujca: wyobramy sobie, e tworzymy ekran, ktry ma za
zadanie wywietla dane. Najprawdopodobniej ekran ten bdzie aktywnoci. W tej aktywnoci
bdzie dostpne menu opcji pozwalajcych na rne sposoby modyfikowania tych danych.
Zamy take na chwil, e pracujemy nad dokumentem lub notatk, definiowan przez
identyfikator URI oraz odpowiadajcy mu typ MIME. Jako programici chcemy, eby urzdzenie
posiadao wicej aplikacji umoliwiajcych edytowanie lub wywietlanie tych danych. Chcemy
sprawi, eby menu tych programw byy wywietlane jako cz menu tworzonego dla naszej
aktywnoci.
eby przyczy elementy menu alternatywnego do naszego menu, naley wykona nastpujce czynnoci podczas konfigurowania menu w metodzie onCreateOptionsMenu:
1. Utwrz intencj, ktrej identyfikator URI danych jest ustanowiony dla pokazywanego
w biecym momencie identyfikatora URI danych.
2. Przydziel t intencj do kategorii CATEGORY_ALTERNATIVE.
3. Wyszukaj aktywnoci, ktre pozwalaj na obsug typu danych definiowanych przez
identyfikator URI.
4. Intencje wywoujce te aktywnoci dodaj jako elementy menu.
Wymienione etapy mwi nam wiele na temat natury aplikacji w Androidzie, wic zastanowimy
si nad kadym z nich. Jak ju wiemy, przyczanie elementw menu alternatywnego do standardowego menu jest przeprowadzane w metodzie onCreateOptionsMenu:
@Override public boolean onCreateOptionsMenu(Menu menu)
{
}
Zastanwmy si, jaki kod tworzy t funkcj. Najpierw musimy pozna identyfikator URI danych, na ktrych zamierzamy pracowa w danej aktywnoci. Mona go uzyska w nastpujcy
sposb:
this.getIntent().getData()
Technika ta dziaa, poniewa klasa Activity posiada metod getIntent(), przekazujc identyfikator URI danych, dla ktrych zostaa przywoana aktywno. Tak aktywnoci moe
by gwna aktywno wywoana przez menu gwne; w takim przypadku moe nie posiada
intencji i metoda getIntent() zwrci warto null. Piszc kod, naley zabezpieczy si przed
podobn sytuacj.
Teraz naszym celem jest odnalezienie innych programw, ktre potrafi pracowa z tego rodzaju danymi. Wyszukiwanie przeprowadzamy, wstawiajc intencj w miejsce argumentu.
Poniej przedstawiamy kod pozwalajcy na skonstruowanie odpowiedniej intencji:
Intent criteriaIntent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
// Grupa
// Unikatowe identyfikatory, ktre chcemy doczy.
Menu.CATEGORY_ALTERNATIVE, // Kolejno
getComponentName(),
// Nazwa klasy wywietlajcej menu Name
// --tutaj, to jest ta klasa.
null,
// Bez szczegw.
criteriaIntent,
// Uprzednio utworzona intencja, ktra
// opisuje nasze wymagania.
0,
// Bez flag.
null);
// Zwracane elementy menu
Menu.CATEGORY_ALTERNATIVE,
Zanim omwimy kad linijk kodu, wyjanimy, co rozumiemy pod pojciem pasujce
aktywnoci. Pasujca aktywno to taka aktywno, ktra potrafi przetworzy przekazany jej
identyfikator URI. Informacje dotyczce obsugiwanych identyfikatorw URI s zazwyczaj
rejestrowane w plikach manifestach aktywnoci za pomoc identyfikatorw URI, dziaa i kategorii. W Androidzie znajduje si mechanizm pozwalajcy uywa obiektu Intent do wyszukiwania pasujcych aktywnoci, jeli ma si dane te atrybuty.
Przyjrzyjmy si teraz uwaniej listingowi 7.18. Metoda addIntentOptions w klasie Menu
wyszukuje aktywnoci pasujce do identyfikatora URI intencji oraz atrybutw kategorii. Nastpnie metoda dodaje te aktywnoci do waciwej grupy w menu, korzystajc z identyfikatorw
elementw menu oraz identyfikatorw kolejnoci. Tym aspektem dziaania metody zajmuj si
trzy pierwsze atrybuty. Na listingu 7.18 grup, od ktrej zaczniemy dodawanie nowych elementw menu, jest Menu.CATEGORY_ALTERNATIVE. Ta sama staa jest uywana jako warto
bazowa dla identyfikatorw elementw oraz kolejnoci.
Kolejny argument wskazuje na w peni kwalifikowan nazw skadnika aktywnoci, ktrej
czci jest nasze menu. W kodzie jest zastosowana pomocnicza metoda getComponentName(),
wywodzca si z klasy Activity. Nazwa skadnika (ang. component name) stanowi po prostu nazw pakietu oraz klasy i jest ona wymagana, poniewa za kadym razem, gdy jest dodawany nowy element menu, element ten bdzie musia wywoywa docelow aktywno.
eby tego dokona, system wymaga rdowej aktywnoci, ktra uruchomia aktywno
docelow. Nastpnym argumentem jest tablica intencji, ktra powinna by stosowana jako filtr
wobec zwracanych intencji. W naszym przykadzie wprowadzilimy warto null.
Kolejny argument wskazuje obiekt criteriaIntent, ktry dopiero co skonstruowalimy.
Jest to kryterium wyszukiwania, ktrego chcemy uy. Nastpujcy po nim argument jest
flag typu Menu.FLAG_APPEND_TO_GROUP wskazujc na to, czy elementy menu maj by
dodawane do istniejcej grupy menu, czy te maj by podmieniane. Domylna warto wynosi
0, co wskazuje na to, e elementy grupy menu maj by podmieniane.
267
Ostatnim argumentem na listingu 7.18 jest tablica dodanych elementw menu. Mona korzysta
z takiego odniesienia do dodanych elementw, w przypadku gdy trzeba je w jaki sposb zmodyfikowa ju po ich dodaniu.
Wszystko brzmi prosto i piknie. Pozostaje jednak kilka pyta bez odpowiedzi. Na przykad: jakie bd nazwy dodanych elementw? Dokumentacja Androida jest w tym temacie wyjtkowo
uboga, zatem poszperalimy troch w kodzie rdowym, eby dowiedzie si, jak ta funkcja
w rzeczywistoci dziaa poza wzrokiem uytkownika (w rozdziale 1. opisalimy, w jaki sposb
uzyska dostp do kodu rdowego Androida).
Okazuje si, e klasa Menu jest jedynie interfejsem, wic nie widzielimy jej kodu rdowego.
Klas implementujc interfejs Menu jest MenuBuilder. Na listingu 7.19 umiecilimy kod podobnej metody, addIntentOptions, z klasy MenuBuilder (wstawilimy ten kod wycznie
w celach pogldowych; nie bdziemy go szczegowo objania).
Listing 7.19. Metoda MenuBuilder.addIntentOptions
public int addIntentOptions(int group, int id, int categoryOrder,
ComponentName caller,
Intent[] specifics,
Intent intent, int flags,
MenuItem[] outSpecificItems)
{
PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> lri =
pm.queryIntentActivityOptions(caller, specifics, intent, 0);
final int N = lri != null ? lri.size() : 0;
if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
removeGroup(group);
}
for (int i=0; i<N; i++) {
final ResolveInfo ri = lri.get(i);
Intent rintent = new Intent(
ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
rintent.setComponent(new ComponentName(
ri.activityInfo.applicationInfo.packageName,
ri.activityInfo.name));
final MenuItem item = add(group, id, categoryOrder, ri.loadLabel(pm));
item.setIntent(rintent);
if (outSpecificItems != null && ri.specificIndex >= 0) {
outSpecificItems[ri.specificIndex] = item;
}
}
return N;
}
Zauwamy, e jeden wiersz na listingu 7.19 zosta pogrubiony; ta cz kodu jest odpowiedzialna
za konstruowanie elementu menu. Zadanie okrelenia tytuu menu zostaje przekazane klasie
ResolveInfo. W kodzie rdowym tej klasy wida, e filtr deklarujcy t intencj powinien
posiada powizany z ni tytu. Poniej znajduje si przykad:
Warto label filtru intencji staje si nazw menu. Mona sprawdzi zachowanie tej klasy na
podstawie przykadowej aplikacji Notepad.
269
Identyfikator grupy mona okreli za pomoc wyraenia @+id. W kadej grupie menu bd
umieszczone elementy menu, ktrych identyfikatory bd powizane z nazwami symbolicznymi.
W dokumentacji rodowiska Android SDK mona znale wszystkie argumenty dla tych
znacznikw jzyka XML.
Listing 7.20. Plik XML zawierajcy definicje menu
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Plik XML menu, ktrego kod wida na listingu 7.20, posiada jedn grup menu. Na podstawie
definicji identyfikatora zasobu @+id/menuGroup_main tej grupie zostanie automatycznie
przydzielony identyfikator zasobw menuGroup_main w pliku R.java. W analogiczny sposb
wszystkie potomne elementy menu uzyskuj wasne identyfikatory, tworzone na bazie definicji
symbolicznych identyfikatorw zasobw z tego pliku XML.
// z aktywnoci
Tworzenie odpowiedzi
dla elementw menu opartych na pliku XML
Nie mielimy jeszcze do czynienia z wyjtkow zalet tej metody staje si ona widoczna dopiero wtedy, gdy przychodzi czas na tworzenie odpowiedzi dla elementw menu. Odpowied dla
elementw umieszczonych w pliku XML tworzy si podobnie jak w przypadku programowania
w jzyku Java, istnieje jednak maa rnica. Tak jak wczeniej, elementami menu zajmuje
si wywoywana metoda onOptionsItemSelected. Tym razem bdziemy musieli skorzysta
z pomocy zasobw Androida (rozdzia 3. zosta powicony midzy innymi zasobom). W punkcie Struktura pliku XML zasobw menu wspomnielimy, e Android generuje nie tylko
identyfikator zasobu dla pliku XML, lecz rwnie niezbdne identyfikatory elementw, dziki
ktrym s one rozrniane. Pod wzgldem tworzenia odpowiedzi dla elementw menu jest
to olbrzymia zaleta, poniewa nie trzeba jawnie tworzy identyfikatorw tych elementw
i zarzdza nimi.
eby w peni zrozumie implikacje, w przypadku menu napisanych w jzyku XML nie trzeba
definiowa staych dla tych identyfikatorw oraz nie naley si martwi o ich unikatowo,
poniewa t kwesti zajmuje si proces generowania identyfikatorw zasobw. Wida to
w poniszym kodzie:
private void onOptionsItemSelected (MenuItem item)
{
this.appendMenuItemText(item);
if (item.getItemId() == R.id.menu_clear)
{
this.emptyText();
}
else if (item.getItemId() == R.id.menu_dial)
{
// co robi
}
else if (item.getItemId() == R.id.menu_testPick)
{
// co robi
}
else if (item.getItemId() == R.id.menu_testGetContent)
{
// co robi
}
else if (item.getItemId() == R.id.menu_show_browser)
{
// co robi
}
itd.
}
271
Zauwamy, w jaki sposb nazwy elementw menu z pliku zasobw XML automatycznie wygeneroway identyfikatory elementw w przestrzeni nazw R.id.
Krtkie wprowadzenie
do dodatkowych znacznikw menu w pliku XML
Podczas konstruowania plikw XML naley zna dostpne znaczniki. Informacje na ich temat
mona szybko zdoby, przegldajc wersje demonstracyjne interfejsw API, dostpne w zestawie Android SDK. Wrd nich znajduje si zbir menu, ktre pomagaj zrozumie wszystkie
aspekty programowania w Androidzie. W podkatalogu /res/menu znajduje si wiele przykadowych plikw XML zasobw menu. Omwimy pokrtce niektre kluczowe znaczniki.
Znacznik zaznaczania
Do kontrolowania zaznaczania na poziomie grupy mona zastosowa znacznik
Behavior:
checkable
<group android:id="@+id/noncheckable_group"
android:checkableBehavior="none">
Widoczno menu
Widoczno elementu menu jest kontrolowana za pomoc flagi visible:
<item android:id="... "
android:visible="true"
...
</item>
Odnoniki
W trakcie nauki korzystania z klas menu Androida przydatny moe si okaza poniszy adres
URL. Mona tam znale projekty rodowiska Eclipse, powizane z niniejszym rozdziaem.
ftp://ftp.helion.pl/przyklady/and3ta.zip z tego adresu moemy pobra projekt testowy,
ukazujcy koncepcje omwione w tym rozdziale. Waciwy plik znajdziesz w katalogu
o nazwie ProAndroid3_R07_Menu.
Podsumowanie
W niniejszym rozdziale zaprezentowano sposb korzystania z rnorodnych rodzajw menu
dostpnych w Androidzie: menu standardowych, kontekstowych, alternatywnych oraz opartych
na jzyku XML. W niektrych rozdziaach, na przykad w 8. (Praca z oknami dialogowymi),
16. (Analiza animacji dwuwymiarowej) czy 20. (Programowanie grafiki trjwymiarowej za
pomoc biblioteki OpenGL), przedstawimy sposb wykorzystania menu XML do przetestowania
omawianych w nich funkcji. Natomiast wprowadzone w wersji 3.0 Androida paski zada oraz
ich interakcja z menu s przedmiotami rozwaa w rozdziale 30.
R OZDZIA
8
Praca z oknami dialogowymi
Tego typu program najlepiej utworzy w programie JavaScript za pomoc funkcji alert, ktra
powoduje wywietlenie prostego, synchronicznego okna dialogowego, zawierajcego informacj
oraz przycisk OK. Po klikniciu przycisku OK program dziaa dalej. To okno dialogowe jest
uznawane za modalne oraz synchroniczne, poniewa uzyskamy dostp do nastpnej linijki kodu
dopiero po wykonaniu funkcji alert.
Okno typu alert przydaje si podczas sprawdzania bdw. Jednak w Androidzie takie bezporednie funkcje lub okna dialogowe nie s dostpne. Zamiast nich jest obsugiwany konstruktor
okien alertw, aplikacja oglnego przeznaczenia suca do tworzenia oraz uywania okien
alertw. Mona zatem samemu stworzy okno alertu, korzystajc z klasy android.app.
AlertDialog.Builder. Dziki tej klasie mona konstruowa okna dialogowe pozwalajce
uytkownikowi wykonywa nastpujce czynnoci:
odczytywa wiadomoci oraz odpowiada Tak lub Nie;
wybiera element z listy;
wybiera wiele elementw z listy;
obserwowa postp dziaania aplikacji;
wybra jedn z opcji;
odpowiedzie na zacht, zanim program wznowi dziaanie.
Pokaemy, w jaki sposb skonstruowa jedno z wymienionych okien dialogowych oraz jak wywoa je z elementu menu. Algorytm odnoszcy si do wszystkich wspomnianych powyej okien
dialogowych skada si z nastpujcych etapw:
1. Utwrz obiekt klasy Builder.
2. Ustaw takie parametry wywietlania, jak liczba przyciskw, lista elementw i tak dalej.
3. Ustanw metody wywoywania zwrotnego dla przyciskw.
275
// Tworzenie konstruktora
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("Okno alertu");
// Wywietlenie
ad.show();
}
}
public class EmptyOnClickListener
implements android.content.DialogInterface.OnClickListener {
public void onClick(DialogInterface v, int buttonId)
{
}
}
Kod z listingu 8.1 mona wywoa w odpowiedniej aktywnoci testowej (na przykad w przygotowanym przez nas projekcie) poprzez utworzenie elementu menu i przypisanie mu nastpujcej odpowiedzi:
if (item.getItemId() == R.id.menu_simple_alert)
{
Alerts.showAlert("Przykad prostego okna alertu", this);
}
Efekt kocowy moe wyglda tak jak na rysunku 8.1 (w zalenoci od aktywnoci testowej).
Kod tego alertu nie jest skomplikowany (jest on zawarty na listingu 8.1 oraz w nastpujcym po
nim wycinku kodu). Nawet fragment dotyczcy obiektu nasuchujcego jest atwy do zrozumienia. W istocie nacinicie przycisku nic nie powoduje.
Interface.
cancel()
dismiss()
Zazwyczaj nie musimy ich wywoywa, poniewa w razie koniecznoci s one automatycznie
przywoywane podczas kliknicia przycisku. Jeeli chcemy reagowa na wywoania tych metod,
moemy zarejestrowa odpowiadajce im wywoania zwrotne. W dokumentacji zestawu SDK
dotyczcej interfejsu DialogInterface znajdziemy pen list dostpnych metod zwrotnych.
Na listingu 8.1 utworzylimy po prostu pusty obiekt nasuchujcy, zarejestrowany dla przycisku
OK. Jedyn now czynnoci jest brak zastosowania polecenia new do utworzenia okna dialogowego. Zamiast tego skonfigurowalimy parametry i utworzylimy konstruktor, ktry zbudowa okno alertu.
277
Na listingu 8.3 pokazano, jak uzyska obiekt klasy LayoutInflater za pomoc statycznej metody
LayoutInflater.from(ctx), a nastpnie uy go do przeksztacenia kodu XML do widoku
View. Po tym naley wprowadzi do konstruktora alertw tytu oraz utworzony przed momentem widok.
Piszc kod na listingu 8.4, zaoylimy, e nazw klasy obiektu nasuchujcego jest Prompt
Listener. Ten obiekt nasuchujcy zosta zarejestrowany dla kadego przycisku. Klasa
PromptListener pobiera skonstruowany za pomoc kodu z listingu 8.3 ukad graficzny. Jeeli
przyjrzymy si tej klasie nieco uwaniej, dostrzeemy, e zmienna view jest stosowana do
identyfikowania kontrolek tekstowych oraz odczytywania danych wprowadzanych przez
uytkownika.
279
// zwraca zacht
return pl.getPromptReply();
// Przycisk OK
promptReply = getPromptText();
}
else {
// Przycisk Anuluj
}
}
promptReply = null;
// wywietla
ad.show();
return pl.getPromptReply();
}
}
Kod z listingu 8.7 mona wywoa poprzez utworzenie elementu menu w odpowiednim
rodowisku testowym oraz napisanie nastpujcej odpowiedzi dla tego elementu:
if (item.getItemId() == R.id.nasz_identyfikator_elementu_menu)
{
String reply = Alerts.showPrompt("Tutaj wpisujesz tekst", this);
}
281
Jednak po przetestowaniu tego kodu Czytelnik zauway, e okno dialogowe zachty zawsze
odsya warto null, nawet po wpisaniu tekstu w polu edycji. Wynika to z faktu, e metoda
show() wywouje okno dialogowe w sposb asynchroniczny:
ad.show();
// dialog.show
return pl.getPromptReply();
// listener.getPromptReply()
Oznacza to, e wywoanie metody getPromptReply() (listing 8.6) nastpuje, zanim uytkownik
zdy wpisa tekst w polu edycji i klikn przycisku OK. Taki bd logiczny zblia nas do zrozumienia sedna natury okien dialogowych systemu Android.
Udowodnilimy, e warto cigu znakw w zmiennej reply bdzie zawsze wynosia null,
poniewa okno dialogowe zachty, zainicjalizowane przez metod Alerts.showPrompt(),
nie ma moliwoci odesania wpisanej wartoci do tego samego wtku. Jedynym sposobem,
dziki ktremu mona tego dokona, jest bezporednie zaimplementowanie wywoywanej
metody w aktywnoci, bez wykorzystywania klasy PromptListener. W tym celu naley zaimplementowa metod onClickListener w klasie Activity:
public class SampleActivity extends Activity
implements android.content.DialogInterface.OnClickListener
{
// ...
public void onClick(DialogInterface v, int buttonId)
{
Jak wida, dziki metodzie onClick mona poprawnie odczytywa zmienne z wywoanego okna
dialogowego, poniewa zanim zostanie ona wywoana, uytkownik zdy zamkn okno
dialogowe.
Taka forma korzystania z okien dialogowych jest cakowicie poprawna. Jednak twrcy Androida
zapewnili dodatkowy mechanizm optymalizacji wydajnoci w postaci zarzdzanych okien dialogowych obiektw wielokrotnie wykorzystywanych przez rne wywoania. Nadal jednak trzeba
stosowa wywoywania zwrotne. Tak naprawd caa wiedza zdobyta podczas implementowania
okna dialogowego zachty przyda si podczas pracy z zarzdzanymi oknami dialogowymi i uatwi
zrozumienie ich dziaania. Takie zarzdzane okna dialogowe pozwalaj rwnie Androidowi na
kontrolowanie stanu okien dialogowych pomidzy ich wywoaniami, dopki stan widoku aktywnoci pozostaje niezmieniony.
283
Wystarczajco proste. Wanie utworzylimy identyfikator okna dialogowego, sucy do sterowania wywoaniami zwrotnymi. Identyfikator ten pozwala nam na wykonanie poniszej czynnoci w odpowiedzi na kliknicie elementu menu:
Dostpna w zestawie Android SDK metoda showDialog uruchamia proces wywoania metody
onCreateDialog() klasy naszej aktywnoci. Android jest wystarczajco sprytny, eby nie
wywoywa wielokrotnie metody onCreateDialog(). Po jej wywoaniu musimy utworzy okno
dialogowe i odesa je Androidowi. Metoda onCreateDialog() jest pniej przechowywana wewntrz systemu na wypadek potrzeby ponownego uycia. Poniej przedstawilimy przykadowy
kod, sucy do utworzenia okna dialogowego na podstawie unikalnego identyfikatora:
public class SomeActivity extends Activity {
...
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_ALERT_ID:
return createAlertDialog();
}
return null;
}
private Dialog createAlertDialog()
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Okno alertu");
builder.setMessage("jaki komunikat");
EmptyOnClickListener emptyListener = new EmptyOnClickListener();
builder.setPositiveButton("OK", emptyListener );
AlertDialog ad = builder.create();
return ad;
}
285
Jeeli powyszy kod zostanie wstawiony, metoda showDialog(1) bdzie dziaa. Nawet jeeli ta
metoda bdzie przywoywana wiele razy, metoda onCreateMethod zostanie wywoana tylko raz.
Taki sam protok mona zastosowa wobec okna dialogowego zachty.
Utworzenie odpowiedzi na wywoanie okna dialogowego to kawa roboty, jednak korzystanie
z protokou zarzdzanych okien dialogowych jest jeszcze bardziej pracochonne. Po dokadnym
przestudiowaniu tego protokou stwierdzilimy, e wyabstrahujemy go i przeksztacimy tak,
eby spenia dwa zadania:
spowodowa, aby identyfikacja okna dialogowego oraz jego tworzenie odbyway si
poza klas aktywnoci obiektw;
umieci proces tworzenia okna dialogowego oraz jego odpowiedzi
w wyspecjalizowanej klasie okna dialogowego.
W nastpnym podrozdziale omwimy proces projektowania takiej struktury, a nastpnie zastosujemy j do ponownego utworzenia okna alertu i okna dialogowego zachty.
//...
// w razie potrzeby uzyskuje dostp do wntrza okna dialogowego mad
dialogFinished()
{
//...
// uywa wartoci z okna dialogowego
mad.getA();
Uwaamy, e jest to o wiele prostszy model korzystania z okien dialogowych. Wyranie wida
zalety tego rozwizania:
Mona bezporednio wykorzystywa obiekty pochodzce z okna dialogowego w celu odczytania wartoci.
Jaki jest mechanizm dziaania tego abstrakcyjnego rozwizania? W pierwszym etapie przenosimy procesy tworzenia i przygotowywania okna dialogowego do klasy rozpoznajcej podstawowe
okno dialogowe. Interfejs ten nosi nazw IDialogProtocol. W tym interfejsie bezporednio
zaimplementowano metod show(). Takie okna dialogowe s zbierane oraz przechowywane
w rejestrze bazowej klasy aktywnoci, a ich identyfikatory peni funkcje kluczy. Bazowa klasa na
podstawie identyfikatorw okien dialogowych rozdziela wywoania onCreate, onPrepare oraz
onClick i kieruje je do klasy okna dialogowego. Omawiana architektura zostaa szczegowo
naszkicowana na rysunku 8.3.
287
Na listingu 8.8 zaprezentowano zastosowanie tej struktury. W dalszej czci rozdziau pokazalimy kod rdowy najwaniejszych klas (w tym GenericPromptDialog i GenericManagedAlertDialog), jednak nie zamiecilimy w ksice penej klasy sterownika. Na listingu
8.8 przedstawilimy jedynie jego najwaniejsze aspekty. W podrozdziale Odnoniki mona znale adres URL do projektu, w ktrym klasa ta pozwala na przetestowanie uprzednio
omwionych okien dialogowych.
Listing 8.8. Wersja skrcona protokou zarzdzanych okien dialogowych
public class MainActivity extends ManagedDialogsActivity
{
// okno dialogowe 1
private GenericManagedAlertDialog gmad =
new GenericManagedAlertDialog(this,1,"InitialValue");
// okno dialogowe 2
private GenericPromptDialog gmpd =
new GenericPromptDialog(this,2,"InitialValue");
IDialogProtocol
Interfejs IDialogProtocol definiuje pojcie zarzdzanego okna dialogowego. Zadaniem zarzdzanego okna dialogowego jest tworzenie okna dialogowego i przygotowanie go do kadego
wywietlenia. Rozsdne jest take przekazanie funkcji show() do samego okna dialogowego.
Okno dialogowe musi rozpoznawa take kliknicia przycisku oraz umoliwia wywoywanie odpowiedniej funkcji nadrzdnej po jego zamkniciu. Poniszy kod przedstawia te dziaania
w formie zestawu funkcji:
public interface IDialogProtocol
{
public Dialog create();
public void prepare(Dialog dialog);
public int getDialogId();
public void show();
public void onClickHook(int buttonId);
}
ManagedActivityDialog
Abstrakcyjna klasa ManagedActivityDialog zapewnia wspln implementacj wszystkim klasom okien dialogowych, wymagajcym wprowadzenia interfejsu IDialogProtocol. Pozwala
bazowym klasom na przesonicie funkcji create oraz prepare, ale umoliwia zaimplementowanie wszystkich pozostaych metod klasy IDialogProtocol. Klasa ManagedActivityDialog
informuje take nadrzdn aktywno o zakoczeniu dziaania okna dialogowego po zarejestrowaniu zdarzenia kliknicia. Wykorzystuje wzorce szablonw uchwytw i pozwala klasom
pochodnym na korzystanie z wyspecjalizowanej metody uchwytu onClickHook. Klasa ta jest rwnie odpowiedzialna za przekierowanie metody show() do nadrzdnej aktywnoci, a tym samym
implementacja tej metody staje si bardziej naturalna. Klasa ManagedActivityDialog powinna
by stosowana jako klasa bazowa dla wszystkich nowych okien dialogowych (listing 8.9).
Listing 8.9. Klasa ManagedActivityDialog
public abstract class ManagedActivityDialog implements IDialogProtocol
,android.content.DialogInterface.OnClickListener
{
private ManagedDialogsActivity mActivity;
private int mDialogId;
public ManagedActivityDialog(ManagedDialogsActivity a, int dialogId)
{
mActivity = a;
mDialogId = dialogId;
}
public int getDialogId()
{
return mDialogId;
}
public void show()
{
mActivity.showDialog(mDialogId);
}
public void onClick(DialogInterface v, int buttonId)
{
onClickHook(buttonId);
289
this.mActivity.dialogFinished(this, buttonId);
}
}
DialogRegistry
Klasa DialogRegistry peni dwie funkcje. Tworzy map powiza pomidzy identyfikatorami
okien dialogowych a ich rzeczywistymi (wbudowanymi) wystpieniami. Kieruje take oglne
wywoania metod onCreate i onPrepare do okrelonych okien dialogowych poprzez mapowanie tych identyfikatorw na obiekty. Klasa ManagedDialogsActivity wykorzystuje klas
DialogRegistry jako magazyn rejestrujcy nowe okna dialogowe (listing 8.10).
Listing 8.10. Klasa DialogRegistry
public class DialogRegistry
{
SparseArray<IDialogProtocol> idsToDialogs
= new SparseArray();
public void registerDialog(IDialogProtocol dialog)
{
idsToDialogs.put(dialog.getDialogId(),dialog);
}
public Dialog create(int id)
{
IDialogProtocol dp = idsToDialogs.get(id);
if (dp == null) return null;
return dp.create();
}
public void prepare(Dialog dialog, int id)
{
IDialogProtocol dp = idsToDialogs.get(id);
if (dp == null)
{
throw new RuntimeException("Identyfikator okna dialogowego jest
niezarejestrowany:" + id);
}
dp.prepare(dialog);
}
}
ManagedDialogsActivity
Klasa ManagedDialogsActivity jest traktowana jako klasa bazowa dla aktywnoci obsugujcych zarzdzane okna dialogowe. Utrzymuje jedno wystpienie klasy DialogRegistry, dziki
czemu na bieco ledzi zarzdzane okna dialogowe identyfikowane przez interfejs Idialog
Protocol. Umoliwia pochodnym aktywnociom rejestrowanie ich okien dialogowych poprzez funkcj registerDialogs(). Jak wida na rysunku 8.3, jest rwnie odpowiedzialna za
przenoszenie semantyk create i prepare do waciwej instancji okna dialogowego poprzez wyszukanie jej w rejestrze okien dialogowych. Klasa ta zapewnia take metod zwrotn dialogFinished
dla kadego okna dialogowego znajdujcego si w rejestrze (listing 8.11).
IDialogFinishedCallBack
Interfejs IDialogFinishedCallBack umoliwia klasie ManagedActivityDialog przekazywanie
aktywnoci nadrzdnej informacji, e uytkownik zakoczy prac z oknem dialogowym i mona
wywoa wobec okna dialogowego odpowiednie metody, eby odczyta parametry. Zazwyczaj
klasa ManagedDialogsActivity implementuje ten interfejs i zachowuje si jak klasa nadrzdna
wobec klasy ManagedActivityDialog (listing 8.12).
291
GenericManagedAlertDialog
Klasa GenericManagedAlertDialog jest implementacj okna alertu. Rozszerza ona klas ManagedActivityDialog. Odpowiada za utworzenie rzeczywistego okna alertu za pomoc konstruktora okien alertw. Przenosi rwnie wszystkie potrzebne jej informacje w formie zmiennych lokalnych. Poniewa klasa GenericManagedAlertDialog implementuje proste okno alertu,
nie wpywa w aden sposb na metod onClickHook. Podczas korzystania z tej klasy naley
przede wszystkim zwrci uwag, e przechowuje ona wszystkie powizane z ni informacje
w jednym miejscu (listing 8.13). Oznacza to, e gwny kod aktywnoci pozostaje sterylnie czysty.
Listing 8.13. Klasa GenericManagedAlertDialog
public class GenericManagedAlertDialog extends ManagedActivityDialog
{
private String alertMessage = null;
private Context ctx = null;
public GenericManagedAlertDialog(ManagedDialogsActivity inActivity,
int dialogId,
String initialMessage)
{
super(inActivity,dialogId);
alertMessage = initialMessage;
ctx = inActivity;
}
public Dialog create()
{
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("Okno alertu");
builder.setMessage(alertMessage);
builder.setPositiveButton("OK", this );
AlertDialog ad = builder.create();
return ad;
}
public void prepare(Dialog dialog)
{
AlertDialog ad = (AlertDialog)dialog;
ad.setMessage(alertMessage);
}
public void setAlertMessage(String inAlertMessage)
{
alertMessage = inAlertMessage;
}
public void onClickHook(int buttonId)
{
GenericPromptDialog
Klasa GenericPromptDialog przechowuje wszystkie dane niezbdne dla okna dialogowego
zachty poprzez rozszerzenie klasy ManagedActivityDialog oraz dostarczenie niezbdnych
metod create i prepare (listing 8.14). Zachowuje ona take tekst wpisany przez uytkownika
w oknie dialogowym zachty jako zmienn lokaln, aby nadrzdna aktywno moga go odczyta dziki metodzie dialogFinished.
Listing 8.14. Klasa GenericPromptDialog
public class GenericPromptDialog extends ManagedActivityDialog
{
private String mPromptMessage = null;
private View promptView = null;
String promptValue = null;
private Context ctx = null;
public GenericPromptDialog(ManagedDialogsActivity inActivity,
int dialogId,
String promptMessage)
{
super(inActivity,dialogId);
mPromptMessage = promptMessage;
ctx = inActivity;
}
public Dialog create()
{
LayoutInflater li = LayoutInflater.from(ctx);
promptView = li.inflate(R.layout.promptdialog, null);
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("zachta");
builder.setView(promptView);
builder.setPositiveButton("OK", this);
builder.setNegativeButton("Anuluj", this);
AlertDialog ad = builder.create();
return ad;
}
public void prepare(Dialog dialog)
{
// na razie puste
}
public void onClickHook(int buttonId)
{
if (buttonId == DialogInterface.BUTTON1)
{
// przycisk OK
String promptValue = getEnteredText();
}
}
293
Odnoniki
Podsumowanie
W tym rozdziale zwrcilimy uwag na nowego rodzaju wyzwania zwizane z korzystaniem
z okien dialogowych w Androidzie. Pokazalimy skutki uywania asynchronicznych okien dialogowych oraz zaprezentowalimy, w jaki sposb mona sobie uatwi korzystanie z zarzdzanych okien dialogowych. Dodatkowo w rozdziale 29. omwilimy dziaanie okien dialogowych
opartych na fragmentach, elementach wprowadzonych w wersji 3.0 Androida. Poniewa interfejs
API dialogw jest wprowadzany rwnie do starszych wersji Androida, rozpoczcie uywania
okien dialogowych tego typu jest dobrym pomysem.
R OZDZIA
9
Praca z preferencjami
i zachowywanie stanw
Klasa ListPreference
Oczywicie najpierw musimy dostarczy interfejs UI, umoliwiajcy przegldanie listy dostpnych opcji. Lista taka bdzie zawieraa przyciski opcji, a domylny (lub biecy) wybr bdzie ju zaznaczony. Rozwizanie tego problemu dotyczcego struktury preferencji w Androidzie
wymaga bardzo niewielkiego nakadu pracy. Najpierw utworzymy plik XML, w ktrym zostan
opisane preferencje, a nastpnie wykorzystamy gotow klas aktywnoci, ktra moe wywietla
i przechowywa preferencje. Szczegy zostay ukazane na listingu 9.1.
Na kocu rozdziau umiecilimy odnonik, za pomoc ktrego mona pobiera
projekty utworzone specjalnie dla tego rozdziau. Projekty te mona importowa
bezporednio do rodowiska Eclipse.
Listing 9.1. Plik XML preferencji lotw i powizana z nim klasa aktywnoci
<?xml version="1.0" encoding="utf-8"?>
package com.androidbook.preferences.sample;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class FlightPreferenceActivity extends PreferenceActivity
{
297
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.flightoptions);
}
}
Listing 9.1 zawiera fragment kodu XML, ktry umoliwia ustawienie preferencji lotw. Na
listingu tym znalaza si rwnie klasa aktywnoci, wczytujca plik XML preferencji. Zacznijmy
od kodu XML. Android posiada pen struktur obsugi preferencji. Oznacza to, e dziki tej
strukturze moemy definiowa wasne preferencje, wywietla je uytkownikowi i zapamitywa
jego decyzje w magazynie danych. Preferencje definiujemy w katalogu /res/xml. Aby wywietli
uytkownikowi preferencje, tworzymy klas aktywnoci, ktra rozszerza predefiniowan klas
Androida android.preference.PreferenceActivity, i dodajemy zasb do zbioru zasobw
aktywnoci za pomoc metody addPreferencesFromResource(). Wspomniana wczeniej struktura zapewnia obsug pozostaych funkcjonalnoci (zapisywanie i przechowywanie preferencji).
W naszym scenariuszu z rozkadami lotw tworzymy plik flightoptions.xml i umieszczamy go
w /res/xml/flightoptions.xml. Generujemy nastpnie klas aktywnoci FlightPreference
Activity, rozszerzajc klas android.preference.PreferenceActivity. W dalszej
kolejnoci wywoujemy metod addPreferencesFromResource() i przekazujemy jej zasb
R.xml.flightoptions. Zauwamy, e kod XML zasobw ustawie wskazuje kilka zasobw
typu string. Aby kompilacja zostaa przeprowadzona pomylnie, musimy doda do projektu
kilka cigw znakw. Wkrtce pokaemy, jak tego dokona. Na razie przyjrzyjmy si utworzonemu na listingu 9.1 interfejsowi uytkownika (rysunek 9.1).
Na rysunku 9.1 pokazano dwa widoki. Widok z lewej strony jest nazywany ekranem preferencji,
a interfejs z prawej strony nosi nazw listy preferencji. Po klikniciu pola Opcje lotu pojawia si
widok Wybierz opcje lotu w formie okna dialogowego modalnego, zawierajcego przycisk opcji
dla kadego ustawienia. Uytkownik wybiera opcj, ktra natychmiast zostaje zapisana, a widok
zostaje zamknity. Po powrocie do ekranu opcji widok odzwierciedla dokonany wybr.
Kod na listingu 9.1 definiuje klas PreferenceScreen, a nastpnie tworzy klas podrzdn
ListPreference. Klasa PreferenceScreen posiada trzy waciwoci: key, title i summary.
Opis
android:key
android:title
Tytu opcji.
android:summary
android:entries
android:entryValues
android:dialogTitle
android:defaultValue
Aby nasz przykadowy kod zadziaa, musimy doda lub zmodyfikowa pliki, tak jak pokazano
na listingu 9.2.
Listing 9.2. Konfigurowanie reszty projektu w naszym przykadzie
<?xml version="1.0" encoding="utf-8"?>
299
android.app.Activity;
android.content.Intent;
android.content.SharedPreferences;
android.os.Bundle;
android.view.Menu;
android.view.MenuInflater;
android.view.MenuItem;
android.widget.TextView;
301
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".FlightPreferenceActivity"
android:label="@string/prefTitle">
<intent-filter>
<action android:name=
"com.androidbook.preferences.sample.intent.action.FlightPreferences" />
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="4" />
</manifest>
303
Preference
305
Widok CheckBoxPreference
Pokazalimy, e preferencja ListPreference wywietla list jako element swego interfejsu
uytkownika. W analogiczny sposb preferencja CheckBoxPreference wywietla widet pola
wyboru w postaci skadnika swego interfejsu UI.
Zamy, e w ramach rozbudowania naszej aplikacji wyszukujcej loty chcemy uytkownikowi umoliwi wybranie kolumn przed wywietleniem zestawu wynikw. Preferencja Check
BoxPreference wywietla spis dostpnych kolumn i umoliwia uytkownikowi wybranie
podanych kolumn poprzez zaznaczenie odpowiedniego pola wyboru. Interfejs uytkownika dla tego przykadu jest przedstawiony na rysunku 9.3, a zawarto pliku XML preferencji zostaa umieszczona na listingu 9.4.
// CheckBoxPreferenceActivity.java
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class CheckBoxPreferenceActivity extends PreferenceActivity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.chkbox);
}
}
Na listingu 9.4 zosta ukazany plik XML preferencji, chkbox.xml, oraz prosta klasa aktywnoci wczytujca ten plik za pomoc metody addPreferencesFromResource(). Jak wida, w interfejsie uytkownika znalazo si pi pl wyboru, kade reprezentowane przez wze
CheckBoxPreference w pliku XML. Kade z tych pl posiada rwnie warto key, ktra jak
mona byo si spodziewa jest ostatecznie wykorzystywana do przechowywania stanu
elementu interfejsu uytkownika w trakcie procesu zapisywania wprowadzonych zmian
preferencji. Dziki widokowi CheckBoxPreference zostaje ona zapisana po zmianie stanu
preferencji. Innymi sowy, jeeli uytkownik zaznacza kontrolk preferencji lub usuwa jej
zaznaczenie, jej stan zostaje zapisany. Listing 9.5 przedstawia magazyn danych preferencji
dla naszego przykadu.
Listing 9.5. Magazyn danych dla preferencji w postaci pola wyboru
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<boolean name="show_total_travel_time_column_pref" value="false" />
<boolean name="show_price_column_pref" value="true" />
<boolean name="show_arrival_column_pref" value="false" />
<boolean name="show_airline_column_pref" value="true" />
<boolean name="show_departure_column_pref" value="false" />
</map>
Ponownie wida, e kada preferencja zostaje zachowana poprzez atrybut key. Typem danych
widoku CheckBoxPreference jest boolean, przyjmujcy warto true lub false: warto true
wskazuje zaznaczenie preferencji, warto false ma przeciwne znaczenie. Aby odczyta
warto z pola wyboru preferencji, musielibymy uzyska dostp do wspdzielonej preferencji,
a nastpnie wywoa metod getBoolean() i przekaza jej atrybut key preferencji:
boolean option = prefs.getBoolean("show_price_column_pref", false);
307
Inn poyteczn funkcj widoku CheckBoxPreference jest moliwo zmiany treci podsumowania w zalenoci od tego, czy pole wyboru jest zaznaczone, czy nie. W tym przypadku
stosowane s atrybuty jzyka XML summaryOn i summaryOff. Przyjrzyjmy si teraz kontrolce
EditTextPreference.
Widok EditTextPreference
W Androidzie jest rwnie dostpna preferencja umoliwiajca pisanie tekstu, zwana EditText
Uytkownik, zamiast zaznaczy wybran opcj, moe wpisa wasn tre. Zamy, e posiadamy aplikacj suc do generowania kodu Java. Jednym z ustawie preferencji tej aplikacji moe by nazwa domylnego pakietu, wyznaczonego dla generowanych
klas. Chcemy wywietli uytkownikowi pole tekstowe i pozwoli mu na wpisanie nazwy
takiego pakietu. Na rysunku 9.4 zosta zaprezentowany interfejs uytkownika takiej aplikacji, za na listingu 9.6 pokazalimy kod XML.
Preference.
// EditTextPreferenceActivity.java
Widok RingtonePreference
Widok RingtonePreference suy do obsugi dzwonkw. Jest on wstawiany do aplikacji,
w przypadku gdy uytkownik ma otrzyma moliwo wyboru dzwonka w formie preferencji.
Rysunek 9.5 ilustruje przykadowy interfejs UI widoku RingtonePreference, a na listingu 9.7
zosta wstawiony jego kod XML.
309
// RingtonePreferenceActivity.java
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class RingtonePreferenceActivity extends PreferenceActivity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.ringtone);
}
}
Kiedy uytkownik kliknie przycisk Wybierz preferencj dzwonka, zostanie wywietlony widok
ListPreference zawierajcy dzwonki zapisane w urzdzeniu (rysunek 9.5). W widoku
tym mona wybra dzwonek, a nastpnie klikn przycisk OK lub Anuluj. Po wybraniu
opcji OK dane zostan zapisane w magazynie preferencji. Zwrmy uwag, e w przypadku
dzwonkw wartoci przechowywan w magazynie preferencji jest identyfikator URI danego
dzwonka chyba e zostanie wybrana preferencja Cichy, ktra w miejsce przechowywanej
wartoci wstawia pusty cig znakw. Przykadowy identyfikator URI wyglda nastpujco:
<string name="ring_tone_pref">content://media/internal/audio/media/26</string>
Jeeli emulator jest skpo wyposaony w dzwonki, mona doda je samemu. W tym celu
wystarczy skopiowa pliki muzyczne na kart SD, nastpnie uruchomi aplikacj Music
Player, zaznaczy plik muzyczny, po czym klikn przycisk Menu i wybra opcj Use
as ringtone. Mechanizm kopiowania plikw na kart SD zosta omwiony w rozdziale 19.
Organizowanie preferencji
Struktura preferencji pozwala na organizowanie preferencji w kategorie. Jeli nasza aplikacja
pozwala na ustawienie wielu preferencji, moemy zbudowa widok przedstawiajcy ich wysokopoziomowe kategorie. Uytkownicy mogliby wtedy wywietla kad z tych kategorii w celu przegldania preferencji umieszczonych w danej grupie i zarzdzania nimi.
Istniej dwa sposoby implementacji takiej struktury. Moemy albo wprowadzi zagniedone
elementy PreferenceScreen wewntrz nadrzdnej klasy PreferenceScreen, albo w podobny
sposb wykorzysta elementy PreferenceCategory. Pierwszy z wymienionych sposobw, polegajcy na grupowaniu preferencji za pomoc zagniedonych elementw PreferenceScreen,
zosta zaprezentowany na rysunku 9.6 i listingu 9.8.
311
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="vegi_screen"
android:title="Warzywa"
android:summary=" Preferencje odnoszce si do warzyw">
<CheckBoxPreference
android:key="tomato_selection_pref"
android:title="Pomidor "
android:summary="W rzeczywistoci jest to owoc" />
<CheckBoxPreference
android:key="potato_selection_pref"
android:title="Ziemniak"
android:summary="Moje ulubione warzywo" />
</PreferenceScreen>
</PreferenceScreen>
Widok z lewej strony na rysunku 9.6 prezentuje dwa ekrany preferencji, jeden zatytuowany Miso,
a drugi noszcy nazw Warzywa. Kliknicie danej grupy spowoduje wywietlenie jej elementw.
Na listingu 9.8 pokazalimy, w jaki sposb tworzy si zagniedone ekrany.
Grupy pokazane na rysunku 9.6 zostay utworzone poprzez zagniedenie elementw
wewntrz nadrzdnej klasy PreferenceScreen. Organizowanie w ten
sposb preferencji jest korzystne, w przypadku gdy istnieje wiele preferencji, a nie chcemy,
aby uytkownicy musieli przewija ekran w celu znalezienia jednej z nich. Jeeli w aplikacji
nie ma zbyt wielu preferencji, a mimo to naley utworzy ich wysokopoziomowe kategorie,
mona zastosowa drug metod, zwizan z obiektem PreferenceCategory. Szczegy
zostay zaprezentowane na rysunku 9.7 i listingu 9.9.
PreferenceScreen
Na rysunku 9.7 pokazano grupy wykorzystane rwnie w poprzednim przykadzie. Teraz jednak
grupy te s zorganizowane w kategorie preferencji. Jedyna rnica pomidzy kodem XML z listingu 9.9 a kodem XML z listingu 9.8 polega na utworzeniu obiektu PreferenceCategory
dla zagniedonych ekranw zamiast zagniedenia elementw PreferenceScreen.
313
Odnoniki
Poniszy odnonik moe si przyda Czytelnikom, ktrzy zechc zapozna si lepiej z omwion w tym rozdziale tematyk:
ftp://ftp.helion.pl/przyklady/and3ta.zip z tego adresu moemy pobra projekty
utworzone z myl o niniejszej ksice. Plik ten zawiera wszystkie przykady omwione
w tym rozdziale, umieszczone w oddzielnych katalogach. Waciwy plik znajdziesz
w katalogu o nazwie ProAndroid3_R09_Preferencje. Dostpny jest tu take plik Czytaj.TXT,
stanowicy dokadn instrukcj importowania projektw do rodowiska Eclipse.
315
Podsumowanie
W tym rozdziale omwilimy zarzdzanie preferencjami w Androidzie. Pokazalimy, w jaki
sposb mona korzysta z widokw ListPreference, CheckBoxPreference, EditText
Preference i RingtonePreference. Omwilimy take techniki organizowania preferencji
w grupy oraz programowy sposb modyfikowania preferencji. Na koniec wyjanilimy, w jaki
sposb mona wykorzysta, poprzez przywoania, struktur preferencji w procesie zapisywania
i odczytywania informacji z aktywnoci.
R OZDZIA
10
Analiza zabezpiecze
i uprawnie
319
Opis
genkey
keystore
alias
storepass
keypass
keyalg
Algorytm.
validity
Okres wanoci.
Aplikacja keytool wywietli monit o podanie hase wymienionych w tabeli 10.1, jeeli nie zostan zdefiniowane w wierszu polece. Jeeli komputer jest wspuytkowany przez wiele osb,
bezpieczniej bdzie nie okrela parametrw storepass i keypass w wierszu polece, lecz
zdefiniowa je, gdy aplikacja keytool tego zada. Polecenie przedstawione na listingu 10.1 wygeneruje bazodanowy plik magazynu kluczy w utworzonym przez nas folderze. Baza danych
bdzie plikiem noszcym nazw release.keystore. Okres wanoci wpisu (parametr validity)
wynosi 14 000 dni (w przyblieniu 38 lat) co stanowi dugi czas. Wane jest zrozumienie
powodu ustanowienia tak dugiego okresu wanoci. W dokumentacji Androida znalazo si
zalecenie, aby definiowa wystarczajco dugi okres wanoci, tak by przewysza cakowity
okres istnienia aplikacji, wliczajc w to jej wielokrotne aktualizacje. Zalecanym okresem wanoci jest 25 lat. Co wicej, jeeli aplikacja ma zosta umieszczona w serwisie Android Market
(http://www.android.com/market/), certyfikat musi by wany przynajmniej do dnia 22 padziernika 2033 roku. Serwis ten sprawdza kad umieszczon aplikacj pod ktem takiego okresu
wanoci. Poniewa podpisy cyfrowe aktualizacji musz bezwzgldnie pasowa do podpisu pierwszej wersji aplikacji, plik magazynu kluczy musi by zabezpieczony za wszelk cen! Jeeli
Czytelnik go utraci bez moliwoci odtworzenia, nie bdzie mg aktualizowa aplikacji, a jej
usprawnianie stanie si nagle olbrzymim problemem.
Wracajc do aplikacji keytool, argument alias jest niepowtarzaln nazw, przydzielan kademu wpisowi magazynu kluczy; mona jej pniej uywa jako odniesienia do tego wpisu. Po
uruchomieniu pokazanego na listingu 10.1 polecenia keytool aplikacja wywietli kilka pyta
(rysunek 10.1), a nastpnie wygeneruje baz danych oraz jej wpis.
Po utworzeniu pliku magazynu kluczy moemy dodawa do niego kolejne certyfikaty. Wystarczy
uruchomi ponownie narzdzie keytool i wprowadzi ciek do istniejcego magazynu kluczy.
321
Naturalnie, skoro posiadamy nowy certyfikat programistyczny, nie moemy za jego pomoc
aktualizowa biecych wersji aplikacji obecnych w emulatorach AVD lub fizycznych urzdzeniach. rodowisko Eclipse bdzie wywietlao w konsoli monit o odinstalowanie istniejcej
aplikacji za pomoc narzdzia adb, co oczywicie powinnimy uczyni. Jeeli mamy zainstalowanych wiele aplikacji w urzdzeniu AVD, prostszym sposobem moe si okaza utworzenie
nowego egzemplarza, dziki czemu bdziemy mogli pracowa na nim od nowa. eby nie mie
problemu z terminem wanoci certyfikatu, moemy utworzy wasny plik debug.keystore z dowolnie zdefiniowanym okresem wanoci certyfikatu. Musi on oczywicie posiada t sam
nazw pliku oraz znajdowa si w tym samym miejscu co plik, ktry zosta utworzony przez narzdzie ADT. Alias certyfikatu posiada warto androiddebugkey, a obydwa hasa storepass
i keypass brzmi tak samo: android. Narzdzie ADT utworzy nazw certyfikatu Android Debug,
ustanowi jego jednostk organizacyjn Android oraz dwuliterowy kod kraju US. Wartoci parametrw organizacji, miasta oraz stanu mog pozosta nieznane (Unknown).
Jeeli Czytelnik otrzyma klucz map-api od firmy Google, korzystajc z przestarzaego certyfikatu,
bdzie musia uzyska nowy klucz powizany z nowym certyfikatem. Klucze map-api zostan
omwione w rozdziale 17.
Skoro wiemy ju, jak zdoby certyfikat cyfrowy pozwalajcy nam na podpisywanie plikw .apk,
warto si dowiedzie, w jaki sposb wykorzysta narzdzie jarsigner do samego procesu podpisywania. Poniej omawiamy, w jaki sposb moemy tego dokona.
eby podpisa plik .apk, podajemy lokalizacj magazynu kluczy, haso do tego magazynu, haso
do klucza prywatnego, ciek do pliku .apk oraz alias wpisu magazynu kluczy. Aplikacja
jarsigner nastpnie podpisze plik .apk za pomoc sygnatury pochodzcej z wpisu magazynu
kluczy. Aby uruchomi narzdzie jarsigner, naley otworzy okno narzdzi (rozdzia 2.) albo
wiersz polece bd okno terminala. Nastpnie trzeba przej do katalogu bin zestawu JDK lub
upewni si, e katalog ten znajduje si w zmiennej systemowej PATH. Ze wzgldw bezpieczestwa
Jak ju wczeniej stwierdzilimy, Android wymaga cyfrowego podpisu aplikacji, eby uniemoliwi zoliwemu programicie aktualizowanie programu utworzonego przez kogo innego
do wasnej wersji. Wynika z tego wniosek, e aktualizacja aplikacji musi by podpisana za pomoc tej samej sygnatury co jej pierwotna wersja. Jeeli aktualizacja zostanie podpisana za pomoc innej sygnatury, zostanie potraktowana jak odrbna aplikacja. Przypominamy wic ponownie, e naley zachowa szczegln ostrono z plikiem magazynu kluczy, ktry musi by
dostpny podczas aktualizowania aplikacji.
323
Zauwamy, e aplikacja zipalign nie modyfikuje pliku wejciowego, dlatego stosujemy czon
raw (ang. nieprzetworzony) w nazwie pliku podczas jego eksportowania ze rodowiska Eclipse.
W wyniku tego plik wyjciowy posiada nazw odpowiedni do przeprowadzenia wdraania.
W przypadku potrzeby nadpisania istniejcego pliku wyjciowego outfile.apk mona uy opcji
f. Zwrmy ponadto uwag, e aplikacja zipalign weryfikuje sposb odwzorowania (wyrwnania) danych w pliku po jego przetworzeniu. eby zweryfikowa poprawno wyrwnania pliku, mona uy aplikacji zipalign w poniszy sposb:
zipalign c v 4 filename.apk
Istotne jest, eby omawiany proces zosta przeprowadzony po procesie podpisywania, w przeciwnym razie podpisanie aplikacji moe zniweczy efekt zoptymalizowania plikw. Nie oznacza
to wcale, e aplikacja bdzie si zawiesza, po prostu moe zajmowa wicej pamici, ni potrzeba.
W rodowisku Eclipse w zakadce Android Tools mona natrafi na opcj Export Signed Application Package. Powoduje ona uruchomienie tak zwanego kreatora eksportu, za pomoc
ktrego mona przeprowadzi wszystkie omwione powyej etapy. Jedynymi danymi, jakie
naley wprowadzi, s cieka do magazynu kluczy, alias klucza, a take hasa i nazwa pliku
output.apk. Kreator w razie koniecznoci wygeneruje nawet nowy magazyn kluczy lub sam
klucz. Niektrym wygodniej bdzie korzysta z kreatora, innym przeprowadza po kolei
poszczeglne etapy na eksportowanym, niepodpisanym pakiecie aplikacji. Skoro ju Czytelnik wie, jak dziaaj obydwa rozwizania, bdzie mg wybra wygodniejsze rozwizanie.
Po podpisaniu i wyrwnaniu zawartoci pliku .apk mona rcznie zainstalowa aplikacj na
emulatorze za pomoc narzdzia adb. W celach wiczeniowych zachcamy teraz do uruchomienia emulatora. Jednym ze sposobw uruchomienia emulatora, o ktrym jeszcze nie wspomnielimy, jest kliknicie menu Window w rodowisku Eclipse i wybranie opcji Android SDK
and AVD Manager. Zobaczymy wywietlone okno z list posiadanych urzdze AVD. Wybierzmy jedno z nich i kliknijmy przycisk Start. Emulator uruchomi si bez funkcji kopiowania jakichkolwiek projektw ze rodowiska Eclipse. Otwrzmy okno narzdzi i wczmy narzdzie adb wraz z poleceniem install:
adb install "CIEKA DO PLIKU APK"
Proces moe zakoczy si niepowodzeniem z kilku powodw. Najczciej jednak jego przyczyn jest wczeniejsze zainstalowanie na emulatorze wersji testowej aplikacji, a to powoduje
konflikt certyfikatw i wywietlenie informacji o bdzie. Moliwe te, e wczeniej zainstalowano gotow wersj aplikacji, co spowoduje wywietlenie komunikatu, e dana aplikacja jest
ju zainstalowana na urzdzeniu. W pierwszym przypadku mona odinstalowa aplikacj testow za pomoc polecenia:
adb uninstall packagename
Zwrmy uwag, e argumentem jest tutaj nazwa pakietu, a nie nazwa pliku .apk. Nazwa
pakietu jest zdefiniowana w pliku AndroidManifest.xml zainstalowanej aplikacji.
W przypadku wystpienia bdu drugiego rodzaju mona wpisa ponisze polecenie, w ktrym
parametr r oznacza ponowne zainstalowanie aplikacji z zachowaniem danych na urzdzeniu
(w emulatorze):
adb install r "CIEKA DO PLIKU APK"
Przeledmy teraz, w jaki sposb podpisywanie aplikacji wpywa na proces jej aktualizowania.
325
Wymagane uprawnienie
Opis
Aparat
fotograficzny
android.permission.
CAMERA
Internet
android.permission.
INTERNET
Dane
kontaktw
uytkownika
android.permission.
READ_CONTACTS
Dane
kalendarza
uytkownika
android.permission.
READ_CALENDAR
Dyktafon
android.permission.
RECORD_AUDIO
Informacje
pooenia
geograficznego
GPS
android.permission.
ACCESS_FINE_LOCATION
Informacje
pooenia
geograficzneg
o sieci Wi-Fi
android.permission.
ACCESS_COARSE_LOCATION
Informacje
o stanie baterii
android.permission.
BATTERY_STATS
Bluetooth
android.permission.
BLUETOOTH
android.permission.
WRITE_CONTACTS
android.permission.
WRITE_CALENDAR
327
Najpierw naley utworzy projekt zawierajcy niestandardowe uprawnienie i aktywno. Dokonujemy tego poprzez uruchomienie rodowiska Eclipse i kliknicie opcji New/New Project/
Android Project. Otworzy si okno dialogowe New Android Project. Jako nazw projektu wpisujemy CustomPermission, wybieramy opcj Create new project in workspace i zaznaczamy Use
default location. Nazwa aplikacji moe brzmie Niestandardowe uprawnienie, nazwa pakietu
com.cust.perm, a nazwa aktywnoci CustPermMainActivity. Powinnimy rwnie wpisa
dowoln wersj docelowej platformy w polu Build Target. eby utworzy projekt, naley klikn przycisk Finish. Wygenerowany projekt bdzie zawiera dopiero co utworzon aktywno,
penic rol domylnej (gwnej) aktywnoci. Stwrzmy take aktywno uprzywilejowan
aktywno, dla ktrej wymagane jest specjalne uprawnienie. W aplikacji Eclipse naley
przej do pakietu com.cust.perm, utworzy klas PrivActivity, dla ktrej superklas jest
android.app.Activity, a nastpnie przepisa kod umieszczony na listingu 10.4.
Listing 10.4. Klasa PrivActivity
package com.cust.perm;
import
import
import
import
import
android.app.Activity;
android.os.Bundle;
android.view.ViewGroup.LayoutParams;
android.widget.LinearLayout;
android.widget.TextView;
Jak wida, aktywno PrivActivity nie ma adnych nadzwyczajnych funkcji. Chcemy jedynie
pokaza, w jaki sposb mona chroni j za pomoc uprawnienia i wywoa j za pomoc
klienta. Jeeli klient zostanie waciwie zaimplementowany, na ekranie pojawi si wiadomo
Pozdrowienia z aktywnoci PrivActivity. Po wygenerowaniu tej chronionej aktywnoci
mona utworzy uprawnienie do niej.
eby stworzy niestandardowe uprawnienie, naley je zdefiniowa w pliku AndroidManifest.xml. Najatwiej to zrobi za pomoc edytora manifestu. Wystarczy dwukrotnie klikn plik
AndroidManifest.xml i wybra zakadk Permissions. W oknie Permissions klikamy przycisk
Add, wybieramy opcj Permission i wciskamy przycisk OK. Zostanie wygenerowane puste
uprawnienie. Naley wypeni je atrybutami, tak jak zilustrowano na rysunku 10.6. Wypeniamy
pola z prawej strony, a jeeli etykieta po prawej stronie wci bdzie nosia nazw Permission,
klikamy j, dziki czemu nazwa uprawnienia powinna zosta zaktualizowana.
Jak wida na rysunku 10.6, uprawnienie posiada nazw, etykiet, ikon, grup uprawnienia, opis
i poziom ochrony. W tabeli 10.3 opisalimy wymienione parametry.
329
Wymagany? Opis
android:name
Tak
android:protectionLevel
Tak
Nie
android:label
Nie
android:description
Nie
android:icon
Nie
android.permission-group.SYSTEM_TOOLS
Jak wida na listingu 10.5, musimy doda sta w postaci cigu znakw startMyActivityDesc
do zasobw typu string. eby kompilacja kodu przebiega bezbdnie, dodajmy nastpujcy
cig znakw do pliku res/values/strings.xml:
<string name="startMyActivityDesc">Umoliwia uruchomienie mojej aktywnoci</string>
Wczmy teraz projekt na emulatorze. Chocia gwna aktywno nie wykonuje adnej czynnoci, musimy zainstalowa aplikacj na emulatorze, zanim bdzie mona napisa klienta
dla uprzywilejowanej aktywnoci.
Napiszmy wic takiego klienta. W rodowisku Eclipse naley klikn opcj New/Project/Android
Project. Jako nazw projektu wpiszmy ClientOfCustomPermission, wybierzmy opcj Create
new project in workspace i zaznaczmy opcj Use default location. Nazwa aplikacji moe brzmie
Klient niestandardowego uprawnienia, nazwa pakietu com.client.cust.perm, a nazwa aktywnoci ClientCustPermMainActivity. W polu Build Target wpisujemy nazw dowolnej wersji
platformy. Aby utworzy projekt, naley klikn przycisk OK.
331
Jak wida, plik XML ukadu graficznego definiuje pojedynczy przycisk nazwany Uruchom
PrivActivity. Napiszmy teraz aktywno, ktra bdzie generowaa zdarzenie wywoane klikniciem i uruchamiaa uprzywilejowan aktywno. Kod z listingu 10.7 naley umieci w klasie
ClientCustPermMainActivity.
Listing 10.7. Zmodyfikowana klasa ClientCustPermMainActivity
package com.client.cust.perm;
android.app.Activity;
android.content.Intent;
android.os.Bundle;
android.view.View;
Na listingu 10.7 wida, e po wywoaniu przycisku zostaje utworzona nowa intencja, a nastpnie ustanowiona nazwa klasy uruchamianej aktywnoci. W naszym przypadku chcemy uruchomi aktywno com.cust.perm.PrivActivity z pakietu com.cust.perm.
333
Rodzaj zacznika okrela obiekt ContentUri. Zwrmy uwag, e utworzenie intencji jest
efektem dziaania Intent.ACTION_VIEW, a metoda setData() powoduje ustawienie danych.
Wida te flag przydzielajc uprawnienia do odczytu zacznika dowolnej aktywnoci dopasowanej do intencji. W tym miejscu do gry wkracza dostawca treci. To, e aktywno odczytaa
uprawnienia do treci, wcale nie oznacza, e bdzie przekazywa te uprawnienia innej aktywnoci, ktra jeszcze nie posiada uprawnie. Musi na to pozwoli rwnie dostawca treci. Po znalezieniu w aktywnoci pasujcego filtra intencji Android sprawdza dostawc treci w celu upewnienia si, czy mona przydzieli dane uprawnienia. W istocie dostawca treci otrzymuje danie
umoliwienia dostpu do tej nowej aktywnoci, dokadniej do treci okrelonej w identyfikatorze
URI. Jeeli dostawca odmwi, zostanie wywietlony komunikat o wyjtku SecurityException,
a caa operacja zakoczy si niepowodzeniem. Na listingu 10.9 ta konkretna aplikacja nie sprawdza wyjtku SecurityException, poniewa programista nie spodziewa si adnej odmowy
udzielenia uprawnie. Wynika to z faktu, e dostawca zacznika jest czci aplikacji Email! Istnieje jednak moliwo, e nie bdzie adnej aktywnoci obsugujcej zacznik, dlatego wic
mamy do czynienia tylko z tym wyjtkiem. W przypadku gdy aktywno przetwarzajca identyfikator URI uzyska do niego uprawnienie, dostawca treci nie moe go odmwi. To znaczy, e
wywoujca aktywno moe uzyska uprawnienie, a jeeli aktywno po stronie odbiorczej intencji posiada ju wymagane uprawnienia do obiektu contentURI, wywoana aktywno bdzie
moga kontynuowa dziaanie bez najmniejszego problemu.
Oprcz flagi Intent.FLAG_GRANT_READ_URI_PERMISSION istnieje jeszcze flaga dajca uprawnienia do zapisu Intent.FLAG_GRANT_WRITE_URI_PERMISSION. Istnieje moliwo ustanowienia kadej z tych flag w obiekcie klasy Intent. Poza tym znajduj one zastosowanie rwnie
w klasach typu Service, Broadcast Receiver, a take Activity, poniewa aktywnoci take
mog odbiera intencje.
Odnoniki
Poniej zamieszczamy przydatne cza do materiaw, z ktrymi warto si dokadniej zapozna:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
bezporednio zwizanych z t ksik. Projekt ukazujcy aspekty omwione w tym
rozdziale zosta umieszczony w katalogu ProAndroid3_R10_Zabezpieczenia. Umiecilimy
w nim rwnie plik Czytaj.TXT wyjaniajcy szczegowo proces importowania
projektw do rodowiska Eclipse.
http://developer.android.com/guide/topics/security/security.html pod tym adresem
znajdziemy podrozdzia Security and Permissions, bdcy czci dokumentacji Android
Developers Guide. Stanowi on oglne omwienie zagadnienia wraz z odnonikami
do wielu dalszych materiaw.
335
Podsumowanie
W niniejszym rozdziale wyjanilimy, e Android wymaga cyfrowych certyfikatw od wszystkich aplikacji. Opisalimy sposoby zapewnienia bezpieczestwa podczas projektowania aplikacji
na emulatorze oraz w rodowisku Eclipse, a take metody podpisywania kocowych wersji pakietu Android. Przedstawilimy take kwesti zabezpiecze rodowiska wykonawczego mona
si byo dowiedzie, e instalator w systemie Android wymaga od aplikacji uprawnie podczas
jej instalowania. Pokazalimy rwnie, w jaki sposb definiowa uprawnienia wymagane przez
aplikacj, a take jak utworzy wasne, niestandardowe uprawnienia. Na koniec wyjanilimy,
jak dostawcy treci kontroluj dostp do swoich zasobw, a take jak pozwalaj jednym jednostkom przekazywa uprawnienia dla innych jednostek, ktre mog zosta wywoane w celu przeprowadzenia okrelonych operacji na danych z dostawcy treci, bez koniecznoci nadawania
takiej pomocniczej jednostce uprawnie do wszystkich informacji zawartych w dostawcy.
W nastpnym rozdziale zajmiemy si tworzeniem oraz uytkowaniem usug w Androidzie.
R OZDZIA
11
Tworzenie i uytkowanie usug
Na kocu tego rozdziau zamieszczamy adres, pod ktrym Czytelnik znajdzie projekty
pozwalajce zrozumie koncepcje omwione w rozdziale. Istnieje moliwo ich
bezporedniego zaimportowania do rodowiska Eclipse. Ponadto, poniewa kod
bdzie prbowa uzyska dostp do internetu, podczas wywoywania protokou HTTP
za pomoc moduu HttpClient musimy doda w pliku manifecie upowanienie
android.permission.INTERNET.
Listing 11.1. Stosowanie klas HttpClient i HttpGet plik HttpGetDemo.java
import
import
import
import
import
import
import
import
import
java.io.BufferedReader;
java.io.IOException;
java.io.InputStreamReader;
org.apache.http.HttpResponse;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpGet;
org.apache.http.impl.client.DefaultHttpClient;
android.app.Activity;
android.os.Bundle;
339
Modu
Podczas wykonywania dania GET parametry (nazwy i wartoci) tego dania s przekazywane
w postaci czci adresu URL. Przekazywanie parametrw w ten sposb ma pewne ograniczenia.
Gwoli cisoci, dugo adresu URL nie powinna przekracza 2048 znakw. Jeeli chcemy wysa wiksz ilo danych, powinnimy skorzysta z dania metody HTTP POST. Metoda POST
jest elastyczniejsza i przekazuje parametry w formie czci treci dania.
Kod widoczny na listingu 11.3 zastpuje trzy wiersze z listingu 11.1, w miejscu, gdzie jest wykorzystywana metoda HttpGet. Caa reszta pozostaje bez zmian. Aby da wywoania metody
POST za pomoc moduu HttpClient, musimy wywoa jego metod execute() wobec instancji HttpPost. Podczas przeprowadzania wywoa metody POST zazwyczaj przekazujemy
parametry nazwa warto zakodowane w postaci adresu URL jako cz dania protokou
HTTP. W celu wykonania tego za pomoc moduu HttpClient musimy utworzy list zawierajc wystpienia obiektw NameValuePair i umieci j w klasie UrlEncodedFormEntity.
Obiekt NameValuePair zawiera kombinacj parametrw nazwa warto, natomiast klasa
UrlEncodedFormEntity potrafi przetumaczy list tych obiektw na jzyk zrozumiay dla wywoa protokou HTTP (ogem wywoa metody POST). Po utworzeniu klasy UrlEncodedForm
Entity mona ustanowi typ obiektu funkcji HttpPost, a nastpnie odpowiedzie na danie.
W kodzie pokazanym na listingu 11.3 stworzylimy modu HttpClient, a nastpnie wywoalimy
funkcj HttpPost posiadajc adres URL punktu kocowego protokou HTTP. Wygenerowalimy
list obiektw NameValuePair i umiecilimy w niej pojedynczy parametr nazwa warto.
Nastpnie utworzylimy wystpienie obiektu UrlEncodedFormEntity i przekazalimy jego
konstruktorowi list obiektw NameValuePair. Na koniec wywoalimy metod setEntity()
dania metody POST i spenilimy to danie za pomoc instancji HttpClient.
341
W rzeczywistoci metoda HTTP POST jest o wiele potniejszym narzdziem. Dziki niej
moemy rwnie atwo przekazywa pojedyncze parametry nazwa warto, co zostao przedstawione na listingu 11.3, jak rwnie zoone obiekty, takie jak pliki. Metoda HTTP POST obsuguje inny format treci dania, znany jako wieloczciowa metoda POST (ang. multipart
POST). Dziki temu typowi metody POST moemy wraz z parametrami nazwa warto wysya wasne pliki. Niestety, wersja moduu HttpClient dostpna w Androidzie nie obsuguje
wieloczciowych metod POST w sposb bezporedni. Aby mona byo przeprowadza wieloczciowe wywoania metody POST, potrzebne jest dodatkowe oprogramowanie. Chodzi
o trzy posiadajce jawny kod rdowy projekty firmy Apache: Apache Commons IO, Mime4J
i HttpMime, ktre mona pobra z nastpujcych witryn:
Commons IO http://commons.apache.org/io/,
Mime4J http://james.apache.org/mime4j/,
HttpMime http://hc.apache.org/downloads.cgi (wewntrz wersji HttpClient).
Na listingu 11.4 zostao przedstawione zastosowanie wieloczciowej metody POST w Androidzie.
Listing 11.4. Tworzenie wywoania wieloczciowej metody POST
import
import
import
import
import
import
import
import
import
import
java.io.ByteArrayInputStream;
java.io.InputStream;
org.apache.commons.io.IOUtils;
org.apache.http.HttpResponse;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpPost;
org.apache.http.entity.mime.MultipartEntity;
org.apache.http.entity.mime.content.InputStreamBody;
org.apache.http.entity.mime.content.StringBody;
org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
public class TestMultipartPost extends Activity
{
public void executeMultipartPost()throws Exception
{
try {
InputStream is = this.getAssets().open("data.xml");
HttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest =
new HttpPost("http://jakisserwersieciowy.com/uslugi/robCos.do");
byte[] data = IOUtils.toByteArray(is);
InputStreamBody isb = new InputStreamBody(new
ByteArrayInputStream(data),"uploadedFile");
StringBody sb1 = new StringBody("Wpisujemy Jaki Tekst");
StringBody sb2 = new StringBody("Rwnie Wpisujemy Jaki Tekst");
MultipartEntity multipartContent = new MultipartEntity();
multipartContent.addPart("uploadedFile", isb);
multipartContent.addPart("one", sb1);
multipartContent.addPart("two", sb2);
postRequest.setEntity(multipartContent);
eby aktywowa wieloczciow metod POST, musimy wywoa modu HttpPost i uruchomi
jego metod setEntity() wraz z instancj MultiPartEntity (zamiast obiektu UrlEncoded
FormEntity, ktry tworzylimy dla pojedynczego parametru nazwa warto). Element
MultiPartEntity reprezentuje tre dania wywoania wieloczciowej metody POST. Jak
wida, najpierw tworzymy wystpienie MultiPartEntity, a nastpnie wywoujemy metod
addPart() dla kadej czci. Na listingu 11.4 dodano do dania trzy czci: dwa cigi znakw oraz plik XML.
Jeeli tworzymy aplikacj wymagajc przekazania wieloczciowej metody POST do zasobu
sieciowego, prawdopodobnie zechcemy sprawdzi nasze rozwizanie pod ktem bdw,
korzystajc z pozorowanej implementacji usugi na lokalnej stacji roboczej. Podczas pracy
z aplikacjami na stacji roboczej uzyskujemy dostp do komputera lokalnego za pomoc polecenia localhost lub adresu IP 127.0.0.1. Jednak w przypadku aplikacji dla Androida nie bdziemy korzysta z polecenia localhost (ani z adresu 127.0.0.1), poniewa emulator bdzie
swoim wasnym lokalnym hostem. Nie chcemy wskazywa tego klienta usudze znajdujcej si
w urzdzeniu obsugujcym Androida, tylko nakierowa na stacj robocz. eby uzyska dostp do swojej projektowej stacji roboczej z poziomu emulatora, musimy uy jej adresu IP
(w rozdziale 2. omwilimy sposb okrelenia adresu IP stacji roboczej). Na listingu 11.4 naley
podstawi w miejsce widocznego adresu IP adres IP stacji roboczej Czytelnika.
343
Obsuga wyjtkw
Obsuga wyjtkw jest czci kadego programu, jednak podczas tworzenia oprogramowania
wykorzystujcego usugi zewntrzne (na przykad usugi HTTP) trzeba zwraca szczegln uwag na wyjtki, poniewa prawdopodobiestwo wystpowania bdw zostaje zwielokrotnione.
Istnieje kilka rodzajw wyjtkw, jakich mona si spodziewa podczas korzystania z usug HTTP.
Do nich zalicza si wyjtki transportowe, wyjtki protokoowe oraz przekroczenia limitu czasu.
Naley wiedzie, kiedy te wyjtki mog si pojawi.
Wyjtki transportowe mog wystpi z wielu rnych powodw, jednak w przypadku urzdze
mobilnych najczstszym scenariuszem jest niska jako poczenia sieciowego. Wyjtki protokoowe wystpuj na poziomie warstwy protokou HTTP. Nale do nich bdy uwierzytelniania, niewaciwe pliki cookies i tak dalej. Moemy spodziewa si wyjtku protokoowego na
przykad wtedy, gdy mamy dostarczy powiadczenie tosamoci za pomoc identyfikatora
uytkownika, a tego nie uczynimy. Pod ktem wywoa protokou HTTP przekroczenia limitu
czasu dzielimy na dwie kategorie: przekroczenie limitu czasu poczenia oraz przekroczenie
limitu czasu gniazda. Przekroczenie limitu czasu poczenia nastpuje wtedy, gdy modu
HttpClient nie moe ustanowi poczenia z serwerem na przykad jeli serwer nie odpowiada. Przekroczenie limitu czasu gniazda wystpuje, gdy modu HttpClient nie otrzyma odpowiedzi w okrelonym czasie. Innymi sowy, modu ten zdoa si poczy z serwerem, lecz
serwer nie przekaza odpowiedzi w wyznaczonym limicie czasowym.
Skoro ju znamy rodzaje pojawiajcych si wyjtkw, to jak sobie z nimi poradzi? Na szczcie
modu HttpClient jest solidn struktur, zdejmujc wikszo obowizkw z barkw projektanta. Tak naprawd jedynymi wyjtkami, jakich obsug powinnimy przewidzie, s te,
ktrymi najatwiej zarzdza. Modu HttpClient obsuguje wyjtki transportowe poprzez
java.io.BufferedReader;
java.io.IOException;
java.io.InputStreamReader;
java.net.URI;
import
import
import
import
org.apache.http.HttpResponse;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpGet;
org.apache.http.impl.client.DefaultHttpClient;
/**
* Jeli tutaj trafimy, to znaczy, e prba przebiega pomylnie
* i moemy zakoczy.
*/
return response;
} catch (Exception e) {
/**
* Jeli wyczerpalimy limit powtrze.
*/
if (count < retry) {
/**
* Mamy jeszcze powtrzenia, zatem wywietlamy wiadomo
* i ponawiamy prb.
*/
System.out.println(e.getMessage());
} else {
System.out.println("wszystkie proby nieudane...");
throw e;
}
}
}
return null;
345
}
public String executeHttpGet() throws Exception {
BufferedReader in = null;
try {
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI("http://code.google.com/android/"));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity()
.getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
String result = sb.toString();
return result;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Kod na listingu 11.5 jest ilustracj moliwoci zaimplementowania prostej techniki ponawiania prb w celu wywoania protokou HTTP po przekroczeniu limitu czasu. Na listingu
zostay ukazane dwie metody: pierwsza wykonuje funkcj HTTP GET (executeHttpGet()),
druga natomiast umieszcza pierwsz metod w algorytmie ponawiania prb (executeHttpGet
WithRetry()). Algorytm ten jest niezmiernie prosty. Liczbie powtrze prb przypisujemy warto 3, a nastpnie wprowadzamy ptl while. We wntrzu tej ptli egzekwujemy
danie. Zauwamy, e jest ono umieszczone w bloku try/catch, a w bloku catch sprawdzamy, czy nie wyczerpalimy limitu powtrze.
Podczas korzystania z moduu HttpClient jako czci zwykej aplikacji musimy powici baczniejsz uwag problemom wielowtkowoci, ktre mog si pojawi. Zajmiemy si nimi teraz.
Problemy z wielowtkowoci
W dotychczas ukazanych przykadach dla kadego dania tworzylimy nowy modu HttpClient.
W praktyce jednak powinnimy tworzy jeden modu HttpClient dla caej aplikacji i uywa
go podczas wszelkiej komunikacji poprzez protok HTTP. Jeeli jeden modu HttpClient
przetwarza wszystkie dania protokou HTTP, naley rwnie uwaa na problemy zwizane
z wielowtkowoci, ktre mog si pojawi w trakcie jednoczesnego przetwarzania wielu
org.apache.http.HttpVersion;
org.apache.http.client.HttpClient;
org.apache.http.conn.ClientConnectionManager;
org.apache.http.conn.params.ConnManagerParams;
org.apache.http.conn.scheme.PlainSocketFactory;
org.apache.http.conn.scheme.Scheme;
org.apache.http.conn.scheme.SchemeRegistry;
org.apache.http.conn.ssl.SSLSocketFactory;
org.apache.http.impl.client.DefaultHttpClient;
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
org.apache.http.params.BasicHttpParams;
org.apache.http.params.HttpConnectionParams;
org.apache.http.params.HttpParams;
org.apache.http.params.HttpProtocolParams;
org.apache.http.protocol.HTTP;
347
return customHttpClient;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
Jeeli aplikacja musi wykonywa wicej wywoa protokou HTTP, naley utworzy modu
HttpClient obsugujcy wszystkie te dania. Najprociej moemy tego dokona poprzez
utworzenie klasy singletonowej, do ktrej dostp bdzie uzyskiwany z dowolnego miejsca aplikacji, tak jak pokazalimy powyej. Jest to standardowy wzorzec w rodowisku Java, za pomoc ktrego synchronizujemy dostp do metody pobierania, a ta z kolei przekazuje tylko i wycznie jeden obiekt HttpClient dla klasy singletonowej (i w razie koniecznoci tworzy go
za pierwszym razem).
Przyjrzyjmy si teraz metodzie getHttpClient() klasy CustomHttpClient. Jest ona odpowiedzialna za tworzenie naszego singletonowego moduu HttpClient. Wprowadzilimy pewne
podstawowe parametry, wartoci przekroczenia czasu, a take schematy obsugiwane przez nasz klas HttpClient (na przykad HTTP i HTTPS). Odnotujmy fakt, e podczas tworzenia
metody DefaultHttpClient() przekazujemy obiekt klasy ClientConnectionManager. Jest ona
odpowiedzialna za zarzdzanie poczeniami HTTP moduu HttpClient. Poniewa chcemy,
eby wszystkie poczenia HTTP przetwarza pojedynczy modu HttpClient (a to dlatego,
e dania mog si nakada na siebie w przypadku korzystania z wtku), tworzymy klas
ThreadSafeClientConnManager.
Prezentujemy take prostszy sposb uzyskiwania odpowiedzi z dania HTTP za pomoc klasy
Kod naszej aktywnoci wykorzystujcej klas CustomHttpClient
zosta ukazany na listingu 11.7.
BasicResponseHandler.
java.io.IOException;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpGet;
org.apache.http.impl.client.BasicResponseHandler;
org.apache.http.params.HttpConnectionParams;
org.apache.http.params.HttpParams;
android.app.Activity;
android.os.Bundle;
android.util.Log;
// obejmuje:
// ClientProtocolException
// ConnectTimeoutException
// ConnectionPoolTimeoutException
// SocketTimeoutException
e.printStackTrace();
}
}
}
349
Po uzyskaniu wystpienia klasy AndroidHttpClient nie moemy w nim zmieni ani doda
adnego parametru (na przykad wersji protokou HTTP). Nasze opcje maj suy do przesaniania ustawie wewntrz obiektu HttpGet (co pokazalimy ju wczeniej) albo nie powinnimy korzysta z klasy AndroidHttpClient.
Na tym zakoczymy dyskusj dotyczc przetwarzania usug HTTP za pomoc moduu
HttpClient. W kolejnych podrozdziaach przedstawimy kolejny interesujcy element Androida:
351
java.io.IOException;
org.apache.http.HttpResponse;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpGet;
org.apache.http.params.BasicHttpParams;
org.apache.http.params.HttpConnectionParams;
org.apache.http.params.HttpParams;
org.apache.http.util.EntityUtils;
android.app.Activity;
android.content.Context;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.os.AsyncTask;
android.util.Log;
android.widget.ImageView;
android.widget.TextView;
353
// Obejmuje:
// ClientProtocolException
// ConnectTimeoutException
// ConnectionPoolTimeoutException
// SocketTimeoutException
e.printStackTrace();
}
return null;
}
355
android.app.Activity;
android.os.AsyncTask;
android.os.Bundle;
android.util.Log;
android.view.View;
jakie zadanie jest ju uruchomione. Sprawdzamy wic stan klasy AsyncTask. Jeeli ma on
jakkolwiek inn warto ni FINISHED, to znaczy, e zadanie jest uruchomione (RUNNING)
lub oczekujce (PENDING) i gotowe do uruchomienia. A zatem jeli dla danego zadania klasa
AsyncTask pozostaje w stanie FINISHED, moemy je uzna za zakoczone i utworzy now
klas AsyncTask. Oczywicie, jeli poprzednie wystpienie klasy AsyncTask mogo z powodzeniem pobra obraz, moemy nie chcie pobiera go ponownie. Jednak w naszym przykadzie
pozwalamy na jego ponowne pobranie.
W trakcie dziaania naszej przykadowej aplikacji po wciniciu przycisku pojawi si aktualizowany komunikat o postpach pracy, a po chwili rysunek. Przycisk przechodzi ze stanu kliknicia
w stan normalny, jeszcze zanim komunikat o postpach zacznie si aktualizowa. Jest to bardzo
wana obserwacja, poniewa oznacza ona, e gwny wtek powrci do zarzdzania interfejsem
uytkownika w trakcie pobierania obrazu.
Z czystej ciekawoci przejdmy do adresu URL kierujcego aplikacj na stron z wykresami
i wprowadmy jak zmian, ktra zepsuje ten cig znakw. Wczmy teraz ponownie aplikacj. Wynik powinien by podobny do przedstawionego na rysunku 11.2.
Warto dowiedzie si jeszcze kilku rzeczy na temat klasy AsyncTask. Po utworzeniu rozszerzenia klasy AsyncTask i uruchomieniu metody execute() nasz gwny wtek wraca do
przetwarzania kodu. Cigle jednak posiadamy odniesienie do zadania i moemy na nim
dziaa z poziomu gwnego wtku. Na przykad moemy wywoa metod cancel(), aby zakoczy to zadanie. Za pomoc metody isCancelled() sprawdzamy, czy zostao ono anulowane. Moemy chcie zmodyfikowa logik w metodzie onPostExecute() pod ktem
anulowania zadania. Z kolei klasa AsyncTask zawiera dwie odmiany metody get(), w ktrych
wynik otrzymujemy z metody doInBackground(), bez koniecznoci wykorzystywania metody
357
onPostExecute(). Jedna z tych odmian metody get() blokuje korzystanie z metody onPost
Execute(), podczas gdy druga korzysta z wartoci przekroczenia limitu czasu zapobie-
android.app.Activity;
android.os.AsyncTask;
android.os.Bundle;
android.util.Log;
android.view.View;
// odniesienie do aktywnoci.
if(diTask.getStatus() == AsyncTask.Status.FINISHED)
diTask.setImageInView();
}
}
public void doClick(View view) {
if(diTask != null) {
AsyncTask.Status diStatus = diTask.getStatus();
Log.v("doClick", "Stan diTask wynosi " + diStatus);
if(diStatus != AsyncTask.Status.FINISHED) {
Log.v("doClick", "...nie trzeba uruchamia nowego zadania");
return;
}
Powyszy przykadowy kod jest bardzo podobny do aktywnoci HttpActivity widocznej na listingu 11.11. Rnica midzy nimi polega na wywoaniu metody getLastNonConfiguration
Instance() w celu sprawdzenia, czy w starym wystpieniu tej aktywnoci znajduje si obiekt
DownloadImageTask. Jeeli istnieje taki obiekt, musimy przekaza mu odniesienie do nowej
aktywnoci i w ten sposb da mu dostp do nowych widokw. Na listingu 11.13 ujrzymy
zmodyfikowany kod klasy DownloadImageTask. Po ustanowieniu nowego kontekstu w klasie
DownloadImageTask sprawdzamy, czy zadanie zostao zakoczone, poniewa mogo si tak
sta w trakcie odtwarzania klasy HttpActivity. Jeli stwierdzimy zakoczenie zadania, stosujemy metod setImageInView() do zaktualizowania obrazu wicej informacji na ten temat
znajdzie si poniej.
359
java.io.IOException;
org.apache.http.HttpResponse;
org.apache.http.client.HttpClient;
org.apache.http.client.methods.HttpGet;
org.apache.http.params.BasicHttpParams;
org.apache.http.params.HttpConnectionParams;
org.apache.http.params.HttpParams;
org.apache.http.util.EntityUtils;
android.app.Activity;
android.content.Context;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.os.AsyncTask;
android.util.Log;
android.widget.ImageView;
android.widget.TextView;
// 5 sekund hibernacji
// Obejmuje:
// ClientProtocolException
// ConnectTimeoutException
// ConnectionPoolTimeoutException
// SocketTimeoutException
e.printStackTrace();
}
return null;
361
((Activity) mContext).findViewById(R.id.image);
mImage.setImageBitmap(downloadedImage);
}
}
Powinnimy zwrci uwag, e procedura obsugi przycisku doClick() pozostaje niezmieniona. Teraz jednak posiadamy implementacj opart na funkcji onRetainNonConfiguration
Instance(), ktrej jedynym zadaniem jest przekazywanie zwracanego obiektu. W naszym
przypadku zaley nam wycznie na obiekcie DownloadImageTask, wic tylko ten obiekt musimy przekaza. Gdybymy chcieli przekaza wicej elementw, musielibymy utworzy obiekt
przechowujcy je wszystkie, a nastpnie przekaza ten obiekt. Mamy do czynienia z podstawowymi obiektami rodowiska Java, nie musimy wic przejmowa si serializacj ani obiektami typu Parcel (w dalszej czci rozdziau zajmiemy si tymi obiektami). Klas AsyncTask przekazujemy wycznie po to, aby j wykorzysta nieco pniej. Moemy wic ponownie nawiza
z ni poczenie.
Zachcamy teraz do uruchomienia naszej przykadowej aplikacji. Dodalimy piciosekundowe opnienie do klasy AsyncTask, aby da Czytelnikowi czas na obrcenie ekranu w trakcie
dziaania drugoplanowego zadania. Aby zasymulowa obrt ekranu urzdzenia za pomoc
emulatora, uywamy skrtu klawiaturowego Ctrl+F11. Interfejs uytkownika powinien si
obrci i odpowiednio dostosowa do pooenia wywietlacza. Z kadym obrotem pobrany
obraz znika (jeli by widoczny) i pojawia si ponownie w trakcie przetwarzania kodu. Warto
obraca ekran na rnych etapach dziaania aplikacji, aby sprawdzi efekty. Moemy nawet
wrci do poprzedniego przykadu, doda opnienie i przekona si, e w trakcie obracania
nie zachowuje si on zgodnie z yczeniem uytkownika. Przyjrzymy si teraz nowemu kodowi,
aby zrozumie mechanizm jego dziaania.
Tym razem klasa rozszerzajca klas AsyncTask jest nieco inna.
W trakcie zmiany konfiguracji dzieje si kilka rzeczy, z ktrymi musimy sobie poradzi. Przede
wszystkim trzeba wiedzie, e klasa AsyncTask musi otrzyma nowe odniesienie do aktywnoci, aby mc aktualizowa odpowiednie widoki zarwno w metodzie onProgressUpdate(),
jak i onPostExecute(). Po zakoczeniu starej aktywnoci odniesienie do niej staje si bezuyteczne i potrzebujemy nowego. Gdybymy zmienili jaki element w interfejsie uytkownika
umieszczonym w metodzie onPreExecute(), musielibymy rwnie w niej zamieci odniesienie do nowej aktywnoci. Teraz mona wykorzysta metod nazwan setContext(), dziki
ktrej kontekst jest aktualizowany wraz z pozostaymi obiektami, wic w razie potrzeby nie
powinnimy mie problemw ze znalezieniem widokw.
Poza tym nieco inaczej zapewniamy obsug aktualizacji postpw. W dalszym cigu korzystamy z obiektu postpw, do ktrego moemy si odnosi w metodzie setContext(), a take
w metodzie setProgress(). Wywoujemy teraz metod setProgress() w odpowiednich
Na ukad graficzny projektu skada si jeden przycisk i jedno pole tekstowe. Wcinicie przycisku spowoduje rozpoczcie pobierania, a w polu tekstowym bd wywietlane informacje dotyczce rozpoczcia oraz zakoczenia tego procesu. Interfejs uytkownika zosta zaprezentowany na rysunku 11.3.
android.app.Activity;
android.app.DownloadManager;
android.content.BroadcastReceiver;
android.content.Context;
android.content.Intent;
android.content.IntentFilter;
android.net.Uri;
android.os.Bundle;
android.util.Log;
android.view.View;
android.widget.TextView;
tv = (TextView)findViewById(R.id.tv);
@Override
protected void onResume() {
super.onResume();
dMgr = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
}
public void doClick(View view) {
DownloadManager.Request dmReq = new DownloadManager.Request(
Uri.parse(
"http://dl-ssl.google.com/android/repository/" +
"platform-tools_r01-linux.zip"));
dmReq.setTitle("Narzdzia dla platformy");
dmReq.setDescription("Wersja dla systemu Linux");
dmReq.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE);
IntentFilter filter = new
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
363
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
dMgr = null;
}
Zrozumienie tego kodu nie powinno nastrcza wikszych trudnoci. Najpierw inicjalizujemy
gwny widok i otrzymujemy odniesienie do pola tekstowego. Wewntrz metody onResume()
uzyskujemy odniesienie do usugi DOWNLOAD_SERVICE. Zauwamy, e usuwamy porednio
z tej usugi w metodzie onPause(). Metoda obsugujca kliknicie doClick() generuje nowe
danie DownloadManager.Request za pomoc cieki do pliku, ktry chcemy pobra. Ponadto
definiujemy tytu, opis i rodzaj preferowanego typu sieci. Istnieje jeszcze kilka dodatkowych
opcji do wyboru. Zostay one szczegowo opisane w dokumentacji.
W celach demonstracyjnych jako typ poczenia wybralimy sie komrkow, moemy jednak
zdefiniowa sie Wi-Fi (za pomoc wartoci NETWORK_WIFI zamiast NETWORK_MOBILE), moemy
te poczy obydwie wartoci za pomoc operatora OR. Domylnie obydwa rodzaje sieci mog
pobiera dane, co w przypadku naszej aplikacji oznacza, e bdziemy korzysta z sieci komrkowej, nawet jeli bdzie dostpna sie Wi-Fi.
Po zdefiniowaniu danego obiektu utworzylimy i zarejestrowalimy filtr dla odbiorcy komunikatw. Wkrtce przedstawimy kod tego odbiorcy. Dziki jego zarejestrowaniu uytkownik
zostanie poinformowany o zakoczeniu pobierania kadego pliku. Oznacza to, e musimy ledzi identyfikator dania, odsyany po wywoaniu metody enqueue() wobec klasy Download
Manager. Na koniec aktualizujemy w interfejsie uytkownika informacj o stanie, tym samym
uytkownik bdzie informowany o rozpoczynaniu pobierania.
Aby aplikacja zadziaaa, musimy zdefiniowa kilka uprawnie w pliku AndroidManifest.xml,
co zostao ukazane na listingu 11.16. Wrd wymaganych uprawnie musimy zadeklarowa
dostp do internetu oraz moliwo zapisywania plikw na karcie SD. Co dziwne, jeeli w wersji
2.3 Androida nie wprowadzimy tych uprawnie, pojawi si komunikat o bdzie, informujcy
o braku uprawnie ACCESS_ALL_DOWNLOADS, mimo e uprawnienie to nie jest naszej przykadowej
aplikacji potrzebne.
365
Powiadomieniem w tym przypadku jest wywietlenie paska postpu pobierania pliku. Po zakoczeniu pobierania element ten zostanie wyczyszczony, a w oknie aplikacji pojawi si dodatkowe informacje, widoczne na rysunku 11.5.
Jednym ze sposobw zlokalizowania pobranego pliku jest wykorzystanie usugi klasy Download
Manager. Klasa ta wymaga zdefiniowanego identyfikatora pobierania. Widzielimy to na listingu 11.17. Klasa DownloadManager zapewnia rozpoznawanie pobranego pliku po identyfikatorze
pobierania. Chocia nasza aplikacja umiecia ten plik w publicznym obszarze karty SD, moemy
w rzeczywistoci wskaza, e zostanie umieszczony w rejonie prywatnych danych aplikacji za
pomoc jednej z metod typu setDestination*() obiektu DownloadManager.Request.
Klasa DownloadManager posiada wasn aplikacj, dziki ktrej mona przejrze list pobranych plikw. Na poziomie menu aplikacji na emulatorze lub w urzdzeniu poszukajmy ikony
przedstawionej na rysunku 11.6.
Dziki tej aplikacji moemy rwnie uzyska dostp do tych plikw. Sprbujmy ju teraz. Po
uruchomieniu programu Downloads ujrzymy okno zaprezentowane na rysunku 11.7. Menu
widoczne w dolnej czci ekranu pojawi si dopiero w momencie zaznaczenia pola wyboru znajdujcego si przy nazwie pobranego pliku, co zrobilimy tu przed wykonaniem zrzutu ekranu.
Klasa DownloadManager zawiera dostawc treci przechowujcego informacje o pobranym pliku.
Aplikacja Downloads po prostu uzyskuje dostp do tego dostawcy w celu wygenerowania listy
plikw dostpnych dla uytkownika. Oznacza to, e my rwnie moemy przebada tego dostawc wewntrz naszej aplikacji, aby zdoby dane o pobranych plikach. W tym celu wykorzystujemy zapytanie DownloadManager.Query oraz metod query(). Nie mamy tu jednak do
dyspozycji zbyt wielu opcji wyszukiwania. Moemy wyszukiwa pliki za pomoc identyfikatorw
pobierania (jednego bd kilku) lub pod ktem stanu pobierania. Wynikiem metody query()
jest obiekt Cursor, ktry moe zosta wykorzystany do sprawdzenia wierszy w dostawcy treci
klasy DownloadManager. List dostpnych kolumn znajdziemy w dokumentacji tej klasy, a niektre
367
z nich to lokalny identyfikator Uri pobranego pliku, rozmiar pliku w bajtach, rodzaj pliku, status
pobierania oraz kilka innych parametrw. Gdy uzyskamy w ten sposb dostp do dostawcy
treci, musimy doda w pliku AndroidManifest.xml uprawnienie ACCESS_ALL_DOWNLOADS.
Moemy rwnie wykorzysta metod remove() do anulowania procesu pobierania, chocia
znajdujcy si na karcie fragment pliku nie zostanie automatycznie usunity.
Pokazalimy, w jaki sposb mona korzysta z usug opartych na protokole HTTP, a take jak
zarzdza interfejsem tych usug za pomoc wyspecjalizowanej klasy AsyncTask. Typowo stosujemy j w sytuacji, w ktrej jaka operacja trwa przez okrelony, niezbyt dugi czas oraz ktrej
wyniki bezporednio wpywaj w jaki sposb na interfejs uytkownika. Niekiedy jednak trzeba
uruchomi jaki proces przetwarzania danych trwajcy przez duszy czas albo przywoa jak
niezwizan z interfejsem uytkownika funkcj, istniejc w osobnej aplikacji. Do takich
celw mona wykorzysta usugi systemu Android. Zajmiemy si teraz ich omwieniem.
Usugi w Androidzie
Klasa Service stanowi oson dla kodu wykazujcego zachowanie charakterystyczne dla usug.
W przeciwiestwie do omawianej wczeniej klasy AsyncTask, obiekt klasy Service nie generuje
automatycznie wasnych wtkw. Niezbdna jest ingerencja programisty, aby obiekt Service
mg korzysta z wtkw. Oznacza to, e w przypadku braku funkcji wtkowania w usudze jej
kod bdzie przetwarzany w gwnym wtku. Jeli usuga prowadzi czynnoci zajmujce niewiele
czasu, nie powinno to stanowi problemu. Wtkowanie staje si niezbdne w przypadku operacji
zajmujcych wicej czasu. Pamitajmy, e nie ma adnych przeciwskaza co do wykorzystania
klasy AsyncTask do wtkowania wewntrz usug.
Android wykorzystuje koncepcj usug z dwch powodw:
Po pierwsze, ma to na celu uatwienie implementacji zada przetwarzanych w tle.
Po drugie, aby zapewni komunikacj midzyprocesow dla aplikacji
uruchomionych na jednym urzdzeniu.
Te dwa powody odpowiadaj dwm konkretnym rodzajom usug w Androidzie: usugom
lokalnym i usugom zdalnym. W pierwszym przypadku jako przykad mona wskaza usug
lokaln, zaimplementowan jako cz aplikacji pocztowej. Usuga ta zajmowaaby si wysyaniem wiadomoci na serwer pocztowy wraz z zacznikami i ponowieniami. Poniewa proces ten
moe zabra troch czasu, usuga stanowi dobre rozwizanie. Pozwala na osonicie funkcji, dziki
czemu mona je przenie z gwnego wtku, a nastpnie wrci do obsugi da uytkownika.
W dodatku, nawet jeli aktywno aplikacji pocztowej zostanie zakoczona, wiadomoci wci
musz zosta dostarczone. Jak si przekonamy, przykadem usugi wykorzystanej z drugiego
powodu jest omwiona w dalszej czci rozdziau aplikacja do tumaczenia tekstu. Zamy, e
na urzdzeniu uruchomiono kilka aplikacji korzystajcych z jednego tekstu. W tym czasie, gdy
aplikacje te pracuj, potrzebujemy usugi, ktra przyjmuje tekst do przetumaczenia z jednego
jzyka na drugi. Zamiast umieszcza odpowiedni kod w kadej aplikacji, moemy napisa
zdaln usug tumaczc i sprawi, eby aplikacje si z ni komunikoway.
Istniej pewne istotne rnice pomidzy usugami lokalnymi a zdalnymi. cilej mwic, jeeli
usuga jest uywana wycznie przez skadniki w obrbie jednego procesu, klient musi j uruchomi za pomoc wywoania metody Context.startService(). Jest to usuga lokalna, poniewa jej celem jest, zasadniczo, uruchamianie zada przetwarzanych w tle dla aplikacji, ktra
uruchomia t usug. Jeeli usuga obsuguje metod onBind(), mamy do czynienia z jej wersj zdaln, ktr mona wywoa za pomoc komunikacji midzyprocesowej (Context.bind
Service()). Usugi zdalne s rwnie nazywane usugami obsugujcymi jzyk AIDL,
poniewa klienty porozumiewaj si z nimi za pomoc jzyka AIDL.
Chocia interfejs klasy android.app.Service obsuguje zarwno usugi lokalne, jak i zdalne,
wdroenie jednej implementacji usugi, ktra obsugiwaaby obydwa rodzaje usug, nie jest dobrym pomysem. Wynika to z faktu, e kady typ usugi posiada predefiniowany cykl ycia;
poczenie obydwu rodzajw, chocia jest dopuszczalne, moe powodowa bdy.
Moemy teraz przeprowadzi szczegow analiz obydwu kategorii usug. Zaczniemy od
omwienia usug lokalnych, a nastpnie przejdziemy do usug zdalnych (usug obsugujcych
jzyk AIDL). Jak ju wspomnielimy, usuga lokalna jest wywoywana jedynie przez zarzdzajc ni aplikacj. Usugi zdalne obsuguj mechanizm wywoania RPC (ang. Remote Procedure Call zdalne wywoanie procedury). Usuga tego typu umoliwia zewntrznym klientom,
istniejcym na tym samym urzdzeniu, podczenie do niej i korzystanie z jej funkcji.
369
Drugi rodzaj usugi nosi w Androidzie rne nazwy: usuga zdalna, usuga obsugujca
jzyk AIDL, usuga AIDL, usuga zewntrzna oraz usuga RPC. Nazwy te dotycz tego
samego typu usugi pozwalajcej na uzyskanie do niej dostpu zdalnego przez
aplikacje uruchomione na urzdzeniu.
Usugi lokalne
Usugi lokalne s usugami uruchamianymi za pomoc metody Context.startService(). Ten
typ usug bdzie dziaa od chwili uruchomienia do momentu wywoania przez klienta metody
Context.stopService() wobec usugi lub do czasu, gdy usuga sama nie wywoa metody
stopSelf(). Zwrmy uwag, e podczas wywoania metody Context.startService(),
w przypadku gdy usuga nie zostaa jeszcze utworzona, system j utworzy i wywoa jej metod
onStartCommand(). Musimy pamita, e ponowne wywoanie metody Context.startService()
nie spowoduje utworzenia nowej instancji usugi (jeeli ju istnieje), lecz przywoa ponownie
metod onStartCommand() ju istniejcej usugi. Poniej przedstawiamy przykady usug
lokalnych:
Usuga monitorujca odczyty z czujnikw urzdzenia oraz przeprowadzajca analiz
danych, a po spenieniu okrelonych warunkw wywietlajca alert. Usuga ta moe
dziaa nieprzerwanie.
Usuga wykonujca zadania, umoliwiajca aktywnociom aplikacji zgaszanie
czynnoci do wykonania oraz kolejkujca je. Usuga ta moe dziaa wycznie przez
okres zgaszania tych czynnoci.
Na listingu 11.18 przedstawiono przykad usugi lokalnej, stanowicej implementacj usugi
wykonujcej zadania w tle. Efektem naszej pracy s cztery artefakty niezbdne do utworzenia
oraz uytkowania usugi: plik BackgroundService.java (sama usuga), plik MainActivity.java
(tworzy klas aktywnoci wywoujcej usug), plik main.xml (ukad graficzny aktywnoci)
oraz plik AndroidManifest.xml. Na listingu 11.18 umieszczono wycznie kod z pliku
BackgroundService.java. Najpierw przeanalizujemy t klas, a nastpnie zajmiemy si pozostaymi trzema plikami. Ponisza implementacja wymaga co najmniej wersji 2.0 Androida.
Listing 11.18. Implementowanie usugi lokalnej plik BackgroundService.java
import
import
import
import
import
import
import
android.app.Notification;
android.app.NotificationManager;
android.app.PendingIntent;
android.app.Service;
android.content.Intent;
android.os.IBinder;
android.util.Log;
371
}
private void displayNotificationMessage(String message)
{
Notification notification =
new Notification(R.drawable.emo_im_winking,
message, System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR;
PendingIntent contentIntent =
PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
notification.setLatestEventInfo(this, TAG, message,
contentIntent);
notificationMgr.notify(0, notification);
}
}
Struktura obiektu Service jest nieco podobna do architektury aktywnoci. Mamy do dyspozycji
metod onCreate() suc do inicjalizacji, a take metod onDestroy() odpowiedzialn za
zakoczenie dziaania usugi. W wersjach Androida starszych od 2.0 usugi posiaday metod
onStart(), ktra poczwszy od wersji 2.0 zostaa przemianowana na onStartCommand().
Rnica pomidzy tymi metodami polega na wstawieniu parametru flagi w tym drugim przypadku, dziki ktremu usuga otrzymuje informacje o ponownym dostarczeniu intencji lub
wskazanie, czy usuga powinna zosta ponownie uruchomiona. W naszym przykadzie wykorzystujemy metod onStartCommand(). Usugi nie s wstrzymywane ani wznawiane w taki
sposb jak aktywnoci, zatem nie ujrzymy w nich metod onPause() ani onResume(). Poniewa
mamy do czynienia z usug lokaln, nie bdziemy wprowadzali adnego mechanizmu wizania,
jednak klasa Service wymaga implementacji metody onBind(), wic wprowadzimy jedn
metod odsyajc po prostu warto null.
Wracajc do metody onCreate() nie musimy robi zbyt wiele, wystarczy powiadomi uytkownika o utworzeniu usugi. Dokonujemy tego za pomoc klasy NotificationManager.
Prawdopodobnie Czytelnik zauway ju pasek powiadomie, znajdujcy si w lewym grnym
rogu ekranu Androida. Po jego rozcigniciu uytkownik ujrzy rnorodne komunikaty, a po
klikniciu powiadomienia moe je uruchamia, co zazwyczaj oznacza powrt do aktywnoci
zwizanej z danym powiadomieniem. Poniewa usugi dziaaj (a przynajmniej istniej) w tle,
bez adnej widocznej aktywnoci, musi istnie jaki sposb uzyskania z nimi kontaktu, chociaby po to, aby je wyczy. Tworzymy wic obiekt Notification, uzupeniamy go intencj
oczekujc, dziki ktrej powrcimy do aktywnoci sterujcej, i j tam umieszczamy. Wszystkie
te czynnoci odbywaj si w metodzie displayNotificationMessage(). Jeszcze jednym bardzo wanym zadaniem jest ustawienie flagi na obiekcie Notification, dziki czemu uytkownik
nie bdzie mg usun go z listy. Istnienie tego obiektu w trakcie trwania usugi jest naprawd
niezbdne, zatem wprowadzamy atrybut Notification.FLAG_NO_CLEAR, aby przechowywa
ten obiekt na licie, dopki nie zostanie usunity za pomoc metody onDestroy(). Dokadniej
mwic, usuwanie powiadomie z listy obsuguje znajdujca si wewntrz onDestroy() metoda
cancelAll() uyta wobec klasy NotificationManager.
373
Znamy wic ju podstawy tworzenia prostej usugi lokalnej. Zanim ukaemy kod aktywnoci, zapoznajmy si najpierw z zawartoci pliku ukadu graficznego, zaprezentowan na listingu 11.19.
Listing. 11.19. Implementacja usugi lokalnej plik main.xml
<?xml version="1.0" encoding="utf-8"?>
android.app.Activity;
android.content.Intent;
android.os.Bundle;
android.util.Log;
android.view.View;
Nasza aktywno MainActivity nie rni si zbytnio od innych aktywnoci, jakie wczeniej
tworzylimy. Istnieje prosta metoda onCreate(), suca do konfigurowania interfejsu uytkownika za pomoc pliku ukadu graficznego main.xml. Mamy rwnie do dyspozycji metod
doClick(), obsugujc wywoania zwrotne przycisku. W naszym przykadzie wywoujemy
metod startService() po wciniciu przycisku Rozpocznij usug oraz metod stopService()
po wciniciu przycisku Zakocz usug. W momencie uruchomienia usugi chcemy przekaza
jej pewne dane, czego dokonujemy za pomoc intencji. Wybralimy przekazanie tych danych
w pakiecie Extras, ale gdybymy posiadali identyfikator URI, moglibymy tego dokona za pomoc metody setData(). W czasie zatrzymywania usugi sprawdzamy otrzymane wyniki.
W normalnych warunkach powinnimy otrzyma warto true, jeli jednak usuga nie bya
uruchomiona, moe zosta przekazana warto false. Na koniec, jeli zamykamy aktywno,
chcemy rwnie zatrzyma usug, zatem dokonujemy tego w metodzie onDestroy(). Pozosta
nam jeszcze jeden plik do omwienia AndroidManifest.xml, ktry widzimy na listingu 11.21.
Listing 11.21. Implementacja usugi lokalnej plik AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.services.simplelocal"
android:versionCode="1"
375
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="BackgroundService"/>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
W przypadku usugi lokalnej mamy do dyspozycji jeszcze jedn opcj, wykorzystywan w przypadku uruchomienia tylko jednego jej wystpienia wraz z tylko jednym wtkiem. W takim przypadku w metodzie onCreate() tej usugi moemy utworzy wtek przetwarzajcy wszystkie operacje usugi. Moemy go umieci w metodzie onCreate() zamiast w onStartCommand(). Jest
to dopuszczalne, poniewa metoda onCreate() jest wywoywana tylko raz, a my potrzebujemy
tylko jednego wtku istniejcego przez czas trwania usugi. Nie otrzymalibymy jednak w metodzie onCreate() treci intencji przekazywanej przez metod startService(). Gdybymy
jej potrzebowali, wystarczy skorzysta z opisanego wczeniej algorytmu i wywoa metod
onStartCommand() tylko raz.
Na tym zakoczymy cz dotyczc usug lokalnych. Zostan one dokadniej opisane w nastpnych rozdziaach. Zbadajmy teraz usugi AIDL posiadajce bardziej zoon struktur.
Usugi AIDL
W poprzednim podrozdziale pokazalimy, jak naley pisa usug uytkowan przez aplikacj,
ktra uruchomia t usug. Zademonstrujemy teraz technik tworzenia usugi wykorzystywanej przez inne procesy poprzez wywoanie RPC. Podobnie jak w przypadku innych rozwiza
opartych na wywoaniach RPC, tak i teraz musimy korzysta w Androidzie z jzyka IDL (ang.
Interface Definition Language jzyk definiowania interfejsu) do definiowania interfejsu dostpnego dla klientw. W wiecie Androida jzyk ten nosi nazw AIDL. Aby utworzy usug
zdaln, naley wykona ponisze dziaania:
1. Napisz plik AIDL, ktry definiuje interfejs dla klientw. Plik AIDL wykorzystuje
skadni jzyka Java i posiada rozszerzenie .aidl. Nazwa pakietu powinna by taka
sama jak nazwa pakietu w projekcie Androida.
2. Dodaj plik AIDL do projektu w rodowisku Eclipse do katalogu src. Wtyczka ADT
wywoa kompilator jzyka AIDL, dziki ktremu z pliku AIDL zostanie wygenerowany
interfejs Java (kompilator AIDL jest wywoywany podczas procesu budowania aplikacji).
3. Zaimplementuj usug i przeka interfejs z metody onBind().
4. Dodaj konfiguracj usugi do pliku AndroidManifest.xml.
W kolejnych podrozdziaach omwilimy poszczeglne czynnoci.
377
definicji interfejsu tej usugi w pliku AIDL. Na listingu 11.22 zostaa ukazana definicja usugi
IStockQuoteService w jzyku AIDL. Plik ten naley umieci wraz ze wszystkimi innymi
plikami Java zwizanymi z projektem StockQuoteService.
Listing 11.22. Definicja usugi notowa giedowych w jzyku AIDL
// Jest to plik IStockQuoteService.aidl
package com.androidbook.services.stockquoteservice;
interface IStockQuoteService
{
double getQuote(String ticker);
}
/**
379
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public double getQuote(java.lang.String ticker) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(ticker);
mRemote.transact(Stub.TRANSACTION_getQuote, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getQuote = (IBinder.FIRST_CALL_TRANSACTION + 0);
}
public double getQuote(java.lang.String ticker) throws android.os.RemoteException;
}
android.app.Service;
android.content.Intent;
android.os.IBinder;
android.os.RemoteException;
android.util.Log;
Klasa StockQuoteService.java przytoczona na listingu 11.24 przypomina omwion wczeniej usug lokaln BackgroundService, pozbawiona jest jednak klasy NotificationManager.
Zasadnicza rnica polega na zaimplementowaniu w tym przypadku metody onBind(). Przypominamy, e w pliku AIDL wygenerowalimy abstrakcyjn klas Stub, ktra implementowaa
interfejs IStockQuoteService. W naszej implementacji usugi zawieramy wewntrzn klas
StockQuoteServiceImpl, ktra rozszerza klas Stub. Klasa ta suy nam jako implementacja
usugi zdalnej, a instancja tej klasy jest przekazywana z metody onBind(). W ten sposb otrzymujemy dziaajc usug AIDL, chocia zewntrzne klienty nie mog si z ni jeszcze poczy.
Aby wyeksponowa usug klientom, musimy doda deklaracj usugi w pliku AndroidManifest.
xml, tym razem jednak potrzebujemy filtru intencji do jej odsonicia. Na listingu 11.25 zostaa
pokazana deklaracja usugi StockQuoteService. Znacznik <service> jest podrzdny wobec
znacznika <application>.
381
com.androidbook.services.stockquoteservice.IStockQuoteService;
android.app.Activity;
android.content.ComponentName;
android.content.Context;
android.content.Intent;
android.content.ServiceConnection;
android.os.Bundle;
android.os.IBinder;
android.os.RemoteException;
android.util.Log;
android.view.View;
android.widget.Button;
android.widget.Toast;
android.widget.ToggleButton;
383
unbindService(serConn);
callBtn.setEnabled(false);
}
break;
case R.id.callBtn:
callService();
break;
}
}
private void callService() {
try {
double val = stockService.getQuote("ANDROID");
Toast.makeText(MainActivity.this,
"Warto z usugi wynosi " + val,
Toast.LENGTH_SHORT).show();
} catch (RemoteException ee) {
Log.e("MainActivity", ee.getMessage(), ee);
}
}
private ServiceConnection serConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name,
IBinder service)
{
Log.v(TAG, "wywolana metoda onServiceConnected()");
stockService = IStockQuoteService.Stub.asInterface(service);
bindBtn.setChecked(true);
callBtn.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.v(TAG, "wywolana metoda onServiceDisconnected()");
bindBtn.setChecked(false);
callBtn.setEnabled(false);
stockService = null;
}
};
protected void onDestroy() {
Log.v(TAG, "wywolana metoda onDestroy()");
if(callBtn.isEnabled())
unbindService(serConn);
super.onDestroy();
}
}
385
387
Aby zaimplementowa ten kod, utwrzmy nowy projekt Androida nazwany StockQuote
Service2. Przypiszmy polu Create Activity aktywno o nazwie MainActivity i skorzystajmy
z pakietu com.androidbook.services.stock2. Nastpnie dodajmy powyszy plik Person.java
do pakietu com.androidbook.services.stock2 w naszym nowym projekcie.
Interfejs Parcelable definiuje kontrakt odpowiedzialny za doczanie lub odczanie obiektw
w trakcie procesu szeregowania lub rozszeregowania. Podstaw interfejsu Parcelable jest pojemnik Parcel. Klasa Parcel jest szybkim mechanizmem serializowania i deserializowania,
zaprojektowanym specjalnie dla komunikacji midzyprocesowej w Androidzie. W klasie tej s
zawarte metody pozwalajce na rozmieszczanie czonkw klasy w pojemniku oraz uzyskiwanie
do nich dostpu. eby zaimplementowa obiekt komunikacji midzyprocesowej we waciwy
sposb, naley postpowa zgodnie z nastpujcym algorytmem:
1. Zaimplementuj interfejs Parcelable. Oznacza to konieczno implementacji metod
writeToParcel() i readFromParcel(). Pierwsza metoda suy do zapisania obiektu
w paczce, druga natomiast pozwala odczytywa obiekty umieszczone w paczce.
Pamitajmy, e kolejno odczytywania waciwoci musi by taka sama jak kolejno
ich zapisywania.
2. Dodaj waciwo static final o nazwie CREATOR do klasy. Waciwo ta wymaga
implementacji interfejsu android.os.Parcelable.Creator<T>.
3. Przygotuj dla interfejsu Parcelable konstruktor, ktry bdzie tworzy obiekty klasy
Parcel.
4. Zdefiniuj klas Parcelable w pliku .aidl odpowiadajcym plikowi .java, w ktrym
zawarty jest zoony typ danych. Kompilator AIDL bdzie szuka tego pliku podczas
kompilowania plikw AIDL. Na listingu 11.29 umiecilimy przykadowy plik Person.aidl.
Plik ten powinien si znajdowa w tym samym miejscu co plik Person.java.
W przypadku interfejsu Parcelable moe rodzi si pytanie, dlaczego w Androidzie
nie zosta wykorzystany wbudowany w rodowisku Java mechanizm serializacji.
Okazuje si, e twrcy Androida uznali proces serializacji w rodowisku Java za zbyt
powolny, aby speni wymogi komunikacji midzyprocesowej w Androidzie. Zostao
zatem utworzone rozwizanie w postaci interfejsu Parcelable. Naley w nim jawnie
serializowa czonkw klasy, jednak w zamian cay proces przebiega o wiele szybciej.
Naley rwnie uwiadomi sobie, e istniej w Androidzie dwa procesy umoliwiajce
przekazywanie danych do innego procesu. Pierwszy z nich polega na przekazaniu
danych do aktywnoci za pomoc intencji, a drugi na przesaniu interfejsu Parcelable
do usugi. Te dwa mechanizmy nie s stosowane wymiennie i nie naley ich ze sob myli.
Oznacza to, e interfejs Parcelable nie jest przekazywany do aktywnoci. Jeeli chcemy
uruchomi aktywno i przekaza jej dane, powinnimy wykorzysta w tym celu intencj.
Interfejs Parcelable jest przeznaczony wycznie do uytku jako cz definicji AIDL.
Bdziemy potrzebowa pliku .aidl dla kadego interfejsu Parcelable w projekcie. W tym przypadku posiadamy tylko jeden interfejs Parcelable Person. Warto zauway, e nie zosta utworzony plik Person.java w katalogu gen. Naleao si tego spodziewa. Plik ten utworzylimy ju wczeniej.
Zastosujmy teraz klas Person w usudze zdalnej. eby nie komplikowa sprawy, zmodyfikujemy nasz obiekt IStockQuoteService, dziki czemu bdzie pobiera parametr wejciowy
typu danych klasy Person. Pomys jest taki, aby klienty przekazyway klas Person do usugi
w celu powiadomienia, jaka aktywno da wyniku notowania. Nowy plik IStockQuoteService.aidl
zosta zaprezentowany na listingu 11.30.
Listing 11.30. Przekazywanie usugom plikw parcelowanych
// Jest to plik IStockQuoteService.aidl
package com.androidbook.services.stock2;
import com.androidbook.services.stock2.Person;
interface IStockQuoteService
{
String getQuote(in String ticker,in Person requester);
}
Metoda getQuote() przyjmuje obecnie dwa parametry: symbol notowanej firmy i obiekt Person
okrelajcy, jaki obiekt wysya danie. Zwrmy uwag, e umiecilimy wskaniki kierunkowe
dla tych parametrw, poniewa typy danych tych parametrw nie s proste, oraz e wprowadzilimy instrukcj import wobec klasy Person. Klasa Person znajduje si w tym samym pakiecie co definicja usugi (com.androidbook.services.stock2).
Implementacja usugi wyglda teraz tak jak na listingu 11.31, a jej ukad graficzny zosta
umieszczony na listingu 11.32.
Listing 11.31. Implementacja usugi StockQuoteService2
package com.androidbook.services.stock2;
android.app.Notification;
android.app.NotificationManager;
android.app.PendingIntent;
android.app.Service;
android.content.Intent;
android.os.IBinder;
android.os.RemoteException;
389
391
android.app.Activity;
android.content.ComponentName;
android.content.Context;
android.content.Intent;
android.content.ServiceConnection;
android.os.Bundle;
android.os.IBinder;
android.os.RemoteException;
android.util.Log;
android.view.View;
android.widget.Button;
android.widget.Toast;
android.widget.ToggleButton;
import com.androidbook.services.stock2.IStockQuoteService;
import com.androidbook.services.stock2.Person;
public class MainActivity extends Activity {
protected static final String TAG = "StockQuoteClient2";
private IStockQuoteService stockService = null;
private ToggleButton bindBtn;
private Button callBtn;
393
Toast.LENGTH_SHORT).show();
} catch (RemoteException ee) {
Log.e("MainActivity", ee.getMessage(), ee);
}
}
private ServiceConnection serConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name,
IBinder service)
{
Log.v(TAG, "wywolana metoda onServiceConnected()");
stockService = IStockQuoteService.Stub.asInterface(service);
bindBtn.setChecked(true);
callBtn.setEnabled(true);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.v(TAG, "wywolana metoda onServiceDisconnected()");
bindBtn.setChecked(false);
callBtn.setEnabled(false);
stockService = null;
}
};
protected void onDestroy() {
if(callBtn.isEnabled())
unbindService(serConn);
super.onDestroy();
}
}
Nasza usuga jest ju gotowa do uruchomienia. Naley jeszcze przesa j do emulatora, zanim
uruchomimy klienta. Interfejs uytkownika powinien wyglda tak jak na rysunku 11.8.
395
Przewidywania autorw okazay si mylne; w momencie wydania polskiej wersji ksiki caa rodzina
interfejsw Google Language zostaa zdeprecjonowana i przestanie by obsugiwana w grudniu
2011 roku. Wyjtkiem jest wersja 2 tego interfejsu, ktra staa si patn usug przyp. tum.
397
jestrowa klucz API AJAX, wystarczy wysa adres URL swojej strony WWW (dokadnie taki
sam jak wprowadzony w nagwku REFERER) i zaakceptowa warunki korzystania z usug. Otrzymawszy klucz API, naley doda go do adresu URL interfejsu API AJAX w nastpujcy sposb:
&key=Your_API_key_goes_here_with_no_quotation_marks
Jeeli zdecydujemy si przekaza interfejsowi API AJAX klucz API, warto atrybutu REFERER
musi by adresem URL lub jakim jego podelementem za pomoc ktrego zosta utworzony ten klucz. W przeciwnym wypadku nie otrzymamy wynikw.
android.app.Activity;
android.content.ComponentName;
android.content.Context;
android.content.Intent;
android.content.ServiceConnection;
android.os.Bundle;
android.os.Handler;
android.os.IBinder;
399
android.util.Log;
android.view.View;
android.view.View.OnClickListener;
android.widget.ArrayAdapter;
android.widget.Button;
android.widget.EditText;
android.widget.Spinner;
android.widget.TextView;
401
fromLang.setAdapter(fromAdapter);
fromLang.setSelection(1); // Polski
ArrayAdapter<?> toAdapter = ArrayAdapter.createFromResource(this,
R.array.languages,android.R.layout.simple_spinner_item);
toAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
toLang.setAdapter(toAdapter);
toLang.setSelection(3); // Niemiecki
inputText.selectAll();
Intent intent = new Intent(Intent.ACTION_VIEW);
bindService(intent, mTranslateConn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mTranslateConn);
}
public void onClick(View v) {
if (inputText.getText().length() > 0) {
doTranslate();
}
}
android.app.Service;
android.content.Intent;
android.os.IBinder;
android.util.Log;
java.io.BufferedReader;
java.io.InputStream;
java.io.InputStreamReader;
java.net.HttpURLConnection;
java.net.URL;
java.net.URLEncoder;
import org.apache.commons.lang.StringEscapeUtils;
import org.json.JSONObject;
import android.util.Log;
public class Translator {
private static final String ENCODING = "UTF-8";
private static final String URL_BASE =
"http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&langpair=";
private static final String INPUT_TEXT = "&q=";
private static final String MY_SITE = "http://my.website.com";
private static final String TAG = "Translator";
public static String translate(String text, String from, String to) throws Exception
{
try {
StringBuilder url = new StringBuilder();
url.append(URL_BASE).append(from).append("%7C").append(to);
403
url.append(INPUT_TEXT).append(URLEncoder.encode(text, ENCODING));
HttpURLConnection conn = (HttpURLConnection) new URL(url.toString())
.openConnection();
conn.setRequestProperty("REFERER", MY_SITE);
conn.setDoInput(true);
conn.setDoOutput(true);
try {
InputStream is= conn.getInputStream();
String rawResult = makeResult(is);
Przed poprawnym skompilowaniem przykadu musimy wprowadzi klas pomocnicz. W projekcie Jakarta Commons Lang znajduje si klasa StringEscapeUtils, ktr wykorzystamy do
konwersji wynikowego cigu znakw z interfejsu AJAX Language API na tekst zrozumiay dla
uytkownika. Interfejs ten odsya nam obiekty XML reprezentujce okrelone znaki specjalne.
Na przykad odpowiednikiem apostrofu jest tu warto '. Te znaki specjalne musz by
wywietlane w sposb zrozumiay dla uytkownika. W tym celu zastosujemy projekt Jakarta
Commons Lang. Mona go znale pod adresem:
http://commons.apache.org/lang/
Naley otworzy stron projektu Jakarta Commons Lang i pobra plik commons-lang.zip lub
commons-lang.tar, w ktrym zawarte s pliki .jar. Nastpnie naley je rozpakowa. W rodowisku Eclipse wybieramy projekt, klikamy jego nazw prawym przyciskiem myszy i wskazujemy
opcje Build Path/Configure Build Path. Klikamy zakadk Libraries i wybieramy opcj Add
External JARs. Wyszukujemy pobrany plik commons-lang i dodajemy go. Aby zakoczy proces
dodawania pliku, klikamy przycisk OK. Caa aplikacja powinna zosta bezbdnie zbudowana.
Nic nie stoi na przeszkodzie, eby j teraz wyprbowa. Jeeli nie wyglda ona zbyt dobrze
w orientacji pionowej, moemy wyprbowa skrt klawiaturowy Ctrl+F11, aby przeczy
emulator w tryb orientacji poziomej. Jeeli wtpimy w poprawno generowanych wynikw,
moemy je porwna z tumaczeniem dostpnym na serwerze Google:
http://www.google.com/uds/samples/language/translate.html
Chcielibymy uczuli Czytelnika na kilka spraw. Z powodu okrelonych zapisw w warunkach
korzystania z usugi Google nasza przykadowa aplikacja zawiera w interfejsie uytkownika cig
znakw powered by Google. Te same warunki okrelaj maksymalny limit 5000 wprowadzanych
znakw, zatem po przekroczeniu tej liczby nadmiar znakw jest usuwany. Prawdopodobnie
chcemy zaprojektowa tu nieco inny model, na przykad umoliwiajcy dzielenie tekstu na
edytowalne fragmenty, ktre s nastpnie przesyane do interfejsu API. Celowo stworzylimy
krtk list dostpnych jzykw, aby nasza aplikacja bya atwiejsza do zarzdzania, mona
jednak bez problemu zamieci dowoln liczb jzykw w tablicy cigw znakw. Musimy
mie jednak wiadomo, e czcionki Droid mog nie posiada kompletu znakw dla niektrych
jzykw, ktre s dostpne w tumaczu. Jeeli tumaczenie wynikowe wyglda podejrzanie,
prawdopodobnie mamy problem z czcionk. Mona temu zapobiec poprzez wprowadzenie
dodatkowych czcionek, nie jest to jednak tematem tego rozdziau. Odpowiedzi interfejsu API
przybieraj posta formatu JSON. Zatem bdziemy za pomoc tego formatu poddawa analizie
405
skadniowej zwracane wynikowe cigi znakw. Format JSON stanowi cz struktury Androida, zatem nie musielimy go pobiera jako osobnego pliku .jar.
Jedn z cech interfejsu AJAX Language API jest brak koniecznoci wskazania jzyka rdowego. Interfejs ten sprbuje samodzielnie ustali, jaki jzyk jest uywany. Jeeli chcemy skorzysta
z takiego rozwizania, nie zamieszczamy wartoci jzyka rdowego w przekazywanym adresie
URL, lecz zamiast tego w atrybucie langpair= zamieszczamy warto %7C. Jest to przydatna
funkcja, jeli nie jestemy pewni, jaki jzyk rdowy zosta uyty; jednak jeeli ilo wprowadzonego tekstu jest zbyt maa, interfejs API moe nie rozpozna jzyka.
Odnoniki
Poniej prezentujemy przydatne odnoniki, pomagajce zapozna si dokadniej z omawianymi tematami:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
utworzonych specjalnie na potrzeby ksiki. Waciwy plik znajdziesz w katalogu
o nazwie ProAndroid3_R11_Usugi. Dostpny jest tu take plik Czytaj.TXT, stanowicy
dokadn instrukcj importowania projektw do rodowiska Eclipse.
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/ strona ta zawiera
znakomite samouczki dotyczce klas HttpClient, w tym rwnie informacje
o uwierzytelnianiu i korzystaniu z plikw cookies.
Podsumowanie
Cay niniejszy rozdzia zosta powicony usugom. Omwilimy sposb uytkowania zewntrznych usug HTTP za pomoc moduu HttpClient firmy Apache, a take metody pisania
usug przetwarzanych w tle. Pod ktem moduu HttpClient zademonstrowalimy, w jaki sposb mona wywoywa metody HTTP GET oraz HTTP POST. Pokazalimy take zastosowanie
wieloczciowej metody POST.
Druga cz rozdziau dotyczya pisania usug dla systemu Android. W szczeglnoci zajlimy
si tworzeniem usug lokalnych i usug zdalnych. Stwierdzilimy, e usuga lokalna jest uytkowana przez skadniki (na przykad aktywnoci) tego samego procesu, w ktrym znajduje si ta
usuga. Klienty usug zdalnych znajduj si poza procesami, ktre wywoay te usugi.
R OZDZIA
12
Analiza pakietw
We wszystkich dotychczasowych rozdziaach zajmowalimy si podstawowymi skadnikami systemu Android. Chcemy zauway, e to bya atwiejsza cz podry
przez Androida. Poczwszy od niniejszego, w kilku nastpnych rozdziaach (12., 13.,
14. i 15.) przyjrzymy si dokadniej kolejnemu poziomowi organizacji Androida.
Badanie rozpoczniemy od zajrzenia w gb pakietw, procesu ich podpisywania,
wspdzielenia danych pomidzy nimi oraz zapoznania si z projektami bibliotek.
Zrozumiemy take kontekst linuksowego procesu, w ktrym jest uruchomiony plik
.apk. Dowiemy si te, w jaki sposb wiele plikw .apk wspdzieli dane i zasoby za
pomoc tego kontekstu.
Chocia w rozdziale 10. dowiedzielimy si co nieco na temat podpisywania pakietw w Androidzie, dopiero teraz poznamy znaczenie, implikacje oraz zastosowania
podpisanych plikw JAR. W kontekcie wspdzielenia plikw zainteresujemy si
rwnie projektami bibliotek Androida, aby zrozumie, w jaki sposb dziaaj oraz
czy mona ich uywa do wspdzielenia zasobw i kodu.
Rozpocznijmy od przypomnienia podstawowych informacji na temat pliku .apk,
gdy to stanowi baz dalszych rozwaa na temat procesw Androida.
Pakiety i procesy
Jak ju widzielimy w poprzednich rozdziaach, proces tworzenia aplikacji koczy
si utworzeniem pliku .apk, ktry zostaje nastpnie podpisany i wdroony do uytkowania w urzdzeniu. Zobaczmy, czego jeszcze moemy si dowiedzie o pakietach systemu Android.
Jeeli twrca tego pakietu podpisa go i zainstalowa, nikt inny nie bdzie mg go aktualizowa. Nazwa pakietu jest cile zwizana z sygnatur, za pomoc ktrej zosta podpisany. W wyniku tego programista posiadajcy inn sygnatur nie moe podpisywa i instalowa pakietu przy
uyciu nazwy wykorzystywanej ju przez kogo innego.
409
Na rysunku 12.1 wida nazw procesu, zdefiniowan przez nazw pakietu Java w pliku AndroidManifest.xml, oraz niepowtarzalny identyfikator uytkownika przydzielony do tego
pakietu. W przypadku aplikacji przegldarkowej nazwa pakietu wskazywana w pliku manifecie to com.android.browser (na rysunku 12.1 jako warto atrybutu Process).
Wszelkie zasoby utworzone przez ten proces lub pakiet zostan zabezpieczone za pomoc tego
linuksowego identyfikatora. Na tej licie wyszczeglniono rwnie poszczeglne skadniki pakietu. Przykadowymi skadnikami s aktywnoci, usugi oraz odbiorcy komunikatw.
411
...
>
413
Zwrmy uwag, w jaki sposb moemy uzyska odniesienie do kontekstu danego pakietu, na
przykad com.androidbook.samplepackage1. Obiekt targetContext widoczny na listingu
12.2 jest taki sam jak kontekst przekazywany docelowej aplikacji w momencie jej uruchomienia. Jak sama nazwa metody wskazuje (przedrostek create), kade wywoanie odsya nowy
obiekt kontekstu. Jednak w dokumentacji znalazo si zapewnienie, e mechanizm ten zosta
zaprojektowany w taki sposb, aby jak najmniej obcia system.
Projekty bibliotek
Podczas omawiania koncepcji wspdzielenia kodu i zasobw nasuwa si jedno zasadnicze
pytanie: czy pomocna okae si idea projektu bibliotek? Aby si tego dowiedzie, najpierw musimy zrozumie, czym s te projekty, jak s tworzone oraz w jaki sposb s uywane.
415
Zarwno projekt bibliotek, jak i gwny projekt mog uzyska dostp do zasobw
przechowywanych w tym pierwszym za pomoc odpowiednich plikw R.java.
Moemy posiada zduplikowane identyfikatory zasobw pomidzy projektem gwnym
a bibliotek. Identyfikatory zasobw w projekcie gwnym maj wyszy priorytet.
Jeeli chcemy rozrnia identyfikatory zasobw dwch projektw, moemy wprowadzi
odmienne przedrostki, na przykad lib_ dla zasobw projektu bibliotek.
Gwny projekt moe odnosi si do dowolnej liczby projektw bibliotek.
Moemy ustanowi pierwszestwo projektw bibliotek, aby si przekona, ktre
zasoby s waniejsze.
Takie skadniki projektu bibliotek jak aktywno naley zadeklarowa w pliku manifecie
gwnego projektu. Nazwa skadnika z pakietu bibliotek musi by cakowicie zgodna
z nazw pakietu bibliotek.
Nie ma potrzeby definiowania skadnikw w pliku manifecie projektu bibliotek,
chocia moe si to okaza przydatne do szybkiego rozpoznawania obsugiwanych
przez niego skadnikw.
Tworzenie projektu bibliotek rozpoczyna si od utworzenia standardowego projektu
Androida oraz ustawienia flagi Is Library w oknie waciwoci.
Moemy rwnie w oknie waciwoci projektu powiza gwny projekt z projektami
bibliotek.
Do wielu rnych gwnych projektw mona docza projekty bibliotek.
Funkcja projektw bibliotek zostaa wprowadzona w wersji 0.9.7 narzdzi ADT,
zestawie SDL w wersji 6 lub wyszej oraz od wersji 2.1 systemu Android.
W aktualnej wersji rodowiska jeden projekt bibliotek nie moe odnosi si do innego
projektu bibliotek, chocia w przyszych wersjach moe si pojawi taka moliwo.
Projekty bibliotek nie obsuguj plikw AIDL.
Projekt bibliotek nie obsuguje katalogu ze wspdzielonymi plikami dodatkowymi.
Przekonajmy si, do czego su projekty bibliotek, poprzez utworzenie jednego z nich oraz
projektu gwnego. Poniej prezentujemy cele naszego przykadowego projektu:
1. Utworzenie prostej aktywnoci w projekcie bibliotek.
2. Utworzenie menu dla aktywnoci z punktu 1. poprzez zdefiniowanie pewnych zasobw
menu.
3. Utworzenie w gwnym projekcie aktywnoci, ktra bdzie korzystaa z projektu
bibliotek.
4. Utworzenie aktywnoci w gwnym projekcie, wygenerowanym w punkcie 3.
5. Utworzenie menu dla gwnej aktywnoci z punktu 4.
6. Przywoanie aktywnoci projektu bibliotek za pomoc elementu menu gwnej aktywnoci.
Po utworzeniu obydwu projektw ujrzymy ekran gwnej aktywnoci (punkt 4.), zaprezentowany
na rysunku 12.2.
Po klikniciu elementu lib, widocznego w aktywnoci gwnego projektu, zostanie wywietlona
aktywno przedstawiona na rysunku 12.3, pochodzca z projektu bibliotek.
417
Menu widoczne w aktywnoci projektu bibliotek pochodz z zasobw tego projektu. Efektem
kliknicia poszczeglnych opcji menu jest wywietlony na ekranie komunikat o klikniciu danego elementu. Rozpocznijmy wiczenie od utworzenia projektu bibliotek.
Na listingu 12.4 widzimy plik ukadu graficznego obsugujcy powysz aktywno prosty
widok tekstowy stosowany do wywietlenia nazwy kliknitego elementu.
Listing 12.4. Przykadowy plik ukadu graficznego w projekcie bibliotek plik layout/lib_main.xml
<?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"
>
<TextView
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Tutaj pojawi si informacje debugowania. "
/>
</LinearLayout>
Listing 12.5 prezentuje nam zawarto pliku opcji menu, ktre s widoczne w aktywnoci projektu bibliotek na rysunku 12.3.
Listing 12.5. Plik menu projektu bibliotek plik menu/lib_main_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
419
Jak ju stwierdzilimy w punkcie, w ktrym zaprezentowalimy twierdzenia dotyczce projektw bibliotek, definicja aktywnoci w pliku manifecie projektu bibliotek pojawia si wycznie
w celach dokumentacyjnych oraz jest opcjonalna pod ktem mechanizmw dziaania kodu.
Po zapoznaniu si z plikami moemy utworzy standardowy projekt Androida. Po skonfigurowaniu projektu klikamy prawym przyciskiem myszy nazw projektu, a nastpnie menu
kontekstowe waciwoci, dziki czemu pojawi si okno dialogowe waciwoci, w ktrym moemy ustanowi projekt bibliotek. Omawiane okno dialogowe widzimy na rysunku 12.4 (w zalenoci od wersji zestawu SDK widoczne na rysunku wersje Androida mog by inne). Wystarczy zaznaczy opcj Is Library, aby przetworzy biecy projekt w projekt bibliotek.
W ten sposb zakoczylimy proces tworzenia projektu bibliotek. Dowiemy si teraz, w jaki sposb utworzy projekt aplikacji wykorzystujcy wygenerowany przed chwil projekt bibliotek.
421
Zwrmy uwag, e po utworzeniu tego pliku moe si pojawi bd kompilacji zwizany z odniesieniem do aktywnoci umieszczonej w projekcie bibliotek. Nie pozbdziemy si go, dopki
nie przeczytamy dalszej czci rozdziau i nie odkryjemy, w jaki sposb zdefiniowa projekt bibliotek jako obiekt zaleny od gwnego projektu.
Kod pliku ukadu graficznego obsugujcego powysz aktywno jest widoczny na listingu 12.8.
Listing 12.8. Ukad graficzny gwnego projektu plik layout/main.xml
<?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"
>
<TextView
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Tutaj pojawi si informacje debugowania"
/>
</LinearLayout>
Kod Java wystpujcy w aktywnoci gwnego projektu (listing 12.7) wykorzystuje element menu R.id.menu_library_activity do wywoania aktywnoci TestLibActivity. Poniej przedstawilimy fragment kodu z pliku Java (pokazanego wczeniej na listingu 12.7):
private void invokeLibActivity(int mid)
{
Intent intent = new Intent(this,TestLibActivity.class);
Do zakoczenia procesu tworzenia projektu potrzebujemy jeszcze pliku manifestu, zamieszczonego na listingu 12.10.
Listing 12.10. Plik manifest gwnego projektu AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.library.testlibraryapp"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon" android:label="Aplikacja
testujca bibl.">
<activity android:name=".TestAppActivity"
android:label="Aplikacja testujca bibl.">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=
"com.androidbook.library.testlibrary.TestLibActivity"
android:label="Aktywno testujca bibl."/>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
Zwrmy uwag, w jaki sposb w tym pliku manifecie gwnej aplikacji zdefiniowano aktywno TestLibActivity, pochodzc z projektu bibliotek. Podczas definiowania aktywnoci
zastosowalimy rwnie pen nazw pakietu. Zauwamy te, e nazwy pakietu dla projektu
bibliotek mog si rni od tych zawartych w gwnej aplikacji.
Po zapenieniu projektu tymi plikami naley otworzy okno dialogowe waciwoci projektu
(rysunek 12.5), aby zaznaczy, e nasz gwny projekt zaley od utworzonego wczeniej projektu
bibliotek.
423
Widzimy w tym oknie dialogowym przycisk Add. Moemy za jego pomoc doda odniesienie
do projektu bibliotek, pokazanego na rysunku 12.5. Inne czynnoci s niepotrzebne.
Po dodaniu projektu bibliotek ukazuje si on zazwyczaj w postaci dodatkowego wza drzewa
gwnej aplikacji (a take pozostaje jednoczenie osobnym projektem bibliotek). Ilustruje to
rysunek 12.6.
Najpierw zostaje wygenerowany jeden plik R.java w projekcie bibliotek, odpowiedzialny za zasoby przechowywane w tym projekcie. Ponadto system tworzy rwnie plik R.java przechowujcy informacje o zasobach znajdujcych si w gwnym projekcie. Mona si byo tego spodziewa dwm projektom odpowiadaj dwa pliki R.java.
Jednak, co ciekawe, Android generuje identyfikatory zasobw mieszczcych si w projekcie bibliotek rwnie w pliku R.java gwnej aplikacji. Oznacza to, e programista moe stosowa
skadni R.id. zwizan z identyfikatorami znajdujcymi si w pliku R.java, ktry stanowi cz
gwnej aplikacji (nie zapominajmy, e plik R.java zostaje automatycznie wygenerowany,
wic wartoci z listingu 12.11, takie jak 0x7f02000, mog by zupenie inne).
Listing 12.11. Ponownie zdefiniowane identyfikatory wspdzielonych zasobw, przechowywane
w pliku R.java gwnego projektu
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
public static final int robot=0x7f020001;
}
public static final class id {
public static final int menuGroup_Main=0x7f060001;
public
public
public
public
public
static
static
static
static
static
final
final
final
final
final
int
int
int
int
int
425
menu_clear=0x7f060002;
menu_library_activity=0x7f060005;
menu_testlib_1=0x7f060003;
menu_testlib_2=0x7f060004;
text1=0x7f060000;
}
public static final class layout {
public static final int lib_main=0x7f030000;
public static final int main=0x7f030001;
}
public static final class menu {
public static final int lib_main_menu=0x7f050000;
public static final int main_menu=0x7f050001;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}
Zauwamy, e w pliku R.java gwnej aplikacji zostay zdefiniowane rwnie zasoby rozpoczynajce si od przedrostka lib_. Oznacza to, e projekt bibliotek bdzie posiada swoje stae dla
zasobw lib_, a gwny projekt bdzie posiada inne stae dla tych samych zasobw.
Obydwa projekty mog si odnosi do tego samego zasobu za pomoc skadni
R.jakis
-identyfikator. Warto tej staej moe by identyczna, jednak identyfikator tego zasobu b-
dzie dostpny w obydwu przestrzeniach nazw Java: w przestrzeni nazw projektu bibliotek
oraz w przestrzeni nazw gwnej aplikacji.
Uwaajmy take na nazwy kontrolek menu: lib_main_menu oraz main_menu. Mogoby si to
okaza kopotliwe, gdyby w aplikacji znalazy si dwa menu wypenione rnymi elementami,
posiadajce jednak tak sam nazw zasobu. Reasumujc, zasoby zostaj zebrane i udostpnione
w jednym miejscu przeznaczonym dla gwnej aplikacji. Musimy by szczeglnie ostroni w przypadku zasobw zlokalizowanych na poziomie pliku, na przykad kontrolek menu lub ukadw
graficznych, a take identyfikatorw generowanych z tych obiektw dla wewntrznych elementw.
Skoro wiemy, czym s projekty bibliotek, czy potrafimy ju odpowiedzie na jakiekolwiek
wczeniej postawione pytanie na temat wspdzielonych danych?
Jak wida, projekty bibliotek s konstruktami czasu kompilacji. Jasne staje si, e wszelkie zasoby
nalece do tego projektu zostaj wchonite i przyczone do gwnego projektu. Nie moemy
zada pytania o wspdzielenie w czasie dziaania aplikacji, poniewa istnieje tylko jeden plik
pakietu zawierajcy nazw gwnego pakietu. Jedn z czsto wymienianych propozycji jest
moliwo utworzenia wersji darmowych i patnych aplikacji, wspdzielcych jeden projekt bibliotek.
Odnoniki
Dziki poniszym odnonikom Czytelnik atwiej zrozumie koncepcje zawarte w tym rozdziale:
http://developer.android.com/guide/publishing/app-signing.html bardzo przydatne
informacje na temat podpisywania plikw .apk.
Podsumowanie
Rozdzia ten powicilimy zagadnieniom dotyczcym pracy z pakietami i procesami, wspdzielenia kodu i danych pomidzy pakietami, a take tworzenia projektw bibliotek Androida.
Dowiedzielimy si, e proces podpisywania odgrywa istotn rol w zabezpieczeniach, zwaszcza na etapie przydzielania uprawnie pakietom.
Niniejszy rozdzia stanowi wprowadzenie do nastpnego, w ktrym przeanalizujemy skadniki
przechowywane w procesie pakietu oraz (przede wszystkim) przebiegajce w jego gwnym wtku.
Dowiemy si, w jaki sposb mona optymalnie dostosowa gwny wtek za pomoc procedur
obsugi oraz wtkw podrzdnych, co pozwala na zapewnienie pynnego dziaania aplikacji.
R OZDZIA
13
Analiza procedur obsugi
W rozdziale 12. stwierdzilimy, e kady pakiet jest przetwarzany w osobnym procesie. Teraz zajmiemy si organizacj wtkw we wntrzu procesu. W ten sposb
odpowiemy sobie na pytanie, do czego s nam potrzebne procedury obsugi.
Wiksza cz kodu aplikacji w Androidzie jest przetwarzana w kontekcie takiego
skadnika, jak aktywno lub usuga. Zastanowimy si, w jaki sposb te skadniki
aplikacji oddziauj z wtkami. Przez wikszo czasu dziaania aplikacji uruchomiony jest tylko jeden wtek wewntrz procesu, znany jako wtek gwny. Wyjanimy skutki wspdzielenia takiego gwnego wtku przez rne skadniki. Przede
wszystkim prowadzi to do wywietlania komunikatw ANR (ang. Application Not
Responding aplikacja nie odpowiada; podkrelamy tylko, e A jest skrtem od
aplikacja, a nie od annoying, czyli irytujca). Pokaemy, w jaki sposb moemy
stosowa procedury obsugi, komunikaty oraz wtki do uniezalenienia si od gwnego wtku w przypadku koniecznoci uruchomienia operacji trwajcych duszy czas.
Rozpoczniemy ten rozdzia od przyjrzenia si skadnikom aplikacji oraz kontekstowi
wtku, w ktrym s one przetwarzane.
Nie liczc pewnych wyjtkw, Android wykorzystuje ten sam wtek do przetwarzania (lub
przechowywania) kodu z tych skadnikw. Mamy tu do czynienia z gwnym wtkiem aplikacji.
Wywoanie tych skadnikw moe by synchroniczne, na przykad w przypadku dania danych
od dostawcy treci, lub opnione za pomoc kolejkowania wiadomoci, jak choby podczas
przywoywania funkcji za pomoc uruchomienia usugi.
Na rysunku 13.1 przedstawiono zwizki pomidzy wtkami a tymi czterema skadnikami. Celem
tego diagramu jest ukazanie, w jaki sposb wtki kr po strukturze Androida oraz jej skadnikach. Zagadnienia z tym zwizane omwilimy w kolejnych kilku podrozdziaach.
429
431
Procedury obsugi
Procedura obsugi (ang. handler) jest mechanizmem sucym do umieszczania komunikatu
w gwnej kolejce (dokadniej mwic, w kolejce doczonej do wtku, dla ktrego ta procedura
zostaa utworzona), dziki czemu komunikat ten zostanie pniej przetworzony przez gwny
wtek. Taki komunikat zawiera wewntrzne odniesienie wskazujce na procedur obsugi, ktra
go umiecia.
Gdy gwny wtek przechodzi do przetwarzania tego komunikatu, przywouje odpowiedzialn
za jego umieszczenie procedur obsugi za pomoc metody zwrotnej na obiekcie tej procedury.
Metoda ta nosi nazw handleMessage. Na rysunku 13.2 zostay ukazane powizania pomidzy
procedurami usug, komunikatami i gwnym wtkiem.
Podczas omawiania procedur obsugi przydatny okae si schemat pokazany na rysunku 13.2,
na ktrym przedstawiono podstawowe wsppracujce ze sob elementy. Tymi elementami s:
gwny wtek,
kolejka gwnego wtku,
procedura obsugi,
komunikat.
Taka konstrukcja speni nasze wymogi w omawianym przypadku. Jednak w rzeczywistoci w ten
sposb wstrzymujemy gwny wtek i gwarantujemy sobie wystpienie komunikatu ANR.
433
435
Zmienna this odnosi si do instancji obiektu Handler. Jak sama nazwa wskazuje, metoda ta
nie powoduje utworzenia nowego komunikatu, lecz pobiera go z globalnej puli komunikatw.
Nieco pniej, ju po przetwarzaniu komunikatu, bdzie on ponownie wykorzystywany. Na listingu 13.5 zaprezentowalimy rne odmiany metody obtainMessage().
Listing 13.5. Tworzenie komunikatu za pomoc procedury obsugi
obtainMessage();
obtainMessage(int
obtainMessage(int
obtainMessage(int
obtainMessage(int
what);
what, Object object);
what, int arg1, int arg2)
what, int arg1, int arg2, Object obj);
Kada odmiana tej metody ustanawia odpowiednie pola w obiekcie komunikatu. Istniej pewne
ograniczenia zwizane z argumentem Object object, czce si z przekraczaniem granicy
procesu przez komunikat. W takich przypadkach musi to by obiekt typu parcelable. O wiele
bezpieczniej i wygodniej jest stosowa jawnie metod setData() wobec obiektu komunikatu,
do czego wymagany jest typ bundle. Na listingu 13.4 zastosowalimy wanie metod setData().
Zachcamy rwnie do korzystania z argumentw arg1 oraz arg2, jeeli chcemy przekazywa
proste wskaniki, ktre mog by dostosowane do wartoci typu int.
Argument what pozwala na usunicie komunikatu z kolejki lub wysanie zapytania o obecno
komunikatw tego typu w kolejce. Wicej szczegw poznamy po zapoznaniu si z operacjami
klasy Handler. W podrozdziale Odnoniki umieszczonym na kocu tego rozdziau znajdziemy adres URL dokumentacji klasy Handler.
437
//Wystpienie wtku.
Thread workerThread = null;
//Wtek ju tu jest.
if (workerThread.getState() != Thread.State.TERMINATED)
{
Log.d(tag, "watek jest nowy albo juz istniejacy, ale niezakonczony");
}
else
{
Log.d(tag, "watek jest prawdopodobnie zakonczony. uruchamianie");
439
informStart();
for(int i=1;i <= 5;i++)
{
Na listingu 13.7 widzimy dwie istotne rzeczy. W metodzie run() wstrzymujemy dziaanie wtku
na 1 sekund i wywoujemy metody informujce gwny wtek o stanie postpw wtku roboczego: czy jest na pocztku, w rodku, czy pod koniec procesu przetwarzania.
Doczylimy rwnie wywoanie metody
zidentyfikowanie wtku.
Utils.logThreadSignature(),
pozwalajcej na
Jednak w standardowej aplikacji zamiast metody sleep() powyszy kod wywoywaby jak
przydatn funkcj, aby dziaaa, dopki bdzie potrzebna. Moemy uzna t metod za symulowanie jakiej czci operacji, ktra zajmuje dokadnie tyle samo sekund.
Kod zawarty w tej klasie jest oczywisty. Kiedy procedura obsugi otrzymuje metod handleMessage(), informuje nadrzdn aktywno, e wtek roboczy przesa komunikat o stanie
za pomoc metody appendText(). Z kolei nadrzdna aktywno po otrzymaniu komunikatu
wykonuje odpowiedni operacj. W naszym przykadzie zostaje jedynie wywietlona wiadomo na ekranie aktywnoci.
Do tej pory, posugujc si odpowiednimi przykadami, przedstawilimy do istotne zagadnienia:
Za pomoc procedury DeferWorkHandler pokazalimy, e gwny wtek moe
okrela moment przetworzenia komunikatu (czy komunikatw), moe te opni
jego (ich) przetworzenie. Ta sama technika moe by wykorzystywana do powtarzania
tej samej czynnoci bez koniecznoci korzystania z czasomierza lub menedera alarmw.
Za pomoc metod ReportStatusHandler i WorkerThread udowodnilimy, e moemy
rozpocz osobny wtek roboczy i umoliwi mu komunikacj z interfejsem uytkownika
za pomoc procedury obsugi.
441
Warto rwnie zwrci uwag na rne stany wtku, aby dobrze zrozumie jego zachowanie.
Wtek moe znajdowa si w jednym z nastpujcych stanw:
New thread zosta utworzony (alive=false);
Runnable zosta uruchomiony (alive=true);
Not runnable wstrzymany, zawieszony, oczekujcy, wywoany lub zablokowany
na wejciu-wyjciu (alive=true);
Dead gdy zostaje wywoana metoda stop() lub nastpi wyjcie z metody run()
(alive=false).
Metoda isAlive() w wtku informuje nas, czy wtek zosta uruchomiony, ale nie zatrzymany.
Oznacza to, e wtek moe znajdowa si w stanie runnable lub not-runnable. Jeeli zostanie
zwrcona warto false, to mamy do czynienia z nowym albo z zakoczonym wtkiem.
W trakcie pracy z wtkami powinnimy pamita o ich stanach.
443
return true;
}
if (item.getItemId() == R.id.menu_test_defered_handler)
{
this.testDeferedHandler();
return true;
}
return true;
}
private TextView getTextView(){
return (TextView)this.findViewById(R.id.text1);
}
public void appendText(String abc){
TextView tv = getTextView();
tv.setText(tv.getText() + "\n" + abc);
}
private void appendMenuItemText(MenuItem menuItem){
String title = menuItem.getTitle().toString();
TextView tv = getTextView();
tv.setText(tv.getText() + "\n" + title);
}
private void emptyText(){
TextView tv = getTextView();
tv.setText("");
}
private DeferWorkHandler th = null;
private void testDeferedHandler()
{
if (th == null)
{
th = new DeferWorkHandler(this);
this.appendText("Tworzenie nowej procedury obsugi");
}
this.appendText(
"Rozpoczcie wykonywania opnionej pracy poprzez wysyanie komunikatw");
th.doDeferredWork();
}
Handler statusBackHandler = null;
Thread workerThread = null;
private void testThread()
{
if (statusBackHandler == null)
{
statusBackHandler = new ReportStatusHandler(this);
workerThread =
new Thread(
new WorkerThreadRunnable(statusBackHandler));
}
if (workerThread.getState() != Thread.State.TERMINATED)
{
Log.d(tag, "watek jest nowy lub istniejacy, ale niezakonczony");
}
else
{
445
Plik menu
Wykorzystywany na potrzeby tego przykadu kod pliku menu, menu/main_menu.xml, zosta
umieszczony na listingu 13.11. Jest to menu obsugujce aktywno zdefiniowan na listingu
13.9. Jak wynika z aktywnoci, menu to zawiera trzy elementy. Jeden suy do czyszczenia
widoku tekstowego podczas pracy z opcjami menu. Dalej mamy dwa gwne elementy menu:
menu_test_defered_handler przywouje procedur obsugi DeferWorkHandler, natomiast
menu_test_thread tworzy wtek roboczy i wykorzystuje procedur ReportStatusHandler.
Listing 13.11. Elementy menu wywoujce kod procedury obsugi i wtku pobocznego
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Plik manifest
Listing 13.12 prezentuje plik manifest (AndroidManifest.xml), ktry zamyka list plikw rdowych. Jego zawarto jest bardzo nieskomplikowana, gdy wskazuje tylko pojedyncz aktywno, widoczn na listingu 13.9 (gwna aktywno sterujca).
Listing 13.12. Plik AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.handlers"
android:versionCode="1"
Plik manifest zawiera odniesienie do ikony aplikacji. Moemy wykorzysta plik ZIP projektu,
do ktrego odnonik znajduje si na kocu rozdziau, albo moemy umieci inn ikon z dowolnego innego projektu.
447
Gdy aktywno przechodzi do stanu penej widocznoci, zostaje wywoana metoda onResume.
Jeeli przechodzi ze stanu zupenej niewidocznoci, najpierw zostaje wywoana metoda onStart,
a nastpnie dopiero onResume (albo onStop, jeli aktywno ma znowu sta si niewidoczna).
Pomidzy wywoaniami metod onResume i onPause aktywno jest w peni widoczna.
Chocia aplikacja moe by czciowo lub cakowicie niewidoczna, kolejka komunikatw bdzie
niezmiennie aktywna, tak samo jak wtek roboczy. Zauwaymy to, jeli bdziemy obserwowa
metody cyklu ycia aktywnoci pokazane na listingu 13.9. Stwierdzimy, e komunikaty z wtku
roboczego oraz z procedury obsugi s cigle aktywne po wywoaniu metod onPause i onStop.
Moemy przetestowa t hipotez, klikajc przycisk ekranu startowego w trakcie przebywania
w aktywnoci. W ten sposb przeniesiemy aktywno do ta i wywoamy metody onPause,
onStop, a moe nawet onDestroy. Przez cay czas bd generowane komunikaty, dopki nie
zostanie wywoana metoda onDestroy (pod warunkiem e wysalimy tyle komunikatw).
Jeeli proces nie jest aktywny w czasie dania wywoania aktywnoci, zostanie ona uruchomiona i zwrcona na pierwszy plan. W przypadku ograniczenia pamici lub wtedy, gdy aktywno jest ukryta i nic si nie dzieje w danym procesie, zostanie on usunity przez system.
Najwaniejsza jest wiadomo, e jeeli aktywno zostanie zatrzymana z ktrego
z wymienionych wyej powodw, nie zostanie pniej automatycznie przywrcona.
Uytkownik musi jawnie przywoa aktywno albo poprzez kliknicie elementu
menu, albo w jaki inny, poredni sposb, na przykad uruchomienie aktywnoci, ktrej
dziaanie przywoa t zatrzyman aktywno. Jedynym przypadkiem, w ktrym aktywno
zostanie zatrzymana i ponownie uruchomiona, jest zmiana konfiguracji urzdzenia
(na przykad zmiana orientacji ekranu). Moemy sobie wyobrazi, e takie obracanie
telefonu moe wystpowa do czsto.
449
jakiego szczeglnego cyklu ycia. Od momentu uruchomienia istniej, dopki trwa proces.
Nawet jeli wykazuj synchroniczno wobec zewntrznych klientw, nie bd przetwarzani
w gwnym wtku, lecz w puli wtkw przechowujcego ich procesu, podobnie jak ma to miejsce w przypadku zwizku klient sieciowy serwer sieciowy. Wtek kliencki bdzie oczekiwa,
dopki nie nadejdzie wywoanie zwrotne. Jeeli nie ma adnych klientw, proces zostaje odzyskany zgodnie z reguami odzyskiwania, w zalenoci od tego, jakie inne skadniki zostay zdefiniowane oraz aktywnie dziaaj w tym procesie.
Odnoniki
W czasie zapoznawania si z tematami omawianymi w tym rozdziale Czytelnik moe zechcie
zdoby wicej informacji. S one dostpne pod poniszymi adresami URL; przy kadym
odnoniku umiecilimy rwnie krtk notatk na temat prezentowanych zasobw.
http://developer.android.com/reference/android/os/Handler.html znajdziemy tu
odniesienie do interfejsu API procedur obsugi. Zostay tu omwione metody
pozwalajce na konstruowanie procedur obsugi, uzyskiwanie komunikatu,
przesonicie metod handleMessage() oraz sendMessage() i tak dalej.
http://developer.android.com/reference/android/os/Message.html pod tym adresem
zdobdziemy informacje na temat interfejsu API komunikatw. Chocia interfejs ten jest
stosunkowo rzadko stosowany, poniewa rwnowane funkcje dostpne s w interfejsie
API procedury obsugi, warto pozna fundamenty obiektu Message i dowiedzie si
co nieco na temat tego interfejsu. Zalecamy zapoznanie si z wiadomociami
umieszczonymi pod tym adresem.
http://developer.android.com/guide/topics/fundamentals.html#lcycles szczegowe
dane na temat cyklw ycia. Zosta tu pooony nacisk przede wszystkim na cykle
ycia aktywnoci i usug, wspomniano take o cyklach ycia odbiorcw komunikatw.
Waciwie nie znajdziemy tu informacji na temat dostawcw treci.
http://www.science.uva.nl/ict/ossdocs/java/tutorial/java/threads/states.html bardzo
merytoryczny i niezbdny do przeczytania artyku wprowadzajcy w tematyk wtkw.
http://www.netmite.com/android/mydroid/1.6/frameworks/base/core/java/android/ap
p/IntentService.java znakomity przykad wykorzystania procedur obsugi przez
bazowy kod systemu Android w implementacji klasy IntentService. Niniejszy
adres jest odniesieniem do kodu rdowego zawartego w pliku IntentService.java.
Bardzo zalecamy, by po przeczytaniu tego rozdziau, w celu utrwalenia wiadomoci
dotyczcych wtkw, przejrze kod rdowy klasy IntentService.
http://www.androidbook.com/item/3514 notatki jednego z autorw dotyczce
usug o duszym czasie trwania.
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
utworzonych na potrzeby ksiki. Waciwy plik znajdziesz w katalogu o nazwie
ProAndroid3_R13_ProceduryObsugi.
Podsumowanie
W tym rozdziale przeanalizowalimy rne skadniki procesu Androida, a take sposb koordynowania ich dziaania przez gwny wtek. Pokazalimy, w jaki sposb procedury obsugi
i wtki mog by wykorzystywane do poszerzania zasigu gwnego wtku, a take dlaczego
gwny wtek musi powraca do dziaania w przecigu piciu sekund, zanim pojawi si komunikat ANR. Taka sama zasada dotyczy odbiorcw komunikatw, przy czym w ich przypadku
rama czasowa zostaje zwikszona do dziesiciu sekund.
451
Omwilimy cykle ycia poszczeglnych skadnikw oraz ich wpyw na wtek gwny i wtki
poboczne. Wiedza ta jest niezbdna do zrozumienia zawioci tych skadnikw oraz czynnoci
potrzebnych do wykonywania dugoterminowych operacji.
Nastpny rozdzia jest powicony pracy z odbiorcami komunikatw oraz przeprowadzaniu
dugoterminowych dziaa. Informacje zawarte w tym rozdziale pomog nam zrozumie
pojcia przedstawione w nastpnym.
R OZDZIA
14
Odbiorcy komunikatw
i usugi dugoterminowe
W poprzednich rozdziaach zajmowalimy si w przewaajcej czci aktywnociami, dostawcami treci i usugami. Niezbyt dokadnie przedstawilimy zagadnienia
dotyczce odbiorcw komunikatw, dlatego teraz przyjrzymy im si uwaniej.
Zaprezentujemy najpierw sposb przywoania prostego odbiorcy komunikatw,
a nastpnie rozszerzymy ten mechanizm na procedur wywoania wielu takich
obiektw. Pokaemy rwnie, w jaki sposb odbiorcy komunikatw mog si znajdowa w zewntrznych procesach. Zademonstrujemy take sposb, w jaki odbiorcy
komunikatw wysyaj powiadomienia poprzez meneder powiadomie.
Przeanalizujemy dziesiciosekundowy limit, w ktrym musi si zmieci odbiorca
komunikatw, aby nie zostaa wywietlona informacja ANR (aplikacja nie odpowiada), a take sposoby pominicia tego ograniczenia czasowego. Utworzymy specjaln struktur, pozwalajc na obserwowanie dugoterminowej usugi, bdcej
specjaln wersj odbiorcy komunikatw, na kocu natomiast omwimy blokady
przechodzenia urzdzenia w stan zatrzymania (ang. wakelock) w kontekcie dugoterminowych usug.
Rozpocznijmy od obszernej analizy odbiorcw komunikatw na przykadzie utworzenia prostego obiektu tego typu.
Odbiorcy komunikatw
W rozdziale 13. zdefiniowalimy odbiorc komunikatw (ang. broadcast receiver)
jako jeden ze skadnikw procesu (innymi skadnikami s: aktywno, usuga oraz
dostawca treci). Jak sama nazwa wskazuje, zadaniem odbiorcy komunikatw jest
odpowiadanie na wiadomoci wysyane przez klienta. Sam taki komunikat jest intencj, ktra moe by odbierana przez wielu odbiorcw.
Taki skadnik, jak aktywno lub usuga (albo inny komponent implementujcy klas
Context), prbujcy wysa zdarzenie (intencj), korzysta z metody sendBroadcast()
dostpnej w klasie Context. Argumentem tej metody jest wysyana intencja.
Wysyanie komunikatu
Listing 14.1 przedstawia przykadowy kod bdcy czci klasy aktywnoci, sucy do przesyania komunikatu. Za pomoc tego kodu tworzymy intencj zawierajc unikatowe, specyficzne dla niej dziaanie, nastpnie wstawiamy do niej dodatkowy komunikat oraz wywoujemy
metod sendBroadcast(). Wstawienie dodatkowego komunikatu jest opcjonaln czynnoci;
czsto samo otrzymanie intencji cakowicie wystarczy odbiorcy, a dodatkowy komunikat
okazuje si zbdny.
Listing 14.1. Nadawanie intencji
private void testSendBroadcast(Activity activity)
{
455
Log.d(tag, message);
}
}
Receiver
Podobnie jak w przypadku pozostaych rodzajw skadnikw, obiekt receiver jest wzem
potomnym elementu application. To nam wystarczy do przetestowania odbiorcy. W nastpnym punkcie zamieszczamy list plikw wymaganych do utworzenia projektu testowego.
Zanim zaczniemy z zapaem kopiowa (albo, co gorsza, przepisywa) kody zamieszczone na listingach, pamitajmy, e w podrozdziale Odnoniki zosta umieszczony adres URL, pod ktrym znajdziemy list wszystkich projektw moemy je pobra na dysk i zaimportowa do
rodowiska Eclipse.
//Wysya komunikat
457
Peny kod rdowy pliku TestReceiver.java znajdziemy na listingu 14.2, natomiast pliku
Utils.java na listingu 14.3.
459
Po utworzeniu powyszego kodu moemy doda tego odbiorc do pliku manifestu (listing 14.8),
zgodnie z ponisz definicj (listing 14.10).
Listing 14.10. Definicja odbiorcy TestReceiver2 w pliku manifecie
<receiver android:name=".TestReceiver2">
<intent-filter>
<action android:name="com.androidbook.intents.testbc"/>
</intent-filter>
</receiver>
Jeeli teraz ponownie przywoamy element menu widoczny na rysunku 14.1, w oknie LogCat
ujrzymy komunikat Witaj, wiecie! pochodzcy z obydwu odbiorcw.
Stwierdzimy rwnie, e odbiorcy s wywoywani zgodnie z kolejnoci ich definiowania w pliku manifecie. Moemy take sprawdzi, w jakim wtku ci odbiorcy s uruchomieni. Wywoanie
metody Utils.logThreadSignature(tag) spowoduje wywietlenie sygnatury dziaajcego
wtku. Okae si, e w istocie odbiorcy s przetwarzani w gwnym wtku.
Zauwaymy ponadto, e komunikaty dziennika umieszczone przed wywoaniem metody
sendBroadcast() w metodzie testSendBroadcast() oraz po jej wywoaniu (listing 14.5) bd
wywietlane w oknie dziennika przed wiadomociami z odbiorcw komunikatw oraz posiadaj t sam sygnatur wtku.
Mamy wic dowd, e gwny wtek pracuje przez cay czas i po wyjciu z kolejki wiadomoci
przetwarza odbiorcw komunikatw. Wida wyranie, e metoda sendBroadcast() jest asynchroniczn wiadomoci, umoliwiajc gwnemu wtkowi powrt do kolejki komunikatw.
461
Jeeli chcemy otrzyma mocniejsze dowody, moemy przetrzyma gwny wtek troch duej,
tak aby znaczniki czasowe zostay wyranie zaznaczone. Utwrzmy kolejnego odbiorc komunikatw, ktry tym razem bdzie nieco opnia wtek gwny za pomoc wstrzymania. Kod
rdowy takiego opniajcego odbiorcy zosta zaprezentowany na listingu 14.11.
Listing 14.11. Odbiorca generujcy opnienie czasowe
/*
* Odbiorca ten zosta wprowadzony w celu ukazania,
* w jaki sposb gwny wtek ustala kolejno przetwarzania odbiorcw komunikatw
*
* Przykad ten pomaga odpowiedzie na takie pytania, jak:
* 1. Czy s one przywoywane w kolejnoci ich definiowania w pliku manifecie?
* 2. Czy s one przetwarzane jeden po drugim, czy te rwnolegle?
*
* Widzimy, e za pomoc opnienia czasowego wprowadzamy gwny wtek
* w stan wstrzymania na tyle wanie sekund. Wida to
* w pliku wynikowym Log.d
*/
public class TestTimeDelayReceiver extends BroadcastReceiver
{
private static final String tag = "TestTimeDelayReceiver";
@Override
public void onReceive(Context context, Intent intent)
{
Utils.logThreadSignature(tag);
Log.d(tag, "intencja=" + intent);
Log.d(tag, "przechodzi w stan wstrzymania na 2 sekundy");
Utils.sleepForInSecs(2);
Log.d(tag, "wybudzanie");
String message = intent.getStringExtra("message");
Log.d(tag, message);
}
}
Jeeli teraz wstawimy definicj tego odbiorcy pomidzy definicje dwch poprzednio utworzonych, zauwaymy, e gwny wtek jest przegldany przez podstawow logik oraz logik odbiorcy komunikatu. Obserwujc okno LogCat, stwierdzimy, e najpierw jest przetwarzany
pierwszy odbiorca. Nastpnie zostaje przywoany drugi odbiorca, za gwny wtek zostaje wtedy
wstrzymany na dwie sekundy i przechodzi do trzeciego odbiorcy. Poza tym zauwaymy, e
wszyscy odbiorcy bd przywoywani dopiero po powrocie metody sendBroadcast().
Aby przetestowa odbiorc komunikatw z opnieniem czasowym, dodajmy jego definicj
z listingu 14.12 do pliku manifestu widocznego na listingu 14.8.
Listing 14.12. Definicja odbiorcy treci z opnieniem czasowym w pliku manifecie
<receiver android:name=".TestTimeDelayReceiver">
<intent-filter>
<action android:name="com.androidbook.intents.testbc"/>
</intent-filter>
</receiver>
Nie ma tutaj niczego wyjtkowego, utworzylimy jedynie standardowego odbiorc. Plik manifest
rejestrujcy go jest widoczny na listingu 14.14.
Listing 14.14. Plik AndroidManifest.xml, w ktrym zosta umieszczony jedynie odbiorca komunikatw
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.salbcr"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="Niezaleny odbiorca komunikatw">
<receiver android:name=".StandaloneReceiver">
<intent-filter>
<action android:name="com.androidbook.intents.testbc"/>
</intent-filter>
</receiver>
463
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
Wraz z omwionym przy okazji poprzedniego projektu plikiem Utils.java moemy utworzy
i wdroy nowy projekt systemu Android. Jeeli teraz przejdziemy do ekranu widocznego
na rysunku 14.1 i klikniemy element menu Transmituj, przekonamy si, e nasz niezaleny
odbiorca wywietli komunikat w oknie dziennika podobnie do pozostaych odbiorcw.
Monitorowanie powiadomie
za pomoc menedera powiadomie
Ikony powiadomie w Androidzie s wywietlane jako alerty umieszczone w obszarze powiadomie. Obszar powiadomie jest wskim paskiem mieszczcym si w szczytowej czci wywietlacza. Zosta pokazany na rysunku 14.2. Wygld oraz pooenie obszaru powiadomie moe
si rni w zalenoci od tego, czy urzdzenie jest tabletem, czy telefonem, a take czasami zaley
od wersji Androida.
Po wprowadzeniu powiadomienia bdzie ono wywietlane w postaci ikony w obszarze zaprezentowanym na rysunku 14.2. Ikona ta zostaa ukazana na rysunku 14.3.
Wysyanie powiadomienia
Zaczynajmy. Proces wysyania powiadomienia skada si z trzech nastpujcych etapw:
1. Utworzenie odpowiedniego powiadomienia.
2. Uzyskanie dostpu do menedera powiadomie.
3. Przesanie powiadomienia do menedera powiadomie.
465
//Wysya powiadomienie.
//Pierwszym argumentem jest unikatowy identyfikator tego powiadomienia.
//Identyfikator ten pozwala na pniejsze anulowanie powiadomienia.
nm.notify(1, notification);
}
}
467
Aby przetworzy zdalny widok na widok treci powiadomienia, naley wykona nastpujce
czynnoci:
1. Utwrz plik ukadu graficznego.
2. Utwrz obiekt RemoteViews za pomoc nazwy pakietu oraz identyfikatora pliku
ukadu graficznego.
3. Wywoaj wobec tego obiektu metody ustanawiajce tekst, ikony itd.
4. Wywoaj metod setContentView() na obiekcie powiadomienia przed jego
wysaniem do menedera powiadomie.
Nie zapominajmy, e w przypadku wersji 2.2 Androida zdalny widok posiada ograniczony zestaw kontrolek:
FrameLayout,
LinearLayout,
RelativeLayout,
AnalogClock,
Button,
Chronometer,
ImageButton,
ImageView,
ProgressBar,
TextView.
W rozdziale 22. dokadnie omwilimy proces tworzenia takich zdalnych widokw, poniewa
widety ekranu startowego zasadniczo s widokami tego typu. Z kolei rozdzia 31. zawiera list
obiektw RemoteViews zaktualizowan dla wersji 2.3 i 3.0 Androida.
Kod z listingu 14.15 generuje powiadomienie i wykorzystuje metod setLatestEventInfo()
ustanowienia niejawnego widoku treci (za pomoc tytuu i tekstu) oraz uruchamianej intencji
(w naszym przypadku jest to intencja przegldarki).
Znajc ju te fakty, zastanwmy si, w jaki sposb mona doprowadzi do wykonywania duszych operacji w odpowiedzi na nadany komunikat.
469
Klasa IntentService
Aby umoliwi tworzenie usug, ktre nie bd zatrzymyway gwnego wtku, system Android
zapewnia implementacj lokalnej usugi zwanej IntentService. Usuga ta przenosi operacje do
wtku roboczego, dziki czemu gwny wtek zostaje odciony po okreleniu zakresu zada
wtku pobocznego. Po wywoaniu metody startService() wobec usugi IntentService klasa
ta zakolejkuje danie w wtku pobocznym za pomoc zaptlenia oraz procedury obsugi,
dziki czemu rzeczywist prac wykonuje metoda wywodzca si z tej usugi.
Dokumentacja interfejsu API definiuje klas IntentService w nastpujcy sposb:
IntentService jest bazow klas usug przetwarzajcych zapytania asynchroniczne
(wyraane w postaci intencji) wysyane na danie. Klienty wysyaj zapytania za pomoc
wywoa metody startService(Intent). W razie potrzeby zostaje uruchomiona
usuga, ktra z kolei przetwarza kad intencj w wtku roboczym. Gdy ten wtek
zakoczy dziaanie, usuga zostaje zatrzymana. Taki wzorzec procesora kolejki operacji
jest powszechnie stosowany do odcienia gwnego wtku aplikacji z rnorodnych
zada. Klasa IntentService zostaa stworzona w celu uproszczenia tego wzorca.
Zapewnia obsug wykorzystywanych w tym przypadku mechanizmw. Aby z niej
skorzysta, naley rozszerzy klas IntentService i zaimplementowa metod
onHandleIntent(). Klasa ta bdzie otrzymywaa intencje, uruchomi wtek roboczy
oraz w odpowiednim momencie zatrzyma usug. Wszystkie dania s przetwarzane
w pojedynczym wtku roboczym mog one trwa dowolnie dugo (i nie bd blokowa
gwnej ptli aplikacji), ale w danym momencie bdzie obsugiwane tylko jedno danie.
Po utworzeniu usugi tego typu moemy j zarejestrowa w pliku manifecie i wykorzysta kod
klienta do przywoania usugi w postaci context.startService(new Intent(MyService.
class)). Tego typu przywoanie poskutkuje wywoaniem metody onHandleIntent(), widocznej na listingu 14.16.
Warto zauway, e metoda logThreadSignature() wywietli identyfikator wtku roboczego,
a nie gwnego (pamitajmy, e mamy tu do czynienia wycznie z pseudokodem; wkrtce jednak zaprezentujemy rzeczywisty przykad).
471
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread =
new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
}
473
Jak wida, moemy rozszerzy t now, dugoterminow klas usugi (podobnie jak klasy
IntentService oraz WakefulIntentService) oraz przesoni pojedyncz metod, a take nic
nie zmienia lub wprowadza bardzo niewielkie zmiany w odbiorcy komunikatw. Wszystkie
zadania zostan wykonane w wtku roboczym (dziki klasie IntentService) bez obawy o zablokowanie gwnego wtku.
/*
* Wykonuje dugoterminowe operacje wewntrz tej metody.
* S one przeprowadzane w oddzielnym wtku.
*/
@Override
protected void handleBroadcastIntent(Intent broadcastIntent)
{
Utils.logThreadSignature(tag);
Log.d(tag,"Wstrzymanie na 60 sekund");
Utils.sleepForInSecs(60);
String message =
broadcastIntent.getStringExtra("message");
Log.d(tag,"Praca zakonczona");
Log.d(tag,message);
}
}
Jak wida, powyszy kod skutecznie symuluje prac wykonywan przez 60 sekund i jednoczenie nie powoduje wywietlenia komunikatu ANR. By moe Czytelnik si teraz zastanawia,
dlaczego nie moemy skompilowa tej klasy i dlaczego nie pokazalimy jeszcze implementacji
abstrakcyjnej klasy dugoterminowej usugi. Warto jednak poczeka i najpierw dokadnie zrozumie wszystkie elementy tego przykadu. W trakcie objaniania zaprezentujemy kod implementacji wszystkich niezbdnych klas. Poza tym w dalszej czci rozdziau umiecilimy dokadne
informacje dotyczce kompilacji tego przykadu, a na jego kocu zamiecilimy adres URL, z ktrego moemy pobra cay projekt.
475
/*
* Przesaniamy t metod, aby powrci do
* obiektu klasy nalecego do
* nietrwaej usugi.
*/
public abstract Class getLRSClass();
}
Po utworzeniu tej abstrakcyjnej klasy potrzebny bdzie jeszcze odbiorca cile wsppracujcy
z dugoterminow (60-sekundow) usug, widoczn na listingu 14.16. Kod takiego typu
odbiorcy jest widoczny na listingu 14.22.
Listing 14.22. Przykadowy dugoterminowy odbiorca komunikatw, nazwany Test60SecBCR
public class Test60SecBCR
extends ALongRunningReceiver
{
@Override
public Class getLRSClass()
{
Utils.logThreadSignature("Test60SecBCR");
return Test60SecBCRService.class;
}
}
Podobnie jak abstrakcja usugi z listingw 14.19 i 14.20, kod na listingu 14.22 stanowi klas
abstrakcyjn pozwalajc na utworzenie odbiorcy komunikatw. Klasa ta rozpoczyna usug
wskazywan przez warto zwracan w metodzie getLRSClass().
Do tej pory wyjanilimy, dlaczego potrzebne s nam dwie abstrakcyjne klasy do zaimplementowania dugoterminowych usug przywoywanych przez odbiorc komunikatw, mianowicie:
ALongRunningNonStickyBroadcastService,
ALongRunningReceiver.
Odkadalimy jednak moment zademonstrowania implementacji kadej z tych klas z powodu
ich zoonoci. Nie pokazalimy te jeszcze implementacji wsplnej klasy, LightedGreenRoom,
wykorzystywanej przez obydwie wymienione klasy. Dotarlimy w kocu do miejsca, z ktrego
moemy zaprezentowa kody rdowe tych dwch wspomnianych klas. Rozpoczniemy
jednak od ich wsplnej klasy LightedGreenRoom.
477
//Kontaktuje si z blokad
PowerManager.WakeLock wl =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag);
//Uzyskuje blokad
wl.acquire();
Za pomoc tego rodzaju oddziaywania odbiorca komunikatw powinien mie moliwo uzyskania blokady, a po zakoczeniu dziaania dugoterminowej usugi blokada ta musi zosta
zwolniona. Jednak nie istnieje skuteczna metoda przekazania usudze tej zmiennej blokady
przechodzenia w stan zatrzymania z poziomu odbiorcy komunikatw. Jedynym rozwizaniem,
dziki ktremu usuga ta uzyska informacj o obecnoci blokady budzenia, jest uycie zmiennej
statycznej lub zmiennej na poziomie aplikacji.
Kolejn trudnoci pojawiajc si podczas uzyskiwania i zwalniania blokady przechodzenia
w stan zatrzymania jest zliczanie referencji. Jeli wic odbiorca komunikatw zostanie kilkakrotnie przywoany, to jeeli te wywoania bd si na siebie nakaday, pojawi si jednoczenie
kilka wywoa dajcych naoenia blokady. Analogicznie pojawi si te kilka wywoa dajcych zwolnienia tej blokady. Jeeli liczba wywoa nakadania i zwalniania blokady nie
bdzie taka sama, skoczy si na tym, e w najgorszym wypadku urzdzenie bdzie wczone
o wiele duej, ni potrzeba. Ponadto, jeeli usuga nie bdzie ju nam potrzebna i bdzie uruchomiony proces oczyszczania pamici, to w przypadku rnicy w liczbie blokad nastpi wywietlenie wyjtku wykonawczego w oknie LogCat.
Problemy te zachciy nas do moliwie jak najskuteczniejszego wyodrbnienia blokady przechodzenia w stan zatrzymania w celu zapewnienia jej waciwego dziaania.
Skoro ju jestemy wiadomi problemw oraz potrzeby posiadania takich blokad,
zachcamy do eksperymentowania z klas LightedGreenRoom oraz zastpowania jej
inn klas, jeli takie rozwizanie okae si prostsze. Powysze zadanie ma nas upewni,
e klasa LightedGreenRoom nie jest magicznym bytem i w swej istocie jest bardzo
nieskomplikowana.
Wyjanimy teraz, co nas skonio do uznania klasy LightedGreenRoom za odpowiedni
blokad przechodzenia w stan zatrzymania.
Tytu podrozdziau odnosi si do nazwy omawianej klasy, ktra w dosownym tumaczeniu oznacza
wanie owietlony zielony pokj przyp. tum.
479
//Znacznik debugowania
private static String tag="LightedGreenRoom";
//Nasz przecznik
PowerManager.WakeLock wl = null;
/*
* Oczekujemy, e bdzie to klasa singletonowa.
* Potencjalnie mona zrobi prywatny
* konstruktor.
*/
public LightedGreenRoom(Context inCtx)
{
ctx = inCtx;
wl = this.createWakeLock(inCtx);
}
/*
* Konfigurowanie zielonego pokoju za pomoc statycznej metody.
* Musi by wywoana przed wywoaniem innych metod.
* Jej zadania:
* 1. Tworzenie wystpienia obiektu.
* 2. Wprowadzenie blokady wczajcej wiata.
* Zaoenie:
* Nie musi by synchronizowana,
* poniewa bdzie wywoywana z gwnego wtku.
* (Moe by bdne. Naley to sprawdzi!!)
*/
private static LightedGreenRoom s_self = null;
public static void setup(Context inCtx)
{
if (s_self == null)
{
Log.d(LightedGreenRoom.tag,"Tworzenie zielonego pokoju i oswietlanie go");
s_self = new LightedGreenRoom(inCtx);
s_self.turnOnLights();
}
}
/*
* Spodziewamy si, e metody wchodzenia i wychodzenia
* bd wsplnie wywoywane.
*
* Przy wejciu licznik jest zwikszany.
*
* Nie wczamy ani nie wyczamy wiate,
* poniewa s one ju zapalone.
*
* Zwikszamy warto licznika tylko po to,
* aby wiedzie, kiedy wyjdzie ostatni go.
*
* Jest to synchronizowana metoda, poniewa
* bdzie wchodzio i wychodzio wiele wtkw.
*
*/
synchronized public int enter()
{
count++;
Log.d(tag,"Nowy gosc: licznik:" + count);
return count;
}
/*
* Spodziewamy si, e metody wchodzenia i wychodzenia
* bd wsplnie wywoywane.
*
* Przy wyjciu zmniejszamy licznik.
*
* Jeeli licznik osignie warto 0, gasimy wiata.
*
* Jest to metoda synchroniczna, poniewa
* wiele wtkw bdzie wchodzio i wychodzio.
*
*/
synchronized public int leave()
{
Log.d(tag,"Opuszczanie pokoju: licznik w momencie wywolania:" + count);
//Ostatni go
//gasi wiata.
turnOffLights();
}
return count;
}
synchronized public int getCount()
{
return count;
}
/*
* Wprowadzamy blokad, aby zapali wiata.
* Od innych synchronizowanych metod zaley, czy zostanie ona
* wywoana we waciwym momencie.
*/
private void turnOnLights()
{
Log.d(tag, "Zapalanie swiatla. Licznik:" + count);
this.wl.acquire();
}
/*
* Zwalnia blokad, aby zgasi wiata.
* Od innych synchronizowanych metod zaley, czy zostanie ona
* wywoana we waciwym momencie.
*/
private void turnOffLights()
{
if (this.wl.isHeld())
{
Log.d(tag,"Zwalnianie blokady. Nie ma juz gosci");
this.wl.release();
}
}
/*
* Standardowy kod sucy do utworzenia czciowej blokady budzenia.
*/
private PowerManager.WakeLock createWakeLock(Context inCtx)
{
PowerManager pm =
(PowerManager)inCtx.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock
(PowerManager.PARTIAL_WAKE_LOCK, tag);
return wl;
}
private int registerClient()
{
Utils.logThreadSignature(tag);
this.clientCount++;
Log.d(tag,"rejestrowanie nowego klienta:licznik:" + clientCount);
481
//*************************************************
//* Statyczni czonkowie: same metody pomocnicze
//* Delegowanie do ukrytego obiektu singletonowego
//*************************************************
public static int s_enter()
{
assertSetup();
return s_self.enter();
}
public static int s_leave()
{
assertSetup();
return s_self.leave();
}
483
485
/*
* Metoda ta moe zosta wywoana w dwch przypadkach:
* 1. Gdy odbiorca komunikatw dostarcza metod startService.
* 2. Gdy Android ponownie j uruchamia z powodu oczekujcych intencji.
*
* W pierwszym przypadku odbiorca komunikatw zdy
* ju skonfigurowa owietlony zielony pokj.
*
* W drugim przypadku musimy zrobi to samo.
*/
@Override
public void onCreate()
{
super.onCreate();
/*
* Zwrmy uwag, e wywoanie tej metody przebiega
* w wtku drugoplanowym, skonfigurowanym przez klas IntentService.
*
* Przesaniamy t metod z poziomu klasy IntentService.
* Odczytujemy oryginaln, nadawan intencj.
* Wywoujemy pochodn klas, zajmujc si intencj nadawanego komunikatu.
* Na kocu owietlony zielony pokj zostaje poinformowany, e go wychodzi.
* W przypadku ostatniego gocia blokada
* zostanie zwolniona.
*/
@Override
final protected void onHandleIntent(Intent intent)
{
try
{
Intent broadcastIntent
= intent.getParcelableExtra("original_intent");
handleBroadcastIntent(broadcastIntent);
}
finally
{
LightedGreenRoom.s_leave();
}
487
/*
* Jeeli Android odzyska proces,
* metoda ta zwolni blokad
* niezalenie od liczby obecnych goci.
*/
@Override
public void onDestroy() {
super.onDestroy();
LightedGreenRoom.s_unRegisterClient();
}
}
<application.>
<receiver android:name=".Test60SecBCR">
<intent-filter>
<action android:name="com.androidbook.intents.testbc"/>
</intent-filter>
</receiver>
<service android:name=".Test60SecBCRService"/>
</application>
..
<uses-permission android:name="android.permission.WAKE_LOCK"/>
</manifest>
489
Zwrmy rwnie uwag, e do uruchomienia tego projektu bd jeszcze potrzebne uprawnienia do korzystania z blokady przechodzenia w stan zatrzymania.
Jeeli bd potrzebne jakie inne pliki, moemy zajrze do projektu umieszczonego w pliku .zip
lub sami je utworzy. Mog to by tak proste elementy, jak domylne ikony lub wartoci w postaci cigw znakw. Gdy ju utworzymy odbiorcw, bdziemy musieli zarejestrowa ich w pliku
manifecie, widocznym na listingu 14.8.
Aby z powyszej listy plikw utworzy dziaajcy projekt, naley wykona ponisze czynnoci:
1. Utwrz nowy projekt poprzez wybr opcji File/New Project/Android/Android Project.
2. Wybierz nazw, a nastpnie zaznacz opcj Create New Project in Workspace.
3. Wprowad nazw aplikacji, na przykad TestBCR. Nazwa aplikacji nie ma wikszego
znaczenia, o wiele waniejsza jest nazwa pakietu.
4. Wybierz poziom interfejsu API.
5. Wprowad nazw pakietu com.androidbook.bcr.
6. Wybierz dowoln, minimaln wersj pakietu SDK, np. 3.
7. Wybierz aktywno o nazwie TestBCRActivity i kliknij przycisk Finish.
8. Android utworzy spor liczb plikw zasobw oraz, prawdopodobnie (w zalenoci
od wersji systemu), pojedynczy plik rdowy.
9. Utwrz, zaktualizuj lub usu te pliki na podstawie listingw 14.2 14.11.
10. W przypadku plikw Java podczas kopiowania treci listingw zamie na samej grze
pliku nazw pakietu. Nastpnie wcinij skrt klawiaturowy Ctrl+Shift+O, aby instrukcje
importu zostay automatycznie wprowadzone.
Naley zwrci uwag, e w trakcie przeprowadzania tego procesu bdzie trzeba zmodyfikowa
kod, aby mg zosta skompilowany oraz aby uzupeni brakujce fragmenty. Wszelkie brakujce elementy moemy skopiowa z pliku ZIP zawierajcego projekt.
491
Odnoniki
Poniej prezentujemy pomocne odnoniki dla Czytelnikw, ktrzy zechc poszerzy wiedz
zawart w tym rozdziale o nowe informacje:
http://developer.android.com/reference/android/content/BroadcastReceiver.html
jest to odnonik kierujcy do interfejsu BroadcastReceiver. W niniejszym
rozdziale omwilimy najbardziej podstawowy rodzaj odbiorcy komunikatw.
Pod tym adresem znajdziemy informacje na temat zamawianych komunikatw oraz
nieco wicej informacji na temat ich cyklu ycia.
http://developer.android.com/reference/android/app/Service.html cze to pozwala
uzyska informacje na temat interfejsu Service. Jest to szczeglnie przydatne rdo
podczas pracy z dugoterminowymi usugami.
http://developer.android.com/reference/android/app/NotificationManager.html
odnonik do interfejsu menedera powiadomie.
http://developer.android.com/reference/android/app/Notification.html adres URL
kierujcy nas do interfejsu Notification. Poznamy tu rnorodne opcje dostpne
podczas korzystania z powiadomie, na przykad takie jak widoki treci oraz efekty
dwikowe.
http://developer.android.com/reference/android/widget/RemoteViews.html tu znajdziemy
informacje o interfejsie RemoteViews. Obiekty tego typu s wykorzystywane do tworzenia
wasnych, szczegowych widokw powiadomie.
http://www.androidbook.com/item/3514 znajdziemy tutaj notatki autorw dotyczce
bada nad dugoterminowymi usugami.
Podsumowanie
W tym rozdziale omwilimy bardzo wane zagadnienia: odbiorcw komunikatw, powiadomienia, blokady przechodzenia w stan zatrzymania oraz dugoterminowe usugi. Zebralimy tu
rwnie najistotniejsze zagadnienia omwione w rozdziaach 12. i 13.
Zademonstrowalimy podstawy stosowania odbiorcy komunikatw, jego czas ycia oraz sposb
dziaania zarwno w procesie, jak i poza nim. Pokazalimy, jak mona doczy do niego usugi,
dziki czemu czas ycia odbiorcy komunikatw zostaje przeduony. Na koniec poeksperymentowalimy z klas IntentService i pokazalimy, w jaki sposb mona j dalej dostosowywa do
wasnych potrzeb przy okazji uywania dugoterminowych usug.
W rozdziale 15. dowiemy si, w jaki sposb mona skorzysta z menedera alarmw do przywoania odbiorcy komunikatw.
R OZDZIA
15
Badanie menedera alarmw
Za pomoc menedera alarmw mona uruchamia zdarzenia w Androidzie. Zdarzenia te mog wystpowa o okrelonej porze lub w regularnych odstpach czasowych. Rozpoczniemy ten rozdzia od omwienia podstaw menedera alarmw, mianowicie od skonfigurowania prostego alarmu. Nastpnie zwrcimy uwag na sposb
konfiguracji powtarzalnego alarmu, anulowania alarmu, roli intencji oczekujcych
(zwaszcza roli, jak odgrywa ich unikatowo) oraz ustanawiania wielu alarmw
naraz. Po ukoczeniu lektury rozdziau Czytelnik zdy zapozna si z podstawami
menedera alarmw w Androidzie oraz jego praktycznymi zastosowaniami.
Na listingu 15.1 zmienna mContext odnosi si do obiektu kontekstu. Jeli na przykad przywoamy ten kod z poziomu menu aktywnoci, zmienna kontekstu jest t aktywnoci.
495
Ustawianie alarmu
Po zdefiniowaniu czasu w postaci obiektu Calendar (czas jest wyraany w milisekundach) oraz
oczekujcej intencji wskazujcej na odbiorc moemy ustawi alarm za pomoc metody set()
menedera alarmw, co zostao zaprezentowane na listingu 15.8.
Listing 15.8. Metoda suca do ustanawiania menedera alarmw
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendarObject.getTimeInMillis(),
pendingIntent);
Jeeli wprowadzimy atrybut RTC_WAKEUP, alarm spowoduje wyjcie urzdzenia ze stanu wstrzymania. Moemy zamiast tego ustawienia wprowadzi atrybut RTC, ktry spowoduje dostarczenie
intencji w momencie wyjcia urzdzenia z tego stanu.
Czas zdefiniowany przez drugi argument jest wyraony w sposb waciwy dla obiektu calendar
Object utworzonego na listingu 15.3. Jest to czas liczony w milisekundach, zliczany od 1970
roku. Pokrywa si to rwnie z domylnymi ustawieniami obiektu Java Calendar.
Po wywoaniu tej metody meneder alarmw przywoa odbiorc TestReceiver (listing 15.4)
po upywie 30 sekund.
497
Projekt testowy
Utwrzmy teraz projekt testowy, dziki ktremu dokadniej przeanalizujemy dziaanie dotychczas omwionego kodu.
Na kocu rozdziau zamiecilimy adres URL, z ktrego mona pobra projekty
utworzone na potrzeby ksiki oraz zaimportowa je do rodowiska Eclipse.
/*
* Interfejs, zazwyczaj implementowany przez aktywno,
* za pomoc ktrego klasa robocza moe przekaza informacje
* na temat zachodzcych zdarze.
*/
public interface IReportBack
{
public void reportBack(String tag, String message);
}
Jest to prosta klasa pomocnicza, zapewniajca dwa elementy wywodzcym si od niej klasom
testujcym, takim jak SendAlarmOnceTester: kontekst, ktry w miar potrzeby bdzie wykorzystywany przez ich metody, oraz aktywno implementujc interfejs IReportBack, dziki
czemu komunikaty bd zapisywane w dzienniku.
Po utworzeniu interfejsu IReportBack oraz SendAlarmOnceTester bdziemy mogli zacz
wprowadzanie kodu klasy SendAlarmOnceTester.java, testujcej wysyanie pojedynczego alarmu
(listing 15.11).
Listing 15.11. Plik klasy pozwalajcej na jednorazowe wysanie alarmu
// SendAlarmOnceTester.java
package com.androidbook.alarms;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
public class SendAlarmOnceTester extends BaseTester
{
private static String tag = "SendAlarmOnceTester";
SendAlarmOnceTester(Context ctx, IReportBack target)
{
super(ctx, target);
}
/*
* Alarm moe przywoywa danie nadania komunikatu
* o okrelonej porze.
* Nazwa odbiorcy komunikatu jest jawnie
499
* zdefiniowana w intencji.
*/
public void sendAlarmOnce()
{
Intencja klasy SendAlarmOnceTester wysya pojedynczy alarm, co powoduje wywoanie odbiorcy komunikatw. Moemy si o tym przekona, przygldajc si metodzie sendAlarmOnce(),
pokazanej na listingu 15.11. Odbiorca TestReceiver, bdcy celem alarmu, zosta przedstawiony na listingu 15.4, zatem kady aspekt tej metody zosta ju wczeniej omwiony. Listing
15.11 stanowi jedynie zoenie opisanych powyej fragmentw kodu.
Przeanalizujmy teraz aktywno sterujc, wywoujc metod sendAlarmOnce(). Jej kod
rdowy prezentujemy na listingu 15.12. Ta gwna aktywno projektu testowego przywouje
obiekty menu, za pomoc ktrych bdziemy sprawdza rne rodzaje alarmw (zarwno ju
przedstawionych, jak i tych, ktre dopiero zostan omwione). Na razie jednak dysponujemy
wycznie fragmentem kodu pozwalajcym na wywoanie elementu menu, dziki ktremu uruchomimy omwiony powyej mechanizm. W dalszej czci rozdziau poznamy fragmenty kodu uruchamiane po wciniciu pozostaych opcji menu.
//z aktywnoci
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
appendMenuItemText(item);
if (item.getItemId() == R.id.menu_clear)
{
this.emptyText();
return true;
}
if (item.getItemId() == R.id.menu_alarm_once)
{
alarmTester.sendAlarmOnce();
return true;
501
Jak wida, aktywno TestAlarmsDriverActivity reaguje na kilka elementw menu. Zdefiniowany dla niej plik menu.xml zosta zaprezentowany na listingu 15.13. Zamiecilimy w nim
od razu wszystkie dodatkowe scenariusze testowe, ktrymi bdziemy si zajmowa przez reszt
rozdziau. Poniewa obecno tych dodatkowych opcji nie bdzie nam przeszkadza w skompilowaniu projektu, postanowilimy umieci je wszystkie za jednym zamachem.
Listing 15.13. Elementy menu suce do testowania rnorodnych scenariuszy menedera alarmw
<!-- /res/menu/main_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Listing 15.14 zawiera plik ukadu graficznego obsugujcy aktywno sterujc TestAlarms
DriverActivity (listing 15.12). Plik ten znajduje si w katalogu /res/layout/main.xml.
Listing 15.14. Ukad graficzny aktywnoci TestAlarmsDriverActivity
<?xml version="1.0" encoding="utf-8"?>
503
Poza definicj odbiorcy nie s potrzebne inne wpisy w manifecie, eby korzysta z menedera
alarmw. Definicja odbiorcy jest na powyszym listingu zaznaczona pogrubionym drukiem.
Po skompilowaniu i uruchomieniu projektu powinnimy ujrze aktywno i struktur menu
przypominajc interfejs uytkownika na rysunkach 15.1 i 15.2.
Na rysunku 15.1 widzimy cz opcji dostpnych w menu. Aby ujrze pozostae elementy menu,
musimy klikn ikon Wicej. Opcje te zostay ukazane na rysunku 15.2.
Jeeli teraz wybierzemy widoczn na rysunku 15.1 opcj Alarm pojedynczy, uruchomimy kod
zawarty w metodzie sendAlarmOnce() (listing 15.11). W tym momencie zostanie zdefiniowany
alarm, ktry uruchomi si po 30 sekundach. Po tym czasie odbiorca TestReceiver zacznie
umieszcza komunikaty w oknie LogCat.
Aby zrozumie zasad postpowania w takim przypadku, spjrzmy na kod na listingu 15.16.
Mamy tu do czynienia ze rodowiskiem testowym, zawierajcym metod SendOnceAlarm
Tester(), w ktrym zaimplementowano rwnie metod sendRepeatingAlarm(). W ten
sposb umoliwiono sprawdzanie powtarzalnego alarmu.
Listing 15.16. Konfigurowanie powtarzalnego alarmu
// SendRepeatingAlarmTester.java
package com.androidbook.alarms;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
public class SendRepeatingAlarmTester
extends SendAlarmOnceTester
{
private static String tag = "SendRepeatingAlarmTester";
SendRepeatingAlarmTester(Context ctx, IReportBack target)
{
super(ctx, target);
}
/*
* Alarm moe wywoa danie nadawania komunikatu
* o okrelonej porze oraz
* w regularnych odstpach.
505
*
* Wykorzystuje t sam intencj co powyej,
* lecz inny identyfikator dania w celu uniknicia konfliktw
* z wczeniej utworzonym alarmem jednokrotnym.
*
* Wykorzystuje metod getDistinctPendingIntent().
*/
public void sendRepeatingAlarm()
{
Calendar cal = Utils.getTimeAfterInSecs(30);
//identyfikator dania
//dostarczana intencja
0);
return pi;
}
}
nastpujcymi:
private SendRepeatingAlarmTester alarmTester = null;
Anulowanie alarmu
Aby zrozumie, jak przebiega anulowanie alarmw, wprowadzimy kolejn klas testow, nazwan CancelRepeatingAlarmTester (listing 15.17).
Listing 15.17. Anulowanie powtarzalnego alarmu
// CancelRepeatingAlarmTester.java
package com.androidbook.alarms;
import android.app.AlarmManager;
507
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
public class CancelRepeatingAlarmTester
extends SendRepeatingAlarmTester
{
private static String tag = "CancelRepeatingAlarmTester";
CancelRepeatingAlarmTester(Context ctx, IReportBack target) {
super(ctx, target);
}
/*
* Alarm moe zosta zatrzymany poprzez anulowanie intencji.
* Do anulowania intencji bdzie nam potrzebna
* jej kopia.
*
* Intencja ta musi posiada t sam sygnatur
* oraz identyfikator dania.
*/
public void cancelRepeatingAlarm()
{
Aby anulowa alarm, musimy najpierw skonstruowa intencj oczekujc, a nastpnie przekaza j menederowi alarmw jako argument metody cancel().
Musimy jednak mie pewno, e obiekt pendingIntent jest skonstruowany dokadnie w taki
sam sposb, jak to miao miejsce podczas konfigurowania alarmu, cznie z identyfikatorem
dania i docelowym odbiorc. Przyjrzyjmy si kodowi rdowemu metody getDistinct
PendingIntent() z listingu 15.16, aby zrozumie, w jaki sposb kod dania jest wykorzystywany wraz z metod PendingIntent.getBroadcast() moemy zignorowa dodatkowe dane
intencji z listingu 15.17, poniewa nie odgrywaj one roli w procesie jej anulowania.
nastpujcymi:
private CancelRepeatingAlarmTester alarmTester = null;
Moemy przetestowa t funkcj, klikajc najpierw obiekt menu Alarm powtarzalny (rysunek
15.1). W wyniku tego widok LogCat zacznie by aktualizowany co 5 sekund. Jeeli wybierzemy
teraz opcj Anuluj alarmy, komunikaty przestan napywa.
/*
* Nie mona wielokrotnie ustanawia harmonogramu tej samej intencji.
* Jeeli tak zrobimy, tylko ostatni bdzie wykonywany.
*
* Zwrmy uwag, e wykorzystujemy ten sam identyfikator dania.
*/
public void scheduleSameIntentMultipleTimes()
{
cal = Utils.getTimeAfterInSecs(30);
cal2 = Utils.getTimeAfterInSecs(35);
cal3 = Utils.getTimeAfterInSecs(40);
cal4 = Utils.getTimeAfterInSecs(45);
/*
* Mona wielokrotnie wykorzystywa t sam intencj,
* jeeli zmienimy identyfikator dania w oczekujcej intencji.
509
cal = Utils.getTimeAfterInSecs(30);
cal2 = Utils.getTimeAfterInSecs(35);
cal3 = Utils.getTimeAfterInSecs(40);
cal4 = Utils.getTimeAfterInSecs(45);
//Tworzy harmonogram tej samej intencji, ale za pomoc rnych id. da.
AlarmManager am =
(AlarmManager)
mContext.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(),
getDistinctPendingIntent(intent,1));
am.set(AlarmManager.RTC_WAKEUP,
cal2.getTimeInMillis(),
getDistinctPendingIntent(intent,2));
am.set(AlarmManager.RTC_WAKEUP,
cal3.getTimeInMillis(),
getDistinctPendingIntent(intent,3));
am.set(AlarmManager.RTC_WAKEUP,
cal4.getTimeInMillis(),
getDistinctPendingIntent(intent,4));
511
nastpujcymi:
private ScheduleIntentMultipleTimesTester alarmTester = null;
Po wprowadzeniu tych zmian moemy przetestowa funkcje dostpne w tym przykadzie poprzez
kliknicie dwch elementw menu: Wiele alarmw i Oddzielne intencje. Wyniki testu ujrzymy
w oknie LogCat.
W tym przypadku prawdopodobnie nabiera sensu ustawienie tylko jednego obiektu intencji
oraz zwizanego z nim alarmu, a take stwierdzenie, e jeli ustanowimy ten alarm wiele razy,
kady poprzedni alarm zostanie zresetowany, podobnie jak ma to miejsce w przypadku zwykego budzika.
T koncepcj moemy sprawdzi za pomoc kodu umieszczonego na listingu 15.19. Interesujc
nas tutaj metod jest alarmIntentPrimacy().
Listing 15.19. Kod sucy do przetestowania pierwszestwa intencji
//AlarmIntentPrimacyTester.java
package com.androidbook.alarms;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
public class AlarmIntentPrimacyTester
extends ScheduleIntentMultipleTimesTester
{
private static String tag = "AlarmIntentPrimacyTester";
AlarmIntentPrimacyTester(Context ctx, IReportBack target){
super(ctx, target);
513
/*
* Tutaj nie alarm ma znaczenie,
* lecz oczekujca intencja.
* Nawet jeli zdefiniujemy powtarzalny alarm,
* w przypadku wykorzystywania tej samej intencji za jednym razem
* tylko ta pniejsza intencja odniesie skutek.
*
* Moemy to porwna do wielokrotnego konfigurowania
* alarmu wobec istniejcej intencji,
* a nie okrnym sposobem.
*/
public void alarmIntentPrimacy()
{
Calendar cal = Utils.getTimeAfterInSecs(30);
String s = Utils.getDateTimeString(cal);
this.mReportTo.reportBack(tag,
"Ustanawianie powtarzalnego alarmu o interwale 5 s, poczwszy od: " + s);
nastpujcymi:
private AlarmIntentPrimacyTester alarmTester = null;
Po wprowadzeniu tych zmian moemy przetestowa funkcje dostpne w tym przykadzie poprzez kliknicie elementu menu Pierwszestwo intencji. Wyniki testu ujrzymy w oknie LogCat,
w ktrym bd rwnie wywietlane nadpisywania starszych alarmw przez alarmy nowsze.
Dlaczego starszy alarm jest zastpowany nowszym w przypadku korzystania z tej samej intencji?
Wiele osb spord zespou twrcw Androida zauwaa, e dwie intencje s tak naprawd wystpieniem tego samego obiektu PendingIntent, jeeli wartoci ich atrybutw s takie same.
Ustanawianie takich intencji jako celw dla wielu alarmw jest tosame z ustanawianiem alarmw dla jednej i tej samej intencji.
Jednak waciwy mechanizm zrozumiemy dopiero po przyjrzeniu si kodowi rdowemu
usugi AlarmManagerService (jest to implementacja interfejsu IAlarmManager). Na listingu
15.20 zosta zamieszczony fragment kodu odpowiedzialny za ustanawianie alarmu (wszystkie
metody ustawiajce ostatecznie przechodz przez ten fragment).
Listing 15.20. Implementacja klasy AlarmManagerService, wycig z kodu rdowego Androida
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
515
Zwrmy uwag, e w samym rodku metody ustawiania kod wywouje metod removeLocked
(operation), gdzie argumentem operation jest obiekt PendingIntent. Ta metoda jest wanie
odpowiedzialna za usunicie wczeniej wystpujcego alarmu. W istocie, gdy wywoujemy
metod cancel(pendingIntent), ostatecznie zostaje rwnie wywoana ta sama metoda
removeLocked(pendingIntent).
Mwic krtko, zestaw SDK postanowi anulowa wszystkie wczeniejsze alarmy i dla tej konkretnej intencji oczekujcej zostawi tylko najnowszy alarm. Jeeli chcemy, aby stao si inaczej,
musimy ustawi dla tej intencji identyfikator dania. Staje si to rwnie jasne, gdy zapoznamy
si z interfejsem API metody cancel(), ktra pobiera wycznie argument w postaci obiektu
PendingIntent. Gdyby zwizek pomidzy alarmem a obiektem PendingIntent nie mia niepowtarzalnego charakteru, jakie znaczenie miaoby anulowanie alarmu wycznie na podstawie
obiektu PendingIntent?
Oczywicie, moemy wykorzysta ten mechanizm na nasz korzy, jeeli naszym celem bdzie
anulowanie wszelkich wczeniejszych alarmw i ustanowienie nowego dla danego odbiorcy.
Trwao alarmw
Czytelnikowi naley si jeszcze jedna informacja dotyczca alarmw: nie s one zachowywane
po ponownym uruchomieniu urzdzenia. Oznacza to, e bdziemy musieli utrzymywa ustawienia alarmu oraz intencji oczekujcej w pamici trwaej oraz ponownie je rejestrowa na podstawie komunikatw nadawanych podczas ponownego uruchamiania urzdzenia oraz, ewentualnie, komunikatw zwizanych ze zmian czasu (na przykad android.intent.action.
BOOT_COMPLETED, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED).
Odnoniki
Ponisze odnoniki stanowi dodatkowy materia do informacji omawianych w niniejszym rozdziale. Warto zwaszcza zwrci uwag na ostatni adres URL, gdy prowadzi do strony z projektami, ktre moemy pobra i zaimportowa w rodowisku Eclipse.
http://developer.android.com/reference/android/app/AlarmManager.html
znajdziemy tu interfejs menedera alarmw. Zostay tu omwione takie metody,
jak set, setRepeating czy cancel.
http://developer.android.com/reference/android/app/PendingIntent.html
na tej stronie zosta wyjaniony mechanizm konstruowania intencji oczekujcej.
Nie zwracajmy zbytnio uwagi na flagi intencji oczekujcych; nie s one niezbdne
w przypadku menedera alarmw.
http://www.androidbook.com/item/1040 kilka krtkich przykadw oraz dalsze
odniesienia do informacji o klasach zwizanych z dat i czasem.
http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/util/
Calendar.html dziki zawartym tu zasobom lepiej zrozumiemy zasady pracy
z obiektem Calendar.
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
wykorzystywanych w tej ksice. Katalog zawierajcy projekty z tego rozdziau nosi
nazw ProAndroid3_R15_MenederAlarmw.
Podsumowanie
W tym rozdziale pokazalimy, jak wykorzysta meneder alarmw do uruchomienia kodu
o okrelonej porze oraz we wskazanych przedziaach czasowych. Jest to bardzo wana funkcja,
wykorzystywana do aktualizacji widetw ekranu startowego oraz w innych czynnociach zalenych od czasu. Wymienilimy rwnie pewne specyficzne cechy zwizane z tym menederem
i pokazalimy sposoby rozwizywania wynikajcych z nich problemw.
R OZDZIA
16
Analiza animacji
dwuwymiarowej
Animacj klatek kluczowych mona wyjani rwnie w taki sposb, e nie wymaga ona rysowania klatki po klatce. Jeeli moemy animowa obiekt bez koniecznoci nakadania i powtarzania kolejnych klatek, to mamy do czynienia z technik
klatek kluczowych. Jeeli na przykad dany obiekt znajduje si w punkcie A, a za 4
sekundy znajdzie si w punkcie B, moemy zmienia jego pooenie co sekund i za
kadym razem od nowa go rysowa. Bdziemy odnosi wraenie, e obiekt ten
porusza si z punktu A do punktu B.
Animacja poklatkowa
Animacja jest prostym procesem polegajcym na wywietlaniu serii obrazw nastpujcych
po sobie w krtkich odstpach czasu, w wyniku czego powstaje wraenie poruszajcego lub
zmieniajcego si obiektu. W taki sposb dziaaj projektory filmowe. Pokaemy przykadowy
projekt, w ktrym zaprojektujemy obraz i zapiszemy go w formie serii oddzielnych klatek, rnicych si od siebie w niewielkim stopniu. Nastpnie umiecimy ten zbir obrazw w przykadowym kodzie umoliwiajcym uruchomienie animacji.
519
Okrelmy sobie podstawowy czon nazwy takiego rysunku, na przykad colored-ball, a nastpnie zachowajmy utworzone rysunki w podkatalogu /res/drawable, eby w przyszoci mona
byo uzyska do nich dostp za pomoc identyfikatorw zasobw. Nazwa kadego pliku powinna zosta utworzona za pomoc wzoru colored-ballN, gdzie N jest numerem porzdkowym klatki. Po utworzeniu animacji powinna ona wyglda tak jak na rysunku 16.2.
Gwny obszar aktywnoci jest wykorzystywany przez widok animacji. Wstawilimy przycisk
uruchamiania i zatrzymywania animacji w celu obserwacji jej zachowania. W grnej czci
ekranu umiecilimy rwnie notatnik testowy, w ktrym mona zapisywa wszelkie wane
zdarzenia podczas eksperymentowania z programem. Zobaczmy, w jaki sposb mona utworzy ukad graficzny takiej aktywnoci.
Utworzenie aktywnoci
Rozpocznijmy od utworzenia prostego pliku XML ukadu graficznego w podkatalogu /res/layout
(listing 16.1).
Listing 16.1. Plik XML ukadu graficznego do przykadu animacji poklatkowej
<?xml version="1.0" encoding="utf-8"?>
Pierwsz kontrolk jest kontrolka notatnika testowego stanowica prosty widok TextView.
Nastpnie dodajemy przycisk uruchamiania i zatrzymywania animacji. Ostatni jest widok
ImageView, w ktrym bdzie odtwarzana animacja. Po skonstruowaniu ukadu graficznego
naley utworzy aktywno wczytujc ten widok (listing 16.2).
Listing 16.2. Aktywno wczytujca widok ImageView
public class FrameAnimationActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.frame_animations_layout);
}
}
Tak aktywno bdzie mona uruchomi za pomoc dowolnego elementu menu, dostpnego
w biecej aplikacji, poprzez wykonanie nastpujcego kodu:
Intent intent = new Intent(inActivity,FrameAnimationActivity.class);
inActivity.startActivity(intent);
521
Kada klatka wskazuje na jeden z rysunkw okrelony przez jego identyfikator zasobu. Znacznik
animation-list zostaje przeksztacony do obiektu AnimationDrawable, reprezentujcego
zbir obrazw. Musimy teraz umieci klas Drawable jako zasb ta dla widoku ImageView.
Zakadajc, e nazwalimy ten plik frame_animation.xml i umiecilimy go w podkatalogu
/res/drawable, moemy zastosowa poniszy kod do ustanowienia klasy AnimationDrawable
jako ta widoku ImageView:
view.setBackGroundResource(Resource.drawable.frame_animation);
Metoda setOneShot() odtwarza animacj jeden raz i potem j zatrzymuje. Metoda addFrame()
dodaje now klatk za pomoc obiektu Drawable i konfiguruje czas jej wywietlania. Dziaanie
tej metody przypomina funkcj znacznika XML android:drawable.
Teraz musimy zoy wszystkie fragmenty kodu w cao, aby otrzyma rodowisko testowe
animacji poklatkowej (listing 16.4).
Listing 16.4. Peny kod rodowiska testowego animacji poklatkowej
public class FrameAnimationActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.frame_animations_layout);
this.setupButton();
}
private void setupButton()
{
523
Button b = (Button)this.findViewById(R.id.startFAButtonId);
b.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View v)
{
parentButtonClicked(v);
}
});
}
private void parentButtonClicked(View v)
{
animate();
}
private void animate()
{
ImageView imgView = (ImageView)findViewById(R.id.animationImage);
imgView.setVisibility(ImageView.VISIBLE);
imgView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable frameAnimation =
(AnimationDrawable) imgView.getBackground();
if (frameAnimation.isRunning())
{
frameAnimation.stop();
}
else
{
frameAnimation.stop();
frameAnimation.start();
}
}
}//eof-class
Metoda
odczytuje ten obiekt i przeprowadza proces animowania. Przycisk uruchamiania i zatrzymywania jest skonfigurowany w ten sposb, e jego nacinicie w trakcie odtwarzania animacji
zatrzyma j; jeeli animacja jest zatrzymana, jego nacinicie spowoduje jej uruchomienie.
Zauwamy, e jeli przypiszemy parametrowi listy animacji OneShot warto true, animacja wykona tylko jeden cykl. Jednak nie mona dokadnie przewidzie, kiedy si to stanie.
Chocia animacja zostaje zakoczona po wywietleniu ostatniego obrazu, nie otrzymamy
adnego informujcego o tym komunikatu. Z tego powodu nie istnieje aden bezporedni
sposb wywoania kolejnej czynnoci w odpowiedzi na zakoczenie animacji.
Pomimo tej niedogodnoci mona uzyska wspaniae efekty wizualne poprzez wywietlanie
po kolei serii obrazw w prostym procesie animacji poklatkowej.
Wszystkie wartoci parametrw w tym pliku animacji zostaj okrelane od do, poniewa
musimy okreli wartoci pocztkowe i kocowe animacji.
525
527
Niektre fragmenty kodu widocznego na listingu 16.7 s oczywiste, ale inne nie. Pierwsza cz
kodu w zwyky sposb wczytuje widok na podstawie wygenerowanego identyfikatora ukadu
graficznego R.layout.list_layout. Naszym zadaniem jest zapenienie widoku ListView
z tego ukadu graficznego szecioma elementami. Te elementy tekstowe zostay wczytane do
tablicy. Musimy ustanowi adapter danych wobec widoku ListView, eby te elementy mogy
zosta wywietlone.
Aby utworzy wymagany adapter, musimy okreli, w jaki sposb kady z elementw bdzie
wstawiany podczas wywietlania listy na ekranie. Ukad graficzny okrelamy za pomoc predefiniowanego ukadu, znajdujcego si w strukturze Androida. W naszym przykadzie ukad
graficzny wyznaczono nastpujco:
android.R.layout.simple_list_item_1
Mona zajrze do dokumentacji Androida, aby si dowiedzie, jak te ukady graficzne wygldaj
i jak si zachowuj. Teraz moemy wywoa t aktywno za pomoc dowolnego przycisku
menu w aplikacji po wstawieniu nastpujcego kodu:
Intent intent = new Intent(inActivity,LayoutAnimationActivity.class);
inActivity.startActivity(intent);
529
Rwnie ten plik naley umieci w podkatalogu /res/anim. W naszym przykadzie zakadamy, e plik nosi nazw list_layout_controller. Po przyjrzeniu si definicji pliku poredniczcego zrozumiemy, dlaczego jest on niezbdny.
W pliku tym zostaje okrelone, e animacja tej listy powinna przebiega w odwrconym porzdku oraz e animacja kadego elementu bdzie opniona o 30% wzgldem cakowitego czasu
trwania animacji. Znajduje si tu rwnie odniesienie do pliku animacji scale.xml. Zauwamy rwnie, e w kodzie jest uyte odniesienie do tego pliku @anim/scale zamiast jego nazwy.
Gdy ju posiadamy wymagane pliki XML z danymi wejciowymi, pokaemy, w jaki sposb
naley zaktualizowa definicj XML widoku ListView, eby obejmowaa ona animacj
XML jako argument. Najpierw przejrzyjmy dotychczas utworzone pliki XML:
// pojedyncza animacja skali
/res/anim/scale.xml
// plik poredniczcy
/res/anim/list_layout_controller.xml
Gdy te pliki s gotowe, musimy zmodyfikowa plik XML ukadu graficznego list_layout.xml
w taki sposb, eby widok ListView wskazywa plik list_layout_controller.xml (listing 16.10).
Listing 16.10. Zaktualizowany kod pliku List_Layout.xml
<?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"
>
<ListView
Animacja typu alfa jest odpowiedzialna za kontrol zmiany nasycenia kolorw. W tym przykadzie w cigu 1000 milisekund (1 sekundy) kolor z przezroczystego staje si w peni nasycony.
Dobrze jest ustawi czas trwania animacji na co najmniej 1 sekund, w przeciwnym wypadku
zmiana nasycenia bdzie trudna do zaobserwowania.
W przypadku zmiany animacji pojedynczego elementu musimy zmieni rwnie tre pliku
poredniczcego (listing 16.9), eby wskazywaa plik z now animacj. Poniej pokazalimy
sposb wskazywania z animacji skali na animacj typu alfa:
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="30%"
android:animationOrder="reverse"
android:animation="@anim/alpha" />
Zmieniony wiersz w tym kodzie wyrniono pogrubion czcionk. Sprbujmy teraz stworzy
animacj czc zmian pooenia ze zmian gradientu nasycenia koloru. Listing 16.12 przedstawia przykadowy kod takiej animacji.
531
Listing 16.12. Poczenie animacji translacyjnej z animacj typu alfa w zestawie animacji
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<translate android:fromYDelta="-100%" android:toYDelta="0"
android:duration="500" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="500" />
</set>
Zwrmy uwag, w jaki sposb okrelilimy dwie animacje w zestawie animacji. Animacja
translacyjna bdzie przesuwaa tekst z gry na d w wydzielonym dla niego obszarze wywietlania. Animacja typu alfa bdzie powodowa zmian gradientu nasycenia koloru od przezroczystego do cakowicie nasyconego podczas przesuwania tekstu w d. Warto 500 czasu
trwania animacji pozwoli uytkownikowi obserwowa w wygodny sposb zmian. Oczywicie
znowu bdzie trzeba zmieni plik poredniczcy layoutAnimation, tak eby znalazo si w nim
odniesienie do nowego pliku. Zakadajc, e nazw pliku zawierajcego poczone animacje
jest /res/anim/translate-alpha.xml, plik layoutAnimation bdzie wyglda nastpujco:
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="30%"
android:animationOrder="reverse"
android:animation="@anim/translate-alpha" />
Kod z listingu 16.13 spowoduje wykonanie jednego penego obrotu przez kady element tekstowy wok rodka tego elementu. Czas trwania 500 milisekund cakowicie wystarczy, eby
obserwator dostrzeg animacj. Podobnie jak w poprzednich przypadkach, tak i teraz musz
zosta zmodyfikowane pliki XML kontrolera animacji oraz ukadu graficznego ListView,
a aplikacja musi zosta ponownie uruchomiona, eby animacja zadziaaa.
Omwilimy ju podstawowe pojcia dotyczce animacji ukadu graficznego, poczwszy od
prostego pliku animacji, a skoczywszy na powizaniu go poprzez plik poredniczcy layout
Animation z widokiem ListView. Ta wiedza wystarczy, eby ujrze animowane efekty. Musimy
omwi jednak jeszcze jedno pojcie dotyczce animacji ukadu graficznego interpolatory.
Stosowanie interpolatorw
Interpolatory okrelaj, w jaki sposb dana waciwo, na przykad gradient koloru, zmienia
si wzgldem czasu. Czy bdzie si ona zmieniaa w sposb liniowy, czy w sposb wykadniczy?
Czy rozpocznie si szybko, lecz bdzie zwalniaa z biegiem czasu? Zastanwmy si nad przykadem animacji typu alfa z listingu 16.11:
W dokumentacji jzyka Java dotyczcej tej klasy mona zobaczy, jakie znaczniki XML s dla niej
dostpne. Zadaniem tego interpolatora jest zapewnienie wspczynnika powielania danego przedziau czasowego w oparciu o krzyw hiperboliczn. Wida to w kodzie rdowym interpolatora:
public float getInterpolation(float input)
{
if (mFactor == 1.0f)
{
return (float)(input * input);
}
else
{
return (float)Math.pow(input, 2 * mFactor);
}
}
Kady interpolator w inny sposb implementuje metod getInterpolation. W naszym przypadku, jeli interpolator zostanie skonfigurowany tak, e wspczynnik bdzie wynosi 1.0,
zostanie zwrcony kwadrat tego wspczynnika. W przeciwnym razie zostanie zwrcona
potga danych wejciowych, ktre bd nadal skalowane przez ten wspczynnik. Jeeli zatem warto wspczynnika bdzie wynosia 1.5, zamiast funkcji kwadratowej ujrzymy funkcj
szecienn.
Poniej wypisalimy list obsugiwanych interpolatorw:
AccelerateDecelerateInterpolator
AccelerateInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator
AnticipateInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
OvershootInterpolator
533
eby zaprezentowa potencjaln elastyczno interpolatorw, przyjrzyjmy si pokrtce obiektowi BounceInterpolator, powodujcemu podskakiwanie elementu (to znaczy jego naprzemienny ruch w gr i w d) do samego koca poniszej animacji:
public class BounceInterpolator implements Interpolator {
private static float bounce(float t) {
return t * t * 8.0f;
}
public float getInterpolation(float t) {
t *= 1.1226f;
if (t < 0.3535f) return bounce(t);
else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
else return bounce(t - 1.0435f) + 0.95f;
}
}
Animacja widoku
Skoro zapoznalimy si ju z animacj poklatkow oraz animacj ukadu graficznego, moemy
zaj si animacj widoku najbardziej skomplikowanym rodzajem animacji. Stosowana jest
w niej technika animowania dowolnego widoku poprzez kontrolowanie macierzy transformacji,
sucej do wywietlania widoku.
Animacja widoku
Widok wywietlany przez Androida przechodzi przez macierz transformacji. W aplikacjach
graficznych macierze transformacji su do przeksztacenia w jaki sposb widoku. Proces ten
polega na przetumaczeniu wejciowego zestawu wsprzdnych pikseli i kombinacji kolorw
na nowy zestaw. Po przeprowadzeniu transformacji ujrzymy obraz zmieniony pod wzgldem
rozmiaru, pozycji, orientacji lub koloru.
Te przeksztacenia mona przeprowadzi za pomoc aparatu matematycznego, mnoc w okrelony sposb wejciowy zestaw wsprzdnych przez wartoci macierzy transformacji, dziki
czemu powstanie nowy zestaw wsprzdnych. Poprzez zmian macierzy transformacji wpywamy na wygld widoku.
Po klikniciu przycisku Uruchom animacj powinien si pojawi may widok porodku ekranu,
ktry nastpnie stopniowo bdzie si powiksza a do wypenienia zarezerwowanej dla niego
przestrzeni. Zaprezentujemy kod, ktry nam to umoliwi. Na listingu 16.14 zosta pokazany
kod pliku XML ukadu graficznego, nadajcy si do zastosowania w aktywnoci.
Listing 16.14. Plik XML ukadu graficznego dla aktywnoci animacji widoku
<?xml version="1.0" encoding="utf-8"?>
535
<Button
android:id="@+id/btn_animate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Uruchom animacj"
/>
<ListView
android:id="@+id/list_view_id"
android:persistentDrawingCache="animation|scrolling"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
Pogrubiona czcionka ma zwrci uwag Czytelnika na lokalizacj oraz nazw pliku. Ten ukad
graficzny skada si z dwch czci: pierwsza z nich to przycisk btn_animate, sucy do uruchomienia animacji widoku; drug jest widok ListView, w naszym przypadku nazwany
list_view_id.
Skoro mamy ju ukad graficzny dla aktywnoci, moemy utworzy sam aktywno, eby
wywietli widok i skonfigurowa przycisk Uruchom animacj (listing 16.15).
Listing 16.15. Kod dla aktywnoci animacji widoku przed rozpoczciem animacji
public class ViewAnimationActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.list_layout);
setupListView();
this.setupButton();
}
private void setupListView()
{
String[] listItems = new String[] {
"Element 1", "Element 2", "Element 3",
"Element 4", "Element 5", "Element 6",
};
ArrayAdapter listItemAdapter =
new ArrayAdapter(this
,android.R.layout.simple_list_item_1
,listItems);
ListView lv = (ListView)this.findViewById(R.id.list_view_id);
lv.setAdapter(listItemAdapter);
}
private void setupButton()
{
Button b = (Button)this.findViewById(R.id.btn_animate);
b.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View v)
{
Kod przedstawiony dla aktywnoci animacji widoku z listingu 16.15 bardzo przypomina kod
aktywnoci animacji ukadu graficznego z listingu 16.7. W podobny sposb wczytalimy widok
i wstawilimy sze elementw tekstowych do widoku ListView. Skonfigurowalimy przycisk
w taki sposb, eby wywoywa metod animateListView() po klikniciu. Na razie jednak
oznaczymy ten fragment komunikatem, dopki nasz przykad nie zadziaa.
Aktywno moemy wywoa tu po jej zarejestrowaniu w pliku AndroidManifest.xml:
<activity android:name=".ViewAnimationActivity"
android:label="Aktywno testowa animacji widoku">
Po przeprowadzeniu procesu rejestracji moemy wywoa aktywno animacji widoku za pomoc dowolnego przycisku menu w aplikacji, korzystajc z poniszego fragmentu kodu:
Intent intent = new Intent(this, ViewAnimationActivity.class);
startActivity(intent);
Dodawanie animacji
W tym wiczeniu naszym celem jest dodanie animacji do widoku ListView, widocznego na
rysunku 16.5. W tym celu potrzebujemy klasy wywodzcej si z pakietu android.view.
animation.Animation. Nastpnie musimy przesoni metod applyTransformation, aby
mona byo zmodyfikowa macierz transformacji. Nazwijmy t klas ViewAnimation. Po jej
utworzeniu moemy przeprowadzi w klasie ListView nastpujc czynno:
ListView lv = (ListView)this.findViewById(R.id.list_view_id);
lv.startAnimation(new ViewAnimation());
537
{
final Matrix matrix = t.getMatrix();
matrix.setScale(interpolatedTime, interpolatedTime);
}
}
Metoda zwrotna initialize informuje nas o wymiarach widoku. W niej s rwnie inicjalizowane wszelkie parametry animacji. W naszym przykadzie skonfigurowalimy czas trwania
na 2500 milisekund (2,5 sekundy). Sprawimy take, e wynik kocowy animacji pozostanie
niezmieniony po jej zakoczeniu, a to za spraw przypisania parametrowi FillAfter wartoci
true. W dodatku okrelilimy, e nasz interpolator jest liniowy, co oznacza, e animacja zmienia si stopniowo od pocztku do koca. Wszystkie wymienione waciwoci pochodz z bazowej klasy android.view.animation.Animation.
Cz zasadnicza animacji jest przeprowadzana w metodzie applyTransformation. Szkielet
Androida bdzie j bez przerwy wywoywa w celu symulowania animacji. Za kadym wywoaniem tej metody zmienia si warto parametru interpolatedTime. Zmienia si ona w zakresie od
0 do 1 w zalenoci od tego, w jakim momencie si znajdujemy podczas 2,5-sekundowego
cyklu animacji, ustawionego na etapie jej inicjalizacji. Kiedy warto parametru interpolatedTime
wynosi 1, znajdujemy si na kocu animacji.
Naszym kolejnym zadaniem jest zmiana macierzy transformacji, dostpnej poprzez obiekt
transformacji t, umieszczony w metodzie applyTransformation. Najpierw naley uzyska
dostp do macierzy i zmieni jej wartoci. Po narysowaniu nowego widoku zadziaa rwnie
zmodyfikowana macierz. W dokumentacji interfejsw API dotyczcej klasy android.graphics.
Matrix mona znale opis wielu metod dostpnych w obiekcie Matrix:
http://developer.android.com/reference/android/graphics/Matrix.html
W kodzie z listingu 16.16 zmian macierzy transformacji zajmuje si poniszy wiersz:
matrix.setScale(interpolatedTime, interpolatedTime);
Metoda setScale zawiera dwa parametry s to wspczynniki skali w osiach x oraz y. Poniewa wartoci parametru interpolatedTime mieszcz si w zakresie od 0 do 1, mona go
zastosowa bezporednio w postaci wspczynnika skali.
Zatem na pocztku animacji wspczynnik ten wynosi 0 w obydwu kierunkach. W poowie przebiegu animacji osie x oraz y bd miay warto 0,5. Po zakoczeniu animacji widok bdzie mia
peny rozmiar, poniewa obydwa wspczynniki skali bd miay wartoci rwne 1. W wyniku tego
widok ListView jest na pocztku animacji niewielki i powiksza si do standardowego rozmiaru.
Na listingu 16.17 zosta zaprezentowany kompletny kod rdowy aktywnoci ViewAnimation
Activity zawierajcej animacj.
Listing 16.17. Kod rdowy aktywnoci animacji widoku wraz z animacj
public class ViewAnimationActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.list_layout);
setupListView();
539
setDuration(2500);
setFillAfter(true);
setInterpolator(new LinearInterpolator());
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
matrix.setScale(interpolatedTime, interpolatedTime);
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
Taki wzorzec metod pre i post jest stosowany bardzo czsto. Podobne wyniki mona osign
za pomoc innych metod klasy Matrix, ta technika jest jednak najpopularniejsza a do tego
jest zwiza. Pozostae techniki rwnie zostan omwione pod koniec rozdziau.
Co waniejsze, klasa Matrix umoliwia nie tylko skalowanie widoku, lecz rwnie przenoszenie
go za pomoc metod translate oraz zmian jego orientacji za pomoc metod rotate. Mona
sprawdzi te metody i przekona si, jak wygldaj ich efekty. W rzeczywistoci wszystkie
animacje omwione w podrozdziale Animacja ukadu graficznego s implementowane wewntrznie za pomoc metod klasy Matrix.
Metoda ta powoduje, e obiekt camera przesuwa si w taki sposb, i przy wartoci 0 parametru interpolatedTime (pocztek animacji) warto z bdzie wynosia 1300. Podczas trwania
animacji warto z bdzie systematycznie malaa a do samego koca, gdy warto parametru
interpolatedTime wyniesie 1, a tym samym warto parametru z bdzie rwna 0.
Metoda camera.rotateY(360 * interpolatedTime) wykorzystuje moliwo obracania bryy
w trjwymiarze wok wybranej osi przez obiekt camera. Na pocztku animacji jej warto
wynosi 0. Na kocu animacji przybierze warto 360.
Metoda camera.getMatrix(matrix) pobiera operacje dotychczas wykonane na obiekcie
Camera i narzuca je przekazanej macierzy transformacji. W tym momencie klasa matrix posiada wszystkie translacje potrzebne do uzyskania kocowego efektu, zapewnione przez klas
Camera. Teraz klasa Camera schodzi z widoku (niezamierzona gra sw), poniewa w macierzy
zostay zaimplementowane wszystkie niezbdne operacje. Wykonujemy teraz operacje pre i post
w celu przesunicia rodka widoku i sprowadzenia go z powrotem. Na koniec przywracamy
obiekt Camera do pierwotnego, uprzednio zachowanego stanu.
Po wstawieniu kodu do naszego przykadu zobaczymy kontrolk ListView zbliajc si ze
rodka widoku w stron uytkownika, przy okazji wirujc, dokadnie tak jak zaplanowalimy.
541
jest:
Matrix matrixPreTranslate = new Matrix();
matrixPreTranslate.setTranslate(-centerX, -centerY);
Matrix matrixPostTranslate = new Matrix();
matrixPostTranslate.setTranslate(cetnerX, centerY);
matrix.concat(matrixPreTranslate,matrix);
matrix.postTranslate(matrix,matrixPostTranslate);
Odnoniki
Poniej prezentujemy przydatne odnoniki do materiaw, dziki ktrym jeszcze lepiej zrozumiemy koncepcje zawarte w tym rozdziale:
http://developer.android.com/reference/android/view/animation/package-summary.html
znajdziemy tu rnorodne interfejsy zwizane z animacj, w tym rwnie
interpolatory.
543
http://developer.android.com/guide/topics/resources/animation-resource.html
omwienie znacznikw XML stosowanych w rnych odmianach animacji.
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu projekt do pobrania,
przygotowany specjalnie do tego rozdziau. Jest to plik umieszczony w katalogu
ProAndroid3_R16_Animacje.
Podsumowanie
W tym rozdziale zaprezentowalimy ciekawy sposb uatrakcyjnienia interfejsu uytkownika poprzez zastosowanie animacji. Omwilimy wszystkie podstawowe typy animacji obsugiwane
w Androidzie: animacj poklatkow, animacj ukadu graficznego oraz animacj widoku. Opisalimy take dodatkowe pojcia dotyczce animacji, midzy innymi interpolatory i macierze
transformacji.
Skoro Czytelnik pozna ju podstawy, proponujemy przejrze przykadowe interfejsy API, udostpnione w zestawie Android SDK, aby przeanalizowa pliki XML definiujce rne typy animacji. Poruszymy jeszcze temat animacji w rozdziale 20., powiconym rysowaniu i animowaniu za pomoc technologii OpenGL. Natomiast w rozdziale 29. moemy si zapozna
z oglnym omwieniem animacji opartej na waciwociach, stosowanej wraz z fragmentami.
R OZDZIA
17
Analiza usug
wykorzystujcych mapy
i dane o lokalizacji
W niniejszym rozdziale zajmiemy si usugami systemu Android wykorzystujcymi mapy oraz dane o lokalizacji urzdzenia. S to jedne z najciekawszych elementw zestawu Android SDK. Ta cz rodowiska SDK zawiera interfejsy API
umoliwiajce projektantom aplikacji wywietlanie map oraz manipulowanie
nimi, a take uzyskiwanie informacji o lokalizacji urzdzenia w czasie rzeczywistym. Interfejsy te posiadaj wiele innych fascynujcych funkcji.
Usugi wykorzystujce dane o lokalizacji urzdzenia skadaj si z dwch zasadniczych czci: interfejsw API map oraz interfejsw API lokalizacji. Kady z tych interfejsw jest umieszczony w oddzielnym pakiecie. I tak pakiet map to com.google.
android.maps, natomiast pakiet lokalizacji nosi nazw android.location. Interfejsy
map zawieraj narzdzia pozwalajce na wywietlanie map oraz manipulowanie
nimi. Mona na przykad przyblia oraz przesuwa widok, zmienia tryb wywietlania mapy (z widoku satelitarnego na widok ulic), nakada na map wasne informacje i tak dalej. Z drugiej strony dostpne s dane systemu GPS (ang. Global System
Positioning globalny system ustalania pooenia geograficznego) oraz informacje
o pooeniu geograficznym uzyskiwane w czasie rzeczywistym, ktre s dostarczane
poprzez pakiet lokalizacji.
Interfejsy te czsto cz si poprzez internet z usugami dostpnymi na serwerach
firmy Google. Zatem jeeli te usugi maj dziaa, zazwyczaj trzeba zapewni dostp
do internetu. Ponadto naley zaakceptowa warunki korzystania z usug, zanim bdzie mona rozpocz projektowanie aplikacji uywajcych interfejsw API tych
usug. Naley uwanie przeczyta te warunki; firma Google ogranicza zastosowanie
danych usug. Na przykad mona udostpni informacje o pooeniu dla uytku
osobistego uytkownikw, jednak pewne zastosowania komercyjne, jak rwnie
aplikacje wspomagajce zautomatyzowan kontrol pojazdw s zabronione. Warunki korzystania z usug zostan wywietlone podczas procesu rejestracji w celu
uzyskania klucza interfejsu API.
547
eby uzyska skrt MD5, moemy uruchomi narzdzie keytool wraz z opcj list, zgodnie
z poniszym poleceniem:
keytool -list -alias androiddebugkey keystore "PENA CIEKA PLIKU
debug.keystore" storepass android -keypass android
Warto zauway, e parametr alias testowego magazynu kluczy posiada warto android
debugkey. Haso do magazynu kluczy brzmi android, podobnie jak haso klucza prywatnego.
Po uruchomieniu powyszego polecenia aplikacja keytool wywietli skrt (rysunek 17.2).
Rysunek 17.2. Dane wyjciowe opcji list w narzdziu keytools (skrt MD5 zosta celowo zamazany)
Teraz wklejamy uzyskany skrt MD5 certyfikatu testowego w odpowiednie pole na stronie Google:
http://code.google.com/android/maps-api-signup.html
Nastpnie trzeba przeczyta warunki korzystania z usug. Jeeli s do zaakceptowania, naley
wcisn przycisk Generate API Key, aby uzyska klucz interfejsu API map dla usugi Google
Maps, odpowiadajcy skrtowi MD5. Klucz ten jest od razu aktywny, zatem mona ju teraz
za jego pomoc uzyska dostp do danych map z serwisu Google. Pamitajmy, e do uzyskania
klucza wymagane jest posiadanie konta Google podczas prby wygenerowania klucza zostanie wywietlony monit o zalogowanie si w serwisie Google.
Przypominamy informacje z rozdziau 10. Napisalimy tam, e jeli certyfikat testowy utraci
wano, to samo spotka uywany w tym czasie klucz interfejsu map. Jeeli zmienimy certyfikat
testowy, bdziemy musieli powtrzy powysze kroki i za pomoc nowego certyfikatu uzyska
549
Na rysunku 17.3 zaprezentowano aplikacj wywietlajc map w trybie widoku ulic. Wida take,
w jaki sposb mona zmienia skal mapy oraz jak zmienia jej tryb wywietlania. Ukad graficzny XML tej aplikacji jest ukazany na listingu 17.2.
Na kocu rozdziau zamieszczamy adres URL, pod ktrym moemy pobra projekty
przygotowane dla tego rozdziau. Bdziemy mogli dziki temu bezporednio
zaimportowa te projekty do rodowiska Eclipse.
Listing 17.2. Ukad graficzny XML aplikacji demonstracyjnej MapViewDemo
<?xml version="1.0" encoding="utf-8"?>
Jak wida na listingu 17.2, nadrzdny meneder LinearLayout zawiera podrzdny meneder
LinearLayout oraz widok MapView. W podrzdnym menederze LinearLayout zostay umieszczone przyciski, widoczne u gry ekranu na rysunku 17.3. Naley rwnie pamita, eby zaktualizowa warto parametru android:apiKey wartoci swojego klucza interfejsu API mapy.
Kod naszej przykadowej aplikacji obsugujcej mapy zosta umieszczony na listingu 17.3.
Listing 17.3. Klasa rozszerzajca MapActivity, wczytujca ukad graficzny XML
// Jest to plik MapViewDemoActivity.java
import android.os.Bundle;
import android.view.View;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class MapViewDemoActivity extends MapActivity
{
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
mapView = (MapView)findViewById(R.id.mapview);
}
public void myClickHandler(View target) {
switch(target.getId()) {
case R.id.zoomin:
mapView.getController().zoomIn();
break;
case R.id.zoomout:
mapView.getController().zoomOut();
551
break;
case R.id.sat:
mapView.setSatellite(true);
break;
case R.id.street:
mapView.setStreetView(true);
break;
case R.id.traffic:
mapView.setTraffic(true);
break;
case R.id.normal:
mapView.setSatellite(false);
mapView.setStreetView(false);
mapView.setTraffic(false);
break;
}
Na listingu 17.3 wida, e wywietlanie widoku MapView za pomoc metody onCreate() nie
rni si od wywietlania innych typw kontrolek: konfigurujemy widok treci interfejsu uytkownika w pliku ukadu graficznego zawierajcego widok MapView, a pozostae zadania s
wykonywane przez ten widok automatycznie. O dziwo, obsuga funkcji zmiany skali mapy jest
rwnie bardzo prosta. Aby zmieni skal mapy wywietlanej w widoku, uywa si klasy
MapController widoku MapView. Najpierw wywoujemy metod mapView.getController(),
a nastpnie stosujemy metod zoomIn() dla zmniejszania skali i metod zoomOut() dla jej
zwikszania. Taki sposb pracy jest jednopoziomowy; uytkownik musi wielokrotnie powtarza czynno, aby dalej zwiksza lub zmniejsza skal ogldanej mapy.
Rwnie prosto mona zmieni tryb wywietlania mapy. Widok MapView obsuguje kilka trybw:
Domylny jest widok standardowy mapy.
W trybie widoku ulic zostaje naoona na map dodatkowa warstwa, dziki ktrej
mona oglda zdjcia ulic zaznaczonych niebieskim obrysem. Zdjcia te zostay
wykonane za pomoc kamer, ktre umieszczono na pojazdach poruszajcych si po
tych ulicach. Naley jednak pamita, e sama kontrolka MapView nie wywietla zdj
w widoku ulic. Do tego jest potrzebna odrbna kontrolka widoku. Temat ten
zostanie dokadniej omwiony w rozdziale 25.
W trybie satelitarnym s wywietlane rzeczywiste zdjcia lotnicze naoone na mapy,
dziki ktrym mona obserwowa dachy budynkw, korony drzew, drogi i tak dalej.
Aby zmienia tryby, naley wywoa waciw metod ustawiajc, podajc warto true.
W pewnych przypadkach ustawienie jednego trybu wyczy inny tryb. Na przykad nie mona
jednoczenie ustawi trybu widoku ulic z trybem ruchu ulicznego w takim przypadku po
wybraniu trybu ruchu ulicznego tryb widoku ulic zostanie automatycznie wyczony. eby
wyczy dany tryb, naley przydzieli mu warto false. Wkrtce zajmiemy si omawianiem
klasy Overlay, teraz jednak wystarczy wiedzie, e tryb ruchu ulicznego oraz tryb widoku ulic
nie korzystaj z tych obiektw.
Instrukcja mapView.postInvalidateDelayed(2000) umoliwia uniknicie problemu
wynikajcego z korzystania z trybw widoku ulic oraz ruchu ulicznego. Problem ten
wie si z wewntrznym wykorzystywaniem wtkw do pobierania danych pozwalajcych
na wywietlanie linii w tych dwch trybach. Wicej informacji znajdziemy na stronie
http://code.google.com/p/android/issues/detail?id=10317, dotyczcej problemu
numer 10317.
Aby przesuwa map, naley dla widoku MapView w pliku XML skonfigurowa atrybut
android:clickable="true" w przeciwnym wypadku bdzie mona wycznie zmniejsza
i zwiksza skal mapy. W kodzie Java mona tego dokona za pomoc wywoania metody
setClickable(true) w widoku MapView.
Ostatnia rzecz, ktr mona powiedzie o naszym przykadowym projekcie, dotyczy dwch
metod: isLocationDisplayed() i isRouteDisplayed(). W dokumentacji tych metod widnieje informacja, e s one wymagane przez warunki korzystania z usug firmy Google, chocia w trakcie uzyskiwania klucza interfejsu map nie ma w zatwierdzanym dokumencie
adnej wzmianki na ich temat. Nie jestemy prawnikami, zalecamy jednak uwzgldnienie tych
metod. Aplikacja musi przekazywa serwerowi map wartoci true lub false w celu okrelenia, czy pooenie geograficzne urzdzenia jest wywietlane lub czy s wywietlane informacje o trasie, na przykad wyznaczanie trasy samochodowej.
Przyznajemy, e w Androidzie ilo kodu potrzebnego do wywietlenia mapy oraz zaimplementowania funkcji zmiany skali i trybw przegldania jest minimalna (listing 17.3). Istnieje
jednak jeszcze prostszy sposb wstawienia kontrolek sucych do zmieniania skali mapy.
Przyjrzyjmy si plikowi XML ukadu graficznego oraz kodowi na listingu 17.4.
Listing 17.4. Prostsza zmiana skali wywietlanej mapy
<?xml version="1.0" encoding="utf-8"?>
553
android:layout_height="wrap_content"
android:clickable="true"
android:apiKey="TUTAJ UMIESZCZAMY KLUCZ INTERFEJSU API MAPY"
/>
</RelativeLayout>
public class MapViewDemoActivity extends MapActivity
{
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
mapView = (MapView)findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
}
@Override
protected boolean isLocationDisplayed() {
return false;
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}
Rnica pomidzy listingami 17.4 a 17.3 polega na przeksztaceniu ukadu graficznego naszego
widoku w taki sposb, eby wykorzystywa meneder RelativeLayout. Usunlimy wszystkie
kontrolki odpowiedzialne za zmian skali oraz trybu wywietlania mapy. Caa magia znajduje
si w kodzie Java, a nie w ukadzie graficznym. Kontrolka MapView zawiera ju kontrolki umoliwiajce zmian skali ogldanej mapy. Wystarczy je wczy za pomoc metody setBuilt
InZoomControls(). Na rysunku 17.4 zostay pokazane domylne kontrolki zmiany skali mapy
w widoku MapView.
Teraz dowiedzmy si, w jaki sposb dodawa wasne dane do mapy.
android.graphics.Canvas;
android.graphics.drawable.Drawable;
android.os.Bundle;
android.widget.LinearLayout;
import
import
import
import
import
com.google.android.maps.GeoPoint;
com.google.android.maps.ItemizedOverlay;
com.google.android.maps.MapActivity;
com.google.android.maps.MapView;
com.google.android.maps.OverlayItem;
555
Na listingu 17.5 pokazujemy, w jaki sposb mona nakada znaczniki na map. W przykadzie
tym zaznaczylimy dwa miejsca: Magiczne Krlestwo Disneya (ang. Magic Kingdom) oraz Lagun Siedmiu Mrz (ang. Seven Seas Lagoon). Obydwa miejsca znajduj si w pobliu miasta
Orlando na Florydzie (rysunek 17.5).
Aby nasza aplikacja demonstracyjna zadziaaa, musimy wprowadzi obiekt rysowany, ktry
posuy nam za znacznik. Obraz ten musi zosta zachowany w folderze /res/drawable
w taki sposb, eby identyfikator tego zasobu w metodzie getDrawable() odpowiada
nazwie pliku obrazu. W razie moliwoci postarajmy si, aby obszar otaczajcy znacznik
by przezroczysty. Kilka przykadowych znacznikw znajdziemy w zasobach projektu
utworzonego na potrzeby tego rozdziau.
557
Aby dodawa znaczniki do mapy, naley dla niej utworzy i doda do niej rozszerzenie klasy
com.google.android.maps.Overlay. Nie mona utworzy samej klasy Overlay, zatem naley
j rozszerzy lub skorzysta z jednego z rozszerze. W naszym przykadzie zaimplementowalimy klas InterestingLocations rozszerzajc klas ItemizedOverlay, ktra z kolei rozszerza klas Overlay. Klasa Overlay definiuje kontrakt dla nakadki, a klasa ItemizedOverlay
stanowi jej przydatn implementacj, pozwalajc na atwe utworzenie listy lokalizacji, ktre
mog zosta nastpnie zaznaczone na mapie.
Standardowym algorytmem dziaania jest rozszerzenie klasy ItemizedOverlay oraz dodanie
elementw miejsc wartych odwiedzenia do konstruktora. Po utworzeniu takich miejsc
mona wywoa metod populate() w klasie ItemizedOverlay. Metoda populate() zapisuje
w pamici podrcznej element bd elementy OverlayItem. Klasa ItemizedOverlay wywouje
wewntrznie metod size() suc do okrelenia liczby nakadanych elementw, a nastpnie wchodzi w ptl, wywoujc metod createItem(i) wobec kadego elementu. Metoda
createItem przekazuje gotowy element, utworzony na podstawie indeksu tablicy.
Jak wida na listingu 17.5, tworzymy po prostu punkty i wywoujemy metod populate(),
aby wywietli znaczniki na mapie. Kontrakt klasy Overlay wykonuje pozosta cz pracy. Aby
to zadziaao, metoda onCreate() aktywnoci tworzy wystpienie klasy InterestingLocations
i przekazuje obiekt rysowany, ktry bdzie wywietlany jako znaczniki. Nastpnie metoda
onCreate() dodaje wystpienie InterestingLocations do zbioru nakadek (mapView.get
Overlays().add()).
Wybrany przez nas obiekt klasy Drawable musi by przystosowany do korzystania z nakadki
ItemizedOverlay. Interfejs API map musi otrzyma wsprzdne punktu (0,0) obiektu
Drawable. Punkt ten bdzie definiowa dokadne miejsce na mapie, reprezentowane przez
znacznik. Moemy tego sami dokona za pomoc metody setBounds() klasy Drawable, ktra
zostaa przedstawiona w powyszym przykadzie. Argumenty reprezentuj wsprzdne lewej,
prawej, grnej i dolnej krawdzi; mona rwnie wykorzysta metody getIntrinsicHeight()
oraz getIntrinsicWidth() do okrelenia, jak wysoki i szeroki jest obiekt Drawable.
w rozdziale 25., w trakcie omawiania ekranw dotykowych, zademonstrujemy jeszcze take ich
inne, ciekawe moliwoci.
Gdy nakadka jest ju powizana z map, musimy jeszcze zdefiniowa waciw pozycj na mapie,
eby znaczniki byy widoczne na ekranie. W tym celu trzeba wyznaczy interesujcy nas punkt
jako rodek ekranu. Metoda getCenter() nakadki przekazuje wsprzdne pierwszego punktu,
a nie punktu rodkowego, jak mona by si spodziewa. Nakadka ItemizedOverlay sortuje
okrelone wczeniej punkty i wskazuje pierwszy z nich. Zatem w celu odnalezienia punktu
rodkowego implementujemy wasn metod getCenterPt(), ktra bdzie iterowaa przez
punkty i wyszukiwaa punkt rodkowy. Metoda setCenter() kontrolera widoku mapy okrela
rodek wywietlanego elementu, trzeba tylko przekaza jej obliczony punkt rodkowy.
Dziki metodzie setZoom() klasy MapController okrelamy skal ogldanej mapy. Metoda ta
przyjmuje wartoci od 1 do 21, gdzie 21 oznacza najmniejsz moliw skal, a 1 najwiksz.
Poniewa jednak nie wiemy dokadnie, jaka skala bdzie potrzebna, aby wywietli cay interesujcy uytkownika zakres mapy na ekranie, wprowadzamy metod zoomToSpan() klasy
MapController. Trzeba wprowadzi wysoko i szeroko prostokta, w ktrym zostanie wywietlona mapa. Na szczcie nakadka ItemizedOverlay posiada dwie metody pozwalajce
nam na okrelenie tych wymiarw: getLatSpanE6() przekazuje zakres szerokoci geograficz-
559
Geokodowanie w Androidzie
Jeeli mapy maj by wykorzystywane w jaki praktyczny sposb, prawdopodobnie naley
przekonwertowa adres (lub lokacj) do wsprzdnych geograficznych. Pojcie to jest znane
jako geolokalizacja, a klasa android.location.Geocoder posiada t funkcj. W rzeczywistoci
klasa Geocoder umoliwia konwersj w obydwie strony moe przeksztaci adres do wsprzdnych geograficznych oraz przetumaczy par szeroko dugo geograficzna na list
adresw. Klasa ta posiada nastpujce metody:
List<Address> getFromLocation(double latitude, double longitude,
int maxResults),
android.location.Address;
android.location.Geocoder;
android.os.Bundle;
android.view.View;
android.widget.Button;
android.widget.EditText;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class GeocodingDemoActivity extends MapActivity
{
Geocoder geocoder = null;
MapView mapView = null;
@Override
protected boolean isLocationDisplayed() {
return false;
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.geocode);
mapView = (MapView)findViewById(R.id.geoMap);
mapView.setBuiltInZoomControls(true);
561
Aby sprawdzi zastosowanie geokodowania w Androidzie, wpiszmy nazw lokacji lub jej adres
w polu EditText, a nastpnie kliknijmy przycisk Znajd lokacj. eby odnale adres lokacji,
wywoujemy metod getFromLocationName() klasy Geocoder. Lokacja moe by adresem lub
znan nazw (w naszym przypadku White House, czyli Biay Dom). Geokodowanie moe by
czasochonnym procesem, dlatego proponujemy ograniczy liczb wynikw do piciu, zgodnie
z sugesti dokumentacji Androida.
Po wywoaniu metody getFromLocationName() otrzymujemy list adresw. Nasza przykadowa
aplikacja pobiera list adresw i, jeeli zostanie jaki znaleziony, przetwarza pierwszy z nich.
Kady adres posiada wsprzdne szerokoci i dugoci geograficznej, suce do utworzenia
obiektu GeoPoint. Nastpnie otrzymujemy dostp do kontrolera mapy i przenosimy si do
wskazanego punktu. Warto poziomu przyblienia jest liczb cakowit w zakresie wartoci
od 1 do 21. Z kadym krokiem nastpuje dwukrotne przyblienie. Moglibymy zaprezentowa
okno dialogowe wywietlajce jednoczenie wiele poszukiwanych lokacji, na razie jednak pokaemy tylko przypadek zawierajcy jedno znalezione miejsce.
W naszej przykadowej aplikacji odczytujemy jedynie dugo i szeroko geograficzn zwracanego obiektu Address. W rzeczywistoci moemy otrzyma ogrom danych powizanych z obiektem tego typu, wcznie ze zwyczajow nazw miejsca, adresem, miastem, prowincj, kodem
pocztowym, pastwem, a nawet numerem telefonu i adresem URL.
W przeciwiestwie do interfejsu map, usugi geolokalizacyjne nie korzystaj z mikrostopni.
Czst przyczyn bdw jest pozostawianie nieskonwertowanych jednostek. eby
przekaza wartoci dugoci i szerokoci geograficznej obiektu Address do metody
interfejsu map, musimy je najpierw pomnoy przez 1 000 000.
563
android.app.AlertDialog;
android.app.Dialog;
android.app.ProgressDialog;
android.location.Address;
android.location.Geocoder;
android.os.Bundle;
android.os.Handler;
android.os.Message;
android.view.View;
android.widget.EditText;
import com.google.android.maps.GeoPoint;
565
}
}
};
thrd.start();
}
Kod na listingu 17.7 jest zmodyfikowan wersj przykadu z listingu 17.6. Rnica polega na
tym, e w metodzie onClick() wywietlamy okno dialogowe postpw i wywoujemy metod
findLocation() (rysunek 17.7). Metoda findLocation() tworzy nastpnie nowy wtek i wywouje metod start(), czego ostatecznym wynikiem jest wywoanie metody run() tego wtku.
W tej metodzie wykorzystujemy klas Geocoder do znalezienia szukanej lokalizacji. Po zakoczeniu wyszukiwania musimy wysa komunikat do obiektu, ktry moe nawiza interakcj z wtkiem interfejsu UI, poniewa musimy zaktualizowa map. Do tego celu suy
klasa android.os.Handler. Z poziomu wtku przebiegajcego w tle wywoujemy metod
uiCallback.sendEmptyMessage(0), aby wtek interfejsu UI mg przetwarza wyniki wyszukiwania. W naszym przypadku nie musimy tak naprawd przesya adnej treci w komunikacie,
poniewa dane s wspdzielone w obiekcie addressList. Kod wywouje nastpnie procedur
metody zwrotnej, ktra usuwa okno dialogowe, a nastpnie sprawdza list addressList zwrcon przez klas Geocoder. Poprzez wywoanie zwrotne mapa zostaje nastpnie zaktualizowana
o wyniki wyszukiwania lub zostaje wywietlony alert informujcy o braku wynikw wyszukiwania. Interfejs uytkownika dla tego przykadu zosta zilustrowany na rysunku 17.7.
Usuga LocationManager
Usuga LocationManager jest jedn z najwaniejszych funkcji oferowanych przez pakiet android.
location. Dziki niej dostpne staj si dwie funkcje: mechanizm pozwalajcy uzyska wsprzdne geograficzne urzdzenia oraz technologia informujca (poprzez intencj) o tym, e
urzdzenie znalazo si w okrelonym rejonie geograficznym.
W tym punkcie opiszemy tajniki dziaania usugi ServiceManager. eby korzysta z tej usugi,
naley najpierw utworzy do niej odniesienie. Na listingu 17.8 zosta ukazany wzorzec korzystania z tej usugi.
Listing 17.8. Korzystanie z usugi ServiceManager
package com.androidbook.maps.locationmanager;
import java.util.List;
import
import
import
import
import
android.app.Activity;
android.content.Context;
android.location.Location;
android.location.LocationManager;
android.os.Bundle;
567
(LocationManager)this.getSystemService(Context.LOCATION_SERVICE);
Location loc = locMgr.getLastKnownLocation(LocationManager.GPS_PROVIDER);
List<String> providerList = locMgr.getAllProviders();
}
}
569
tw, ktre wzito pod uwag podczas obliczenia wsprzdnych urzdzenia. Takie dodatkowe
informacje mog, ale nie musz by dostpne. Aby si dowiedzie, czy obiekt klasy Location
posiada jedn z takich wartoci, w klasie tej naley ustawi metody typu has(), ktre przekazuj warto logiczn. Przykadem moe by metoda hasAccuracy(). Przed otrzymaniem
wartoci metody getAccuracy() rozsdnie byoby najpierw wywoa metod hasAccuracy().
Location posiada rwnie inne przydatne metody, na przykad metod statyczn
distanceBetween(), dziki ktrej moemy otrzyma najkrtsz odlego pomidzy dwoma
obiektami Location. Inn metod zwizan z odlegoci jest distanceTo(), ktra bdzie
przekazywaa najmniejsz warto dystansu pomidzy biecym obiektem Location a obiektem Location przekazanym metodzie. Zwrmy uwag, e odlego jest mierzona w metrach
Klasa
oraz e pod uwag brana jest krzywizna Ziemi. Musimy te jednak mie wiadomo, e odlegoci podawane w wyniku dziaania omawianych metod nie s mierzone na przykad z perspektywy jazdy samochodem.
Jeeli potrzebujemy wskazwek dla kierowcw lub dystansu midzy dwoma punktami mierzonego jak dla jazdy samochodem, bd nam potrzebne pocztkowy i kocowy obiekt Location,
jednak do przeprowadzenia wszystkich oblicze prawdopodobnie bdziemy musieli wykorzysta usugi Google Maps JavaScript API. Przydatny moe si okaza na przykad interfejs Google
Directions, podobny nieco do omwionego w rozdziale 11. interfejsu Google Translate. Umoliwia
on aplikacji ukazanie caej trasy przejazdu od punktu pocztkowego do punktu kocowego.
android.app.Activity;
android.content.Context;
android.location.Location;
android.location.LocationListener;
android.location.LocationManager;
android.os.Bundle;
android.widget.Toast;
0, // minDistance w metrach
locListener);
@Override
public void onPause() {
super.onPause();
locMgr.removeUpdates(locListener);
}
Nie zamiecilimy tutaj kodu interfejsu uytkownika. Standardowy pocztkowy ukad graficzny powinien nam cakowicie wystarczy. Z tego samego powodu nie rozszerzamy klasy
MapActivity, gdy nie bdziemy wywietla adnej mapy.
571
Jednym z gwnych zastosowa usugi LocationManager jest uzyskiwanie powiadomie o pooeniu geograficznym urzdzenia. Na listingu 17.9 pokazalimy, w jaki sposb mona zarejestrowa obiekt nasuchujcy odbierajcy zdarzenia uaktualniania pozycji. W celu zarejestrowania
obiektu nasuchujcego wywoujemy metod requestLocationUpdates() i przekazujemy jej nazw dostawcy jako parametr. Podczas zmiany pooenia geograficznego usuga LocationManager
wywouje metod onLocationChanged() obiektu nasuchujcego wraz z danymi o nowej lokalizacji. Bardzo wane jest, aby usun we waciwym czasie wszystkie rejestracje aktualizacji
pooenia. W naszym przykadzie rejestrujemy metod onResume() i usuwamy t rejestracj
w metodzie onPause(). Jeeli nie bdziemy mieli zamiaru korzysta z aktualizacji pooenia,
powinnimy powiadomi dostawc, eby ich nie przesya. Istnieje rwnie prawdopodobiestwo, e aktywno zostanie zakoczona (na przykad w przypadku obrcenia urzdzenia
i ponownego uruchomienia aktywnoci), co oznacza, e wczeniejsza aktywno moe cigle
istnie, otrzymywa aktualizacje, wywietla je w obiekcie Toast oraz zajmowa pami.
W naszym przykadzie ustalamy wartoci minimalnego czasu oraz minimalnego dystansu na 0.
W ten sposb usuga LocationManager bdzie przesyaa informacje jak najczciej. W rzeczywistej aplikacji nie byyby to zalecane ustawienia, ale dziki nim wersje testowe bd dziaay
lepiej (jest niewskazane, aby fizyczne urzdzenie zbyt czsto sprawdzao biece pooenie
geograficzne, poniewa czynno ta wyczerpuje bateri). Naley te parametry ustawi w sposb odpowiedni do sytuacji, starajc si zminimalizowa liczb powiadomie o zmianie pooenia geograficznego.
Na listingu 17.9 zostao zaprezentowane nowe narzdzie widet Toast. Jest to przydatna
aplikacja, pozwalajca na krtki czas wywietli may widok uytkownikowi. Zasania on na
moment widok biecy, a nastpnie sam znika. Jego czas trwania mona wyduy, stosujc
zamiast argumentu LENGTH_SHORT argument LENGTH_LONG.
eby przetestowa nasz przykad na emulatorze, moemy skorzysta z umieszczonego we wtyczce
ADT interfejsu DDMS (ang. Dalvik Debug Monitor Service usuga monitora sprawdzania
bdw Dalvik). Interfejs DDMS posiada ekran, za pomoc ktrego moliwe jest emulowanie
zmieniajcej si lokalizacji (rysunek 17.8).
Mona wczyta plik GPX lub KML do emulatora oraz skonfigurowa szybko, z jak bdzie
on odtwarza te pliki (rysunek 17.9). W efekcie emulator bdzie przesya aktualizacje lokalizacji
do aplikacji, bazujc na ustalonej szybkoci symulowanego poruszania si. Jak wida na rysunku
17.9, plik GPX posiada w grnej czci ekranu punkty, a w dolnej czci cieki. Nie mona
odtwarza punktw, jednak po klikniciu jednego z nich zostanie on wysany do emulatora.
Po klikniciu cieki stanie si dostpny przycisk Play, sucy do odtwarzania punktw.
Doszy do nas informacje, e nie wszystkie pliki GPX s odczytywane przez emulator.
Jeeli po wczytaniu pliku GPX nic si nie dzieje, naley sprbowa wykorzysta inny
plik z innego rda.
Na listingu 17.9 znajduje si jeszcze kilka metod klasy LocationManager, o ktrych do tej pory
nie wspomnielimy. S to metody zwrotne onProviderDisabled(), onProviderEnabled()
oraz onStatusChanged(). W naszym przykadzie nie wykorzystalimy ich potencjau, jednak
w prawdziwej aplikacji bdziemy za ich pomoc powiadamiani o dostpnoci dostawcy pooenia, na przykadu systemu GPS, dla uytkownika lub o zmianach stanu dostawcy w czasie.
573
Rysunek 17.9. Wczytywanie plikw GPX oraz KML do emulatora w celu odtworzenia danych
gdzie emulator_numer_portu jest wartoci powizan z wystpieniem uruchomionego urzdzenia AVD, ktra jest podana w pasku tytuowym emulatora. Po podczeniu si do konsoli
moemy wprowadzi polecenie geo fix, aby wysa aktualizacj pooenia. W tym celu stosujemy nastpujc skadni (wysoko jest opcjonalna):
geo fix dlugosc szerokosc [ wysokosc ]
package com.androidbook.location.myoverlay;
import
import
import
import
import
android.os.Bundle;
com.google.android.maps.MapActivity;
com.google.android.maps.MapController;
com.google.android.maps.MapView;
com.google.android.maps.MyLocationOverlay;
575
Zwrmy uwag, e w tym przykadzie metoda isLocationDisplayed() bdzie przekazywaa warto true, w przypadku gdy na mapie bdzie wywietlane pooenie geograficzne
urzdzenia.
577
Pamitajmy, e w naszej aplikacji metoda onResume() bdzie wywoywana nawet podczas jej
uruchamiania, jak rwnie po wyjciu ze stanu wstrzymania. Trzeba zatem powiza namierzanie lokalizacji z metod onResume(), a wyczy je po wywoaniu metody onPause().
Nie ma sensu marnowanie zapasu energii w baterii na dania ustalenia pooenia geograficznego, jeeli nie bd potrzebne. Po wczeniu ledzenia pooenia geograficznego podczas wywoania metody onResume() chcemy od razu uzyska dane o aktualnym pooeniu geograficznym. Klasa myLocationOverlay posiada pomocn w tym przypadku klas runOnFirstFix().
Dziki tej metodzie moemy skonfigurowa kod, ktry zostanie uruchomiony tu po otrzymaniu wsprzdnych geograficznych pooenia urzdzenia. Moe to nastpi natychmiast, poniewa posiadamy wsprzdne ostatniego znanego pooenia, albo nieco pniej, po otrzymaniu informacji od dostawcy GPS_PROVIDER, NETWORK_PROVIDER lub PASSIVE_PROVIDER.
Gdy uzyskamy odpowiednie dane, mapa zostanie zgodnie z nimi wyrodkowana. Nie musimy
robi nic wicej, poniewa klasa MyLocationOverlay bdzie uzyskiwaa uaktualnienia pooenia geograficznego i bdzie w tych miejscach umieszczaa migoczc, niebiesk kropk. Jeeli
kropka ta zbliy si zanadto do krawdzi mapy, mapa automatycznie zostanie wyrodkowana
w taki sposb, e migoczcy punkt znajdzie si w centrum ekranu.
MyCustom
Teraz mona wczy t aplikacj na emulatorze, a nastpnie przesa dane o nowej lokalizacji
poprzez okno Emulator Control. Jeeli wysyamy strumie aktualizacji za pomoc pliku GPX,
stwierdzimy, e niebieski punkt zawsze jest przesuwany na rodek ekranu. Nawet jeli cakowicie oddalimy widok, mapa zostanie wyrodkowana na tym punkcie.
android.app.Activity;
android.app.PendingIntent;
android.content.Intent;
android.content.IntentFilter;
android.location.LocationManager;
android.net.Uri;
android.os.Bundle;
lon = -81.38;
geo = "geo:"+lat+","+lon;
intent = new Intent(PROX_ALERT, Uri.parse(geo));
intent.putExtra("message", "Orlando, Floryda");
pIntent2 = PendingIntent.getBroadcast(getApplicationContext(), 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
locMgr.addProximityAlert(lat, lon, radius, -1L, pIntent2);
proxReceiver = new ProximityReceiver();
579
android.content.BroadcastReceiver;
android.content.Context;
android.content.Intent;
android.location.LocationManager;
android.os.Bundle;
android.util.Log;
Poniewa w rzeczywistoci nie wywietlamy adnej pozycji na mapie, nie korzystamy ani z klasy
MapActivity, ani z bibliotek interfejsu Google Map, ani nie okrelamy adnego celu. Musimy
jednak doda w pliku manifecie uprawnienie android.permission.ACCESS_FINE_LOCATION,
poniewa klasa LocationManager bdzie prbowaa korzysta z dostawcy GPS. W takim
samym stopniu bdzie korzystaa z dostawcy sieciowego, ale skoro wprowadzamy uprawnienie
ACCESS_FINE_LOCATION, wymagania dotyczce uprawnie zostaj spenione. Rejestrujemy
odbiorc BroadcastReceiver w metodzie onCreate(), nie musimy wic go rejestrowa w pliku
manifecie. Gdybymy umiecili odbiorc komunikatw w oddzielnej aplikacji, wtedy zaistniaaby potrzeba jego zdefiniowania w manifecie. Definicja przykadowego kodu z listingu
17.12 zostaa zaprezentowana na listingu 17.13.
581
Poniewa mamy tu do czynienia z nadawanymi komunikatami, nie moemy polega na kolejnoci, w jakiej s otrzymywane. Jeeli na przykad urzdzenie znajduje si wewntrz okrgu
otaczajcego Orlando i nagle przemieci si do okrgu wok Jacksonville, uytkownik moe
otrzyma komunikat, e znajduje si w Jacksonville, jeszcze zanim pojawi si informacje o jego
obecnoci w Orlando.
Poniewa zajmujemy si tu obiektami typu Location, naszym identyfikatorem URI jest schemat geo, ktry jest jednym z lepiej znanych schematw i doskonale si nadaje do przekazywania
informacji o wsprzdnych geograficznych. Zwrmy uwag, e struktura tego identyfikatora
zawiera najpierw szeroko, a dopiero pniej dugo geograficzn, gdy jednak korzystamy
z polecenia geo fix, kolejno wystpowania tych wsprzdnych jest odwrotna. Moe to spowodowa spore problemy, jeeli nie bdziemy ostroni. W efekcie atwo straci mnstwo czasu
na znalezienie rda problemu, podczas gdy wystarczy jedynie zamieni wartoci wsprzdnych sucych do aktualizowania informacji o pooeniu. Zawsze moemy rwnie skorzysta
z plikw GPX lub KML, w ktrych moemy wybiera lokalizacje do testowania, nakadajce si
na utworzone przez nas obszary zainteresowania.
Nasza prbna aplikacja jest bardzo prosta. W rzeczywistym programie odbiorca komunikatw
moe wysya powiadomienia lub uruchamia usug. W przypadku aktywnoci lub usugi
(nawet znajdujcej si w innej aplikacji) zamiast nadawanego komunikatu moemy zamieci
oczekujc intencj. Rwnie zamiast aplikacji moemy korzysta ze wspomnianej ju usugi.
583
Odnoniki
Poniej prezentujemy przydatny odnonik do informacji rozszerzajcych informacje zawarte
w tym rozdziale:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
stworzonych specjalnie na potrzeby ksiki. Projekty dotyczce tego rozdziau
zostay zawarte w katalogu ProAndroid3_R17_Mapy. W katalogu umiecilimy
take plik Czytaj.TXT, w ktrym zawarte s szczegowe instrukcje dotyczce
importowania projektw ze skompresowanych plikw do rodowiska Eclipse.
Podsumowanie
W niniejszym rozdziale omwilimy usugi wykorzystujce mapy oraz dane o lokalizacji. Szczegowo przedstawilimy zastosowanie kontrolki MapView oraz klasy MapActivity. Najpierw
zajlimy si podstawowymi funkcjami mapy, a nastpnie pokazalimy, w jaki sposb mona
wykorzysta nakadki do umieszczania znacznikw na mapie. Przeanalizowalimy nawet proces
geokodowania i przetwarzania tego procesu w wtkach przeprowadzanych w tle. Podalimy
informacje na temat klasy LocationManager, ktra uzyskuje szczegowe informacje o pooeniu
geograficznym za pomoc dostawcw oraz pozwala wywietli biece pooenie urzdzenia na
mapie. Na koniec pokazalimy, w jaki sposb mona wykorzysta alerty odlegociowe.
W nastpnym rozdziale zajmiemy si usugami telefonicznymi w Androidzie.
R OZDZIA
18
Uywanie interfejsw telefonii
android.app.Activity;
android.os.Bundle;
android.telephony.SmsManager;
android.view.View;
android.widget.EditText;
587
import android.widget.Toast;
public class TelephonyDemo extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void doSend(View view) {
EditText addrTxt =
(EditText) findViewById(R.id.addrEditText);
EditText msgTxt =
(EditText) findViewById(R.id.msgEditText);
try {
sendSmsMessage(
addrTxt.getText().toString(),msgTxt.getText().toString());
Toast.makeText(this, "Wiadomo SMS wysana",
Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(this, "Nie udao si wysa wiad. SMS",
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void sendSmsMessage(String address,String message)throws Exception
{
SmsManager smsMgr = SmsManager.getDefault();
smsMgr.sendTextMessage(address, null, message, null, null);
}
}
Na listingu 18.1 pokazano przykad wysyania wiadomoci SMS w rodowisku SDK. Jeli przyjrzymy si fragmentowi kodu ukadu graficznego, zauwaymy dwa pola EditText: w jednym
umieszczamy adres docelowy (numer telefonu) adresata, a w drugim wpisujemy tre wiadomoci. Interfejs uytkownika jest take zaopatrzony w przycisk powodujcy wysanie wiadomoci SMS, co zostao zilustrowane na rysunku 18.1.
Interesujc czci kodu jest metoda sendSmsMessage(). Wykorzystuje ona metod send
TextMessage() klasy SmsManager do wysania wiadomoci SMS. Poniej umiecilimy sygnatur metody SmsManager.sendTextMessage():
sendTextMessage(String destinationAddress, String smscAddress, String textMsg,
PendingIntent sentIntent, PendingIntent deliveryIntent);
W naszym przykadzie wypeniamy jedynie dane adresu docelowego oraz parametry wiadomoci tekstowej. Mona jednak dostosowa metod do wasnych potrzeb, tak eby nie korzystaa
z domylnego centrum usugi SMS (adresu serwera sieci komrkowej przesyajcego wiadomo). Moemy take zaimplementowa konfiguracj pozwalajc na nadanie oczekujcych
intencji po wysaniu (lub nieudanym wysaniu) wiadomoci i dostarczeniu powiadomienia.
Wysyanie wiadomoci SMS skada si z dwch gwnych etapw: wysania wiadomoci i jej
dostarczenia. Jeeli aplikacja to umoliwia, po osigniciu kadego etapu zostaje nadana intencja oczekujca. Moemy w niej umieci cokolwiek zechcemy, na przykad jakie dziaanie, ale
kod wynikowy wysany do odbiorcy komunikatw bdzie niepowtarzalny dla etapu wysyania
i dostarczenia wiadomoci SMS. Ponadto moemy otrzymywa dodatkowe dane zwizane
z bdami transmisji lub raportami o stanie wiadomoci, w zalenoci od sposobu implementacji wysyania wiadomoci.
Przy braku intencji oczekujcych aplikacja nie moe okreli, czy wiadomo zostaa pomylnie wysana. Dlatego musimy jak najwicej testowa. Jeeli wczymy t przykadow aplikacj
w emulatorze i uruchomimy kolejne wystpienie emulatora (bez rnicy, czy z wiersza polece,
czy za pomoc opcji Window/Android SDK and AVD Manager w rodowisku Eclipse), moemy
wykorzysta numer jego portu jako adres docelowy. Numer portu jest liczb pojawiajc si
w pasku tytuowym emulatora; zazwyczaj przybiera warto 5554. Po klikniciu przycisku Wylij
wiadomo w drugim emulatorze powinno si pojawi powiadomienie informujce, e zostaa
do niego dostarczona wiadomo.
Klasa SmsManager zawiera jeszcze dwie metody pozwalajce na wysanie wiadomoci SMS:
sendDataMessage() pobiera dodatkowy argument definiujcy numer portu
i zamiast cigu znakw przesya tablic bajtw;
589
sendMultipartTextMessage()
Podsumowujc, obsuga wysyania wiadomoci SMS w Androidzie jest wyjtkowo prosta. Pamitajmy, e emulator nie bdzie w rzeczywistoci wysya wiadomoci tekstowych. Moemy jednak
zaoy, e implementacja przebiega pomylnie, jeli metoda sendTextMessage() nie przekae adnego wyjtku. Jak wida na listingu 18.1, klasa Toast zostaa wykorzystana do wywietlenia
w interfejsie uytkownika komunikatu o powodzeniu wysania wiadomoci tekstowej.
Wysanie wiadomoci SMS jest zaledwie poow sukcesu. Zajmiemy si teraz monitorowaniem
przychodzcych wiadomoci tekstowych.
android.content.BroadcastReceiver;
android.content.Context;
android.content.Intent;
android.telephony.SmsMessage;
android.util.Log;
Pierwsz czci listingu 18.2 jest definicja manifestu dla klasy BroadcastReceiver pozwalajca
na odbieranie wiadomoci SMS. Klas monitorujc wiadomoci SMS jest klasa MySMSMonitor.
Zostaje w niej zaimplementowana abstrakcyjna metoda onReceive(), ktra jest wywoywana
przez system podczas otrzymywania wiadomoci SMS. Jednym ze sposobw przetestowania
tej aplikacji jest skorzystanie z widoku Emulator Control w rodowisku Eclipse. Uruchamiamy
aplikacj na emulatorze, a nastpnie przechodzimy do Window/Show View/Other/Android/
Emulator Control. Interfejs uytkownika pozwala na przesyanie do emulatora danych, ktre
591
imituj otrzymanie wiadomoci SMS lub odebranie poczenia telefonicznego. Na rysunku 18.2
wida, e wiadomoci SMS s wysyane poprzez wypenienie pola Incoming number, a nastpnie zaznaczenie opcji SMS. W dalszej kolejnoci wpisujemy tre wiadomoci w polu Message
i klikamy przycisk Send. W ten sposb zostaje wysana wiadomo tekstowa do emulatora i wywoana metoda onReceive() klasy BroadcastReceiver.
android.app.ListActivity;
android.database.Cursor;
android.net.Uri;
android.os.Bundle;
android.widget.ListAdapter;
android.widget.SimpleCursorAdapter;
593
Kod z listingu 18.3 otwiera skrzynk odbiorcz wiadomoci SMS i tworzy list elementw,
z ktrych kady zawiera cz treci wiadomoci tekstowej. Fragment kodu dotyczcy ukadu
graficznego z listingu 18.3 zawiera prost kontrolk TextView, w ktrej bdzie przechowywana
tre wiadomoci kadego elementu listy. Aby uzyska list wiadomoci SMS, tworzymy identyfikator URI wskazujcy na skrzynk wiadomoci przychodzcych (content://sms/inbox), a nastpnie wykonujemy prost kwerend. W kolejnym kroku filtrujemy tre wiadomoci SMS
i wyznaczamy adapter listy klasy ListActivity. Po wykonaniu kodu z listingu 18.3 ujrzymy
list wiadomoci tekstowych, dostpnych w skrzynce odbiorczej. Zanim uruchomimy kod na
emulatorze, utwrzmy najpierw kilka wiadomoci SMS za pomoc narzdzia Emulator Control.
Skoro potrafimy uzyska dostp do skrzynki odbiorczej wiadomoci SMS, spodziewamy si, e
bdziemy mogli korzysta rwnie z innych, powizanych folderw, na przykad ze skrzynki
nadawczej lub z folderu przechowujcego wersje robocze wiadomoci. Jedyn rnic pomidzy
skrzynk odbiorcz a innymi folderami s ich identyfikatory URI. Na przykad moemy uzyska
dostp do skrzynki nadawczej poprzez wykonanie kwerendy wobec adresu content://sms/sent.
Poniej znajduje si pena lista folderw wiadomoci SMS oraz definiujce je identyfikatory URI:
Wszystkie: content://sms/All.
Odebrane: content://sms/inbox.
Wysane: content://sms/sent.
Wersje robocze: content://sms/draft.
Nieodebrane: content://sms/outbox.
Niewysane: content://sms/failed.
Zakolejkowane: content://sms/queued.
Niedostarczone: content://sms/undelivered.
Konwersacje: content://sms/conversations.
W Androidzie koncepcje wiadomoci MMS i SMS s ze sob poczone i mona uzyska dostp
jednoczenie do dostawcw treci obydwu rodzajw za pomoc upowanienia mms-sms. Zatem
moemy korzysta z nastpujcego identyfikatora URI:
content://mms-sms/conversations
android:onClick="doClick"
/>
<TextView
android:id="@+id/textView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
android.app.Activity;
android.content.Context;
android.content.Intent;
android.net.Uri;
android.os.Bundle;
android.telephony.PhoneStateListener;
android.telephony.TelephonyManager;
android.view.View;
android.widget.TextView;
595
597
W powyszym przykadzie zajmujemy si tylko jednym ze stanw telefonw, ktre moemy nasuchiwa. Warto przejrze dokumentacj klasy PhoneStateListener, aby pozna inne stany,
na przykad LISTEN_MESSAGE_WAITING_INDICATOR. Podczas pracy ze zmianami stanu telefonu
moe by potrzebny rwnie numer telefonu subskrybenta (uytkownika). Otrzymujemy go
za pomoc metody TelephonyManager.getLine1Number().
Czytelnik by moe zastanawia si, czy mona odebra poczenie za pomoc kodu. Niestety,
na obecn chwil zestaw Android SDK nie posiada takiej moliwoci, chocia w dokumentacji
widnieje informacja, e mona uruchomi intencj za pomoc dziaania ACTION_ANSWER. W praktyce jednak rozwizanie to nie dziaa, chocia warto byoby sprawdzi, czy problem ten nie
zosta ju rozwizany.
Analogicznie, moemy chcie umieci poczenie wychodzce w kodzie. Tutaj na szczcie
sprawy maj si prociej. Najatwiejszym sposobem wprowadzenia poczenia wychodzcego
jest przywoanie aplikacji Dialer za pomoc intencji, wykorzystujc nastpujcy kod:
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:5551212"));
startActivity(intent);
599
Odnoniki
Poniej prezentujemy kilka odnonikw do materiaw, z ktrymi warto si dokadniej zapozna:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
przygotowanych z myl o tej ksice. Przykady z tego rozdziau znajdziemy w katalogu
ProAndroid3_R18_Telefonia. Doczylimy take plik Czytaj.TXT, w ktrym
dokadnie omwilimy proces importowania projektw do rodowiska Eclipse.
http://pl.wikipedia.org/wiki/Session_Initiation_Protocol artyku z Wikipedii dotyczcy
protokou SIP.
http://tools.ietf.org/html/rfc3261 oficjalny standard IETF dotyczcy protokou SIP.
http://tools.ietf.org/html/rfc4566 oficjalny standard IETF dotyczcy protokou SDP.
http://code.google.com/p/sipdroid/, http://code.google.com/p/csipsimple/ dwie
przeznaczone dla Androida aplikacje o otwartym kodzie rdowym, implementujce
klientw SIP.
Podsumowanie
W tym rozdziale zajmowalimy si interfejsami telefonii. Skupilimy si zwaszcza na wysyaniu
wiadomoci tekstowych, monitorowaniu otrzymywanych wiadomoci SMS oraz zaprezentowalimy metody uzyskania dostpu do rnych folderw zwizanych z wiadomociami SMS,
znajdujcych si na urzdzeniu. Dokonalimy rwnie analizy klasy TelephonyManager. Rozdzia zakoczylimy omwieniem zestawu funkcji zwizanych z protokoem SIP, wprowadzonego w wersji 2.3 systemu Android.
R OZDZIA
19
Uywanie szkieletu
multimedialnego
MediaPlayer
Klasa MediaPlayer potrafi obsuy kilka rnych formatw multimediw, w tym takie
jak 3GPP (.3gp), MP3 (.mp3), MIDI (.mid i inne), Ogg Vorbis (.ogg), PCM/WAVE
(.wav) oraz MPEG-4 (.mp4). W przypadku wersji 3.0 Androida obsugiwane s rwnie
media strumieniowe, przesyane protokoem HTTP w czasie rzeczywistym, oraz listy
odtwarzania M3U. Pen list obsugiwanych formatw mona znale pod adresem:
http://developer.android.com/guide/appendix/media-formats.html
Wykorzystywanie kart SD
Zanim przejdziemy do procesu tworzenia i stosowania rnych rodzajw multimediw, zobaczmy, w jaki sposb pracujemy z kartami SD. Karty SD s uywane w urzdzeniach obsugujcych system Android do przechowywania duej iloci danych uytkownika, na przykad
plikw obrazw, audio i wideo. Zasadniczo s to niewielkie ukady scalone przechowujce dane
nawet przy braku zasilania. W rzeczywistym telefonie karta SD jest umieszczana w gniedzie
pamici i staje si dostpna dla urzdzenia. Wikszo urzdze posiada tylko jedno gniazdo
pamici i karta SD zazwyczaj nie jest w nich wymieniana. W niektrych urzdzeniach mona
posiada wiele kart i przecza si pomidzy nimi w obrbie jednego urzdzenia, a take mona je wymienia pomidzy rnymi urzdzeniami. Na szczcie emulator Androida potrafi
symulowa karty SD, a za ich pojemno suy przestrze dysku twardego.
Podczas tworzenia pierwszego urzdzenia AVD w rozdziale 2. okrelilimy rozmiar karty
SD, dziki czemu staa si ona dostpna dla aplikacji podczas jej uruchomienia na emulatorze. Jeeli przyjrzymy si zawartoci utworzonego katalogu urzdzenia AVD, ujrzymy plik
sdcard.img, w ktrym zdefiniowano rozmiar karty. Nie korzystalimy wwczas z tej symulacji karty, bdziemy jednak to robi w tym rozdziale.
Projektanci aplikacji mog, po utworzeniu karty SD, uywa narzdzi rodowiska Eclipse do
umieszczania plikw multimedialnych (gwoli cisoci, dowolnego rodzaju plikw) na karcie
SD. Do umieszczania plikw na takiej karcie lub usuwania ich z niej moemy rwnie wykorzysta aplikacj adb. Aplikacja ta jest umieszczona w podkatalogu tools rodowiska Android
SDK; w rozdziale 2. opisalimy atwy sposb uzyskania do niej dostpu z okna narzdzi.
Wiemy ju, w jaki sposb wygenerowa symulacj karty SD podczas procesu tworzenia urzdzenia AVD. Oczywicie moemy rwnie utworzy wiele takich samych urzdze AVD rnicych si jedynie rozmiarem karty SD. Istnieje jeszcze inny sposb. Wrd narzdzi rodowiska
SDK znajduje si aplikacja mksdcard, suca do tworzenia obrazu karty SD. W rzeczywistoci
aplikacja ta generuje sformatowany plik, wykorzystywany jako karta SD. Aby skorzysta z tej
aplikacji, musimy najpierw wyszuka lub utworzy folder, w ktrym bdzie przechowywany obraz
karty SD, na przykad c:\Android\sdcard\. Nastpnie otwieramy okno narzdzi (w rozdziale 2.
zostay umieszczone informacje na temat okna narzdzi) i wpisujemy nastpujce polecenie,
podajc ciek do obrazu karty SD:
mksdcard 256M c:\Android\sdcard\sdcard.img
603
urzdzenia AVD jest uruchomienie emulatora z poziomu wiersza polece i okrelenie tam,
ktry obraz karty SD ma by wykorzystywany. Ponisze polecenie, wpisane w oknie narzdzi,
uruchamia dane urzdzenie AVD wraz z wybranym obrazem karty SD, a nie z obrazem karty
SD utworzonej wraz z tym urzdzeniem:
emulator -avd AVDName -sdcard "PATH_TO_YOUR_SD_CARD_IMAGE_FILE"
Tu po utworzeniu karty SD jest ona pusta. Mona na niej umieszcza pliki poprzez narzdzie
File Explorer w rodowisku Eclipse. W tym celu wczamy emulator i czekamy, a si uruchomi
do koca. Nastpnie w rodowisku Eclipse przechodzimy do perspektywy Java, Debug lub DDMS
i wyszukujemy zakadk File Explorer, pokazan na rysunku 19.1.
Jeeli zakadka ta nie jest widoczna, moemy j wywietli, klikajc Windows/Show View/Other/
Android i zaznaczajc opcj File Explorer. Ewentualnie moemy przej do perspektywy DDMS,
wybierajc Window/Open Perspective/Other/DDMS. Widok File Explorer jest domylny w perspektywie DDMS. Lista wszystkich dostpnych widokw zostaa zaprezentowana na rysunku 19.2.
W ten sposb plik zostanie przeniesiony ze stacji roboczej na kart SD. Zwrmy uwag, e
urzdzenie stosuje ukoniki (/) do oddzielania katalogw. Uywamy dowolnego znaku oddzielania katalogu wykorzystywanego przez stacj robocz dla kopiowanego pliku oraz wpisujemy
waciw ciek dostpu do pliku umieszczonego w stacji roboczej. I odwrotnie, ponisze polecenie przekopiuje plik z karty SD do stacji roboczej:
adb pull /mnt/sdcard/nazwa_pliku c:\cieka_docelowa\nazwa_pliku
Jedn z ciekawszych funkcji tego polecenia jest tworzenie katalogw w razie potrzeby, a take
pobieranie i wysyanie plikw po zdefiniowaniu docelowej cieki. Niestety, aplikacja adb nie
posiada moliwoci rwnoczesnego kopiowania wielu plikw. Kady plik naley przenosi
oddzielnie.
A do wersji 2.2 Androida karta SD bya najczciej umiejscowiona w folderze /sdcard.
W tej oraz nowszych wersjach karta SD znajduje si w folderze /mnt/sdcard, jednak
istnieje symboliczne powizanie, nazwane /sdcard, wskazujce ciek /mnt/sdcard.
Zapewnia ono wsteczn kompatybilno.
605
(nie wszystkich) wykorzystujcych wersje Androida starsze od 2.2 prawdopodobnie otrzymalibymy nazw /sdcard. W przypadku wersji 2.2 i nowszych najczciej spotykana jest cieka
/mnt/sdcard. O wiele lepiej wykorzystywa metod Environment, ni zakada, e znamy nazw
gwnego katalogu karty SD. Omwimy teraz drug metod.
Od wersji 2.2 Androida (o nazwie kodowej Froyo) do klasy Environment zostay wprowadzone
nowe stae, a take nowa metoda, suce do lokalizowania katalogw. Wczeniej hierarchia
plikw na karcie SD bya do nieuporzdkowana, nie istniaa bowiem tutaj, nie liczc katalogu
DCIM, ustandaryzowana specyfikacja nazewnictwa katalogw. Wraz z wersj Froyo nastpia
pewna standaryzacja nazw katalogw, co zostao zaprezentowane w tabeli 19.1. W trzeciej
kolumnie zostaa podana stosowana nazwa katalogu w emulatorze, gdzie gwny katalog bdzie
prawdopodobnie wyglda tak: /mnt/sdcard (w zalenoci od urzdzenia). Z powodu rnic
w nazewnictwie katalogw powinnimy zawsze wykorzystywa klas Environment do znalezienia tego waciwego.
Tabela 19.1. Ustandaryzowane nazwy folderw na karcie SD
Nazwa katalogu
w emulatorze
Staa katalogu
Opis
DIRECTORY_ALARMS
Alarms
DIRECTORY_DCIM
DCIM
DIRECTORY_DOWNLOADS
Download (uwaga:
liczba pojedyncza)
DIRECTORY_MOVIES
Movies
DIRECTORY_MUSIC
Music
DIRECTORY_NOTIFICATIONS
Notifications
DIRECTORY_PICTURES
Pictures
DIRECTORY_PODCASTS
Podcasts
DIRECTORY_RINGTONES
Ringtones
Aplikacje tworzone w starszej wersji rodowiska SDK nie wymagaj podania tego uprawnienia.
Oznacza to, e jeeli warto parametru minSdkVersion aplikacji jest mniejsza od 4 (odpowiada
ona wersji 1.6 zestawu Android SDK), to nie musimy dodawa powyszego znacznika do pliku
AndroidManifest.xml, nawet jeli aplikacja jest uruchomiona na urzdzeniu obsugujcym nowsz wersj rodowiska SDK. Zatem jeeli podczas procesu tworzenia aplikacji wybierzemy
wersj Android 1.6 lub nowsz (warto minSdkVersion rwna co najmniej 4) i chcemy, aby
istniaa moliwo zapisywania danych na karcie SD, musimy doda powyszy znacznik do
naszego pliku manifestu. Jeeli uywamy wersji 1.5 Androida lub niszej, nie potrzebujemy tego znacznika. Skoro zapoznalimy si ju z podstawami kart SD, przejdmy do multimediw
dwikowych.
Odtwarzanie multimediw
Rozpoczniemy od napisania prostej aplikacji odtwarzajcej plik MP3 udostpniony w internecie
(rysunek 19.3). Nastpnie omwimy zastosowanie metody setDataSource() klasy MediaPlayer,
dziki ktrej moliwe jest odtwarzanie zawartoci multimedialnej pliku .apk lub karty SD. Klasa
MediaPlayer nie jest jednak jedynym sposobem umoliwiajcym odtwarzanie dwiku, zatem
przyjrzymy si klasie SoundPool, a take klasom JetPlayer, AsyncPlayer oraz wystpujcej na
najniszym poziomie zoonoci klasie AudioTrack. Nastpnie opiszemy kilka brakw dostrzeonych w klasie MediaPlayer. Temat zamkniemy omwieniem sposobu odtwarzania plikw wideo.
607
android.app.Activity;
android.content.res.AssetFileDescriptor;
android.media.MediaPlayer;
android.os.Bundle;
android.os.Environment;
android.util.Log;
android.view.View;
// Environment.getExternalStoragePublicDirectory(
// Environment.DIRECTORY_MUSIC) +
// "/music_file.mp3";
// Environment.getExternalStoragePublicDirectory(
// Environment.DIRECTORY_MOVIES) +
// " /movie.mp4";
private MediaPlayer mediaPlayer;
private int playbackPosition=0;
// playLocalAudio();
// playLocalAudio_UsingDescriptor();
} catch (Exception e) {
e.printStackTrace();
}
break;
case R.id.pausePlayerBtn:
if(mediaPlayer != null && mediaPlayer.isPlaying()) {
playbackPosition = mediaPlayer.getCurrentPosition();
mediaPlayer.pause();
}
break;
case R.id.restartPlayerBtn:
if(mediaPlayer != null && !mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(playbackPosition);
mediaPlayer.start();
}
break;
case R.id.stopPlayerBtn:
if(mediaPlayer != null) {
mediaPlayer.stop();
playbackPosition = 0;
}
break;
}
}
private void playAudio(String url) throws Exception
{
killMediaPlayer();
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
mediaPlayer.start();
}
private void playLocalAudio() throws Exception
{
mediaPlayer = MediaPlayer.create(this, R.raw.music_file);
609
W tym przypadku odtwarzamy plik MP3 udostpniony w internecie, zatem musimy umieci
w pliku manifecie uprawnienie android.permission.INTERNET. W kodzie z listingu 19.1 wida,
e klasa MainActivity obejmuje trzy elementy: cig znakw final okrelajcy adres URL
pliku MP3, wystpienie klasy MediaPlayer oraz obiekt playbackPosition przyjmujcy wartoci
w postaci liczb cakowitych. Nasza metoda onCreate() konfiguruje jedynie interfejs graficzny
z pliku XML. W procedurze obsugi kliknicia przycisku Odtwarzaj plik audio jest wywoywana metoda playAudio(). W metodzie tej tworzymy nowe wystpienie klasy MediaPlayer,
a rdem danych odtwarzacza staje si adres URL pliku MP3. Nastpnie wywoujemy metod
odtwarzacza prepare(), suc do przygotowania odtwarzania, po niej za zostaje przywoana
metoda start(), rozpoczynajca odtwarzanie.
Spjrzmy teraz na kod przyciskw Wstrzymaj odtwarzacz i Uruchom ponownie odtwarzacz.
Widzimy, e po klikniciu przycisku Wstrzymaj odtwarzacz otrzymujemy biec pozycj odtwarzacza za pomoc wywoania metody getCurrentPosition(). Nastpnie wywoujemy metod
pause(), aby wstrzyma odtwarzanie. Przed ponownym uruchomieniem odtwarzacza
wywoujemy metod seekTo(), ktra pobiera pozycj przechowywan przez metod getCurrent
Position(), a w dalszej kolejnoci przywoujemy metod start().
Klasa MediaPlayer posiada take metod stop(). Jeeli za pomoc tej metody zatrzymamy
odtwarzacz, przed ponownym wywoaniem metody start() musimy przywoa znowu metod
prepare(). W przypadku wstrzymania odtwarzacza poprzez metod pause() nie musimy wywoywa metody prepare() przed ponownym uruchomieniem odtwarzania. Po zakoczeniu
korzystania z aplikacji musimy rwnie wywoa metod release(). W naszym przykadzie
jest ona czci metody killMediaPlayer().
Na listingu 19.1 pokazalimy sposb odtwarzania pliku audio udostpnionego w internecie.
Klasa MediaPlayer obsuguje rwnie odtwarzanie lokalnych plikw multimedialnych, bdcych czci pakietu .apk. Na listingu 19.2 przedstawiono technik tworzenia odniesienia
do pliku zawartego w folderze /res/raw pakietu .apk oraz sposb jego odtwarzania. Moemy
utworzy katalog raw w wle res, jeli nie zosta jeszcze utworzony podczas generowania projektu w rodowisku Eclipse. Nastpnie skopiujmy dowolny plik MP3 nazwany music_file.mp3
do podkatalogu /res/raw.
Listing 19.2. Zastosowanie klasy MediaPlayer do odtwarzania lokalnego pliku w aplikacji
private void playLocalAudio()throws Exception
{
mediaPlayer = MediaPlayer.create(this, R.raw.music_file);
Jeeli plik audio lub wideo ma si znale w aplikacji, powinnimy go umieci w katalogu
/res/raw. Moemy nastpnie uzyska wystpienie klasy MediaPlayer dla tego zasobu poprzez
przekazanie jej identyfikatora zasobu tego pliku; w tym celu wywoujemy statyczn metod
611
create(),
tak jak pokazano na listingu 19.2. Odnotujmy fakt, e klasa MediaPlayer rwnie
zapewnia metody create(), dziki ktrym mona uzyska do niej dostp, zamiast samemu tworzy jej nowy egzemplarz. Na przykad na listingu 19.2 wywoujemy metod create(), lecz rwnie
dobrze moglibymy wywoa konstruktor MediaPlayer(Context context,int resourceId).
Zalecane jest stosowanie statycznych metod create(), poniewa ukrywaj one proces tworzenia
klasy MediaPlayer, a w tym przypadku wan rol odgrywa wywoanie metody prepare().
Jednak, jak si wkrtce okae, czasami nie mona wybiera pomidzy tymi opcjami w przypadku gdy nie bdzie mona lokalizowa rde danych multimedialnych za pomoc identyfikatora zasobw lub adresu URL, trzeba utworzy obiekt domylnego konstruktora.
Metoda setDataSource
Na listingu 19.2 wywoalimy metod create(), pozwalajc na wczytanie pliku audio z nieskompresowanego zasobu. Dziki temu nie musimy wywoywa metody setDataSource().
Ewentualnie, jeeli sami utworzymy klas MediaPlayer za pomoc domylnego konstruktora
lub jeli nie mona uzyska dostpu do pliku multimedialnego za pomoc identyfikatora zasobw bd adresu URL, bdzie potrzebna metoda setDataSource().
Metoda setDataSource() istnieje w olbrzymiej liczbie wersji, dziki ktrym moemy dostosowa rdo danych do wasnych potrzeb. Na przykad na listingu 19.3 zosta ukazany sposb
wczytania pliku audio z nieskompresowanego zasobu za pomoc obiektu FileDescriptor.
Listing 19.3. Konfigurowanie rda danych dla klasy MediaPlayer za pomoc obiektu FileDescriptor
private void playLocalAudio_UsingDescriptor() throws Exception {
AssetFileDescriptor fileDesc = getResources().openRawResourceFd(
R.raw.music_file);
if (fileDesc != null) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(fileDesc.getFileDescriptor(), fileDesc
.getStartOffset(), fileDesc.getLength());
fileDesc.close();
mediaPlayer.prepare();
mediaPlayer.start();
}
}
Zakadamy, e kod z listingu 19.3 znajduje si wewntrz kontekstu aktywnoci. Jak wida,
wywoujemy metod getResources(), aby uzyska dostp do zasobw aplikacji, a nastpnie
korzystamy z metody openRawResourceFd() w celu otrzymania deskryptora pliku audio, znajdujcego si w folderze res/raw. W dalszej kolejnoci wywoujemy metod setDataSource(),
ktra poprzez obiekt AssetFileDescriptor otrzymuje informacj o pocztkowej i kocowej
pozycji odtwarzania. Ta wersja metody setDataSource() moe by rwnie wykorzystana do
odtwarzania okrelonego fragmentu pliku. Jeeli chcemy zawsze odtwarza plik w caoci, moemy uy prostszej wersji metody setDataSource(FileDescriptor desc), ktra nie wymaga
wartoci pocztkowej i czasu trwania odtwarzania tego pliku.
613
Klasa SoundPool nie jest jednak pozbawiona wad. Istnieje wsplny bufor przeznaczony dla
wszystkich cieek zarzdzanych przez t klas i nie jest on zbyt pojemny. W zasadzie jego
rozmiar wynosi 1 MB. Moe si to wydawa du wielkoci w przypadku plikw MP3, ktrych rozmiary czsto nie przekraczaj kilku kilobajtw. Klasa SoundPool dekompresuje jednak
plik audio w pamici, aby odtwarzanie dwiku przebiegao szybko i sprawnie. Rozmiar strumienia audio w pamici zaley od prdkoci transmisji, liczby kanaw (mono lub stereo), czstotliwoci prbkowania oraz dugoci cieki. Jeeli mamy problem z wczytaniem dwikw
do klasy SoundPool, powinnimy wprowadzi plik rdowy o nieco niszych parametrach
jakociowych, aby zmniejszy zuycie pamici.
Zaprezentujemy teraz przykadow aplikacj, pozwalajc na wczytanie i odtwarzanie dwikw wydawanych przez zwierzta. Jeden z dwikw, odtwarzany bez przerwy w tle, jest
wydawany przez wierszcze. Pozostae dwiki s odtwarzane w rnych odstpach czasowych.
Czasami bdziemy sysze wycznie wierszcze, a za innym razem odezwie si kilka zwierzt
naraz. Umiecimy rwnie w interfejsie przycisk pozwalajcy na wstrzymywanie i ponowne
uruchamianie odtwarzania. Listing 19.4 zawiera plik ukadu graficznego oraz kod Java definiujcy aktywno. Zalecamy Czytelnikowi pobranie tego projektu z naszej oficjalnej strony, poniewa oprcz kodu s tam rwnie pliki dwikowe. Adres URL do tej strony mona znale
na kocu rozdziau, w podrozdziale Odnoniki.
Listing 19.4. Odtwarzanie dwiku za pomoc klasy SoundPool
<?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"
>
<ToggleButton android:id="@+id/button"
android:textOn="Wstrzymaj" android:textOff="Wznw"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onClick="doClick" android:checked="true" />
</LinearLayout>
java.io.IOException;
android.app.Activity;
android.content.Context;
android.content.res.AssetFileDescriptor;
android.media.AudioManager;
android.media.SoundPool;
android.os.Bundle;
android.os.Handler;
android.util.Log;
android.view.View;
android.widget.ToggleButton;
int
int
int
int
int
sid_background;
sid_roar;
sid_bark;
sid_chimp;
sid_rooster;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onResume() {
soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,
SRC_QUALITY);
soundPool.setOnLoadCompleteListener(this);
aMgr =
(AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
sid_background = soundPool.load(this, R.raw.crickets, PRIORITY);
sid_chimp = soundPool.load(this, R.raw.chimp, PRIORITY);
sid_rooster = soundPool.load(this, R.raw.rooster, PRIORITY);
sid_roar = soundPool.load(this, R.raw.roar, PRIORITY);
try {
AssetFileDescriptor afd =
this.getAssets().openFd("dogbark.mp3");
sid_bark = soundPool.load(afd.getFileDescriptor(),
0, afd.getLength(), PRIORITY);
afd.close();
} catch (IOException e) {
e.printStackTrace();
}
615
soundPool.release();
soundPool = null;
super.onPause();
}
@Override
public void onLoadComplete(SoundPool sPool, int sid, int status) {
Log.v("soundPool", "sid " + sid + " wczytany ze stanem " +
status);
final float currentVolume =
((float)aMgr.getStreamVolume(AudioManager.STREAM_MUSIC)) /
((float)aMgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
if(status != 0)
return;
if(sid == sid_background) {
if(sPool.play(sid, currentVolume, currentVolume,
PRIORITY, -1, 1.0f) == 0)
Log.v("soundPool", "Proba odtworzenia dzwieku zakonczona niepowodzeniem");
} else if(sid == sid_chimp) {
queueSound(sid, 5000, currentVolume);
} else if(sid == sid_rooster) {
queueSound(sid, 6000, currentVolume);
} else if(sid == sid_roar) {
queueSound(sid, 12000, currentVolume);
} else if(sid == sid_bark) {
queueSound(sid, 7000, currentVolume);
}
}
private void queueSound(final int sid, final long delay,
final float volume)
{
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if(soundPool == null) return;
if(soundPool.play(sid, volume, volume,
PRIORITY, 0, 1.0f) == 0)
Log.v("soundPool", "Nie udalo sie odtworzyc dzwieku (" + sid +
")");
queueSound(sid, delay, volume);
}}, delay);
}
}
Struktura tego kodu nie jest zoona. Widzimy interfejs uytkownika zawierajcy jedn kontrolk
ToggleButton. Za jej pomoc bdziemy wstrzymywa i wznawia odtwarzanie aktywnych
strumieni dwikowych. Po uruchomieniu aplikacji tworzymy obiekt klasy SoundPool i wczytujemy do niego pliki dwikowe. Jeeli zostan one prawidowo wczytane, rozpoczynamy ich
odtwarzanie. Plik z cykaniem wierszczy zosta zaptlony i odtwarza si w sposb cigy, podczas gdy pozostae dwiki s odtwarzane po opnieniu, rwnie w zaptleniu. Dziki przyjciu rnych czasw opnienia dwiki nakadaj si na siebie w rozmaitych konfiguracjach.
617
Po uruchomieniu tej aplikacji usyszymy wierszcze, szympansa, koguta, psa i ryk (podejrzewamy, e niedwiedzia). Usyszymy, e wierszcze cykaj nieprzerwanie, natomiast gosy
pozostaych zwierzt pojawiaj si i znikaj. Jedna z najlepszych cech klasy SoundPool jest taka,
e pozwala na odtwarzanie wielu dwikw naraz bez wielkiego udziau ze strony programisty.
Rwnie urzdzenie nie zostaje zbyt mocno obcione, poniewa dwiki zostay zdekodowane
ju w trakcie wczytywania do pamici i jedyne, co nam pozostaje, to przekaza je sprztowi.
Jeli klikniemy przycisk, odtwarzane gosy wierszczy oraz wszystkich aktualnie syszanych
zwierzt zostan wstrzymane. Jednak metoda autoPause() nie zatrzymuje odtwarzania innych
dwikw. Po okrelonym czasie znowu usyszymy gos jakiego zwierzaka (nie liczc wierszczy). Poniewa stworzylimy kolejk dwikw, ktre maj by odtwarzane w przyszoci, bdziemy je cigle sysze. W rzeczywistoci klasa SoundPool nie posiada adnej metody pozwalajcej na zatrzymywanie odtwarzanych i przewidzianych do odtworzenia dwikw. Trzeba
samodzielnie zaimplementowa odpowiedni funkcj. Odgosy wierszczy mog zosta na nowo
odtworzone wycznie po ponownym klikniciu przycisku. Jednak nawet wtedy niekoniecznie
zostan odtworzone, poniewa klasa SoundPool usuwa najstarszy dwik, aby zrobi miejsce
dla najnowszego, w przypadku gdy zostanie osignita maksymalna liczba odtwarzanych jednoczenie dwikw.
619
Odtwarzanie plikw wideo jest bardziej zoonym procesem, poniewa trzeba sobie poradzi
nie tylko ze skadow dwikow, ale rwnie z wizualn. Aby nieco uatwi spraw, w Androidzie uwzgldniono wyspecjalizowan kontrolk widoku, nazwan android.widget.VideoView,
ktra zajmuje si tworzeniem i uruchamianiem klasy MediaPlayer. Aby odtworzy plik wideo,
budujemy widet VideoView w interfejsie uytkownika. Nastpnie wprowadzamy ciek lub
identyfikator URI pliku wideo i wywoujemy metod start(). Na listingu 19.6 przedstawiamy
kod odpowiedzialny za odtwarzanie plikw wideo w Androidzie.
Listing 19.6. Odtwarzanie pliku wideo za pomoc interfejsw API multimediw
<?xml version="1.0" encoding="utf-8"?>
android.app.Activity;
android.net.Uri;
android.os.Bundle;
android.widget.MediaController;
android.widget.VideoView;
/* videoView.setVideoPath(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES) +
"/movie.mp4");
*/
videoView.requestFocus();
videoView.start();
}
}
Na listingu 19.6 pokazalimy, w jaki sposb mona odtwarza plik wideo dostpny w internecie pod adresem www.androidbook.com/akc/filestorage/android/documentfiles/3389/movie.mp4.
Oznacza to, e aplikacja przetwarzajca ten kod bdzie daa uprawnienia android.
permission.INTERNET. Wszystkie funkcje odtwarzania pliku wideo znajduj si wewntrz klasy
VideoView. W rzeczywistoci wystarczy poda odtwarzaczowi adres pliku wideo. Interfejs
UI tej aplikacji jest pokazany na rysunku 19.4.
Po uruchomieniu aplikacji przez mniej wicej trzy sekundy w dolnej czci ekranu bd widoczne przyciski, ktre nastpnie znikn. Kliknicie gdziekolwiek w widoku odtwarzacza spowoduje ich ponowne wywietlenie. Podczas odtwarzania pliku audio wystarczyy nam przyciski odpowiedzialne za odtwarzanie, wstrzymywanie i ponowne uruchamianie odtwarzania
621
pliku. Skadowa graficzna przedstawiajca plik audio nie bya nam do niczego potrzebna.
Oczywicie w przypadku plikw wideo potrzebujemy zarwno przyciskw, jak i pojemnika,
w ktrym moemy oglda obraz. W naszym przykadzie korzystamy ze skadowej VideoView
do ogldania pliku. Jednak zamiast tworzy wasne kontrolki przyciskw (w razie potrzeby
mamy tak moliwo), tworzymy obiekt MediaController, zawierajcy predefiniowane przyciski. Jak zostao pokazane na rysunku 19.4 i listingu 19.6, konfigurujemy kontroler multimediw poprzez wywoanie metody setMediaController() umoliwiajcej odtwarzanie, wstrzymywanie oraz przewijanie pliku wideo. Gdybymy chcieli wprowadzi wasne przyciski, moemy
wywoa metody start(), pause(), stopPlayback() i seekTo().
Nie zapominajmy, e w tym przykadzie cigle korzystamy z klasy MediaPlayer tylko jej nie
widzimy. W rzeczywistoci moemy odtwarza pliki wideo bezporednio z poziomu tej klasy.
Jeeli wrcimy do przykadu umieszczonego na listingu 19.1, umiecimy plik wideo na karcie
SD i wpiszemy ciek dostpu do tego filmu jako warto zmiennej AUDIO_PATH, stwierdzimy,
e dwik jest odtwarzany cakiem dobrze, chocia nie widzimy obrazu.
Podczas gdy klasa MediaPlayer zawiera metod setDataSource(), klasa VideoView jej nie
posiada, korzysta za to z metod setVideoPath() lub setVideoURI(). Zakadajc, e plik wideo
znajduje si na karcie SD, zmieniamy kod z listingu 19.6, tak aby oznaczy komentarzem wywoanie metody setVideoURI() oraz usun znaki komentarza z wywoania metody setVideoPath(),
i wpisujemy poprawn ciek do filmu. Po ponownym uruchomieniu aplikacji usyszymy
i zobaczymy odtwarzany plik wideo w widoku VideoView. Technicznie ten sam efekt moglibymy osign, wywoujc metod setVideoURI() w sposb przedstawiony poniej:
videoView.setVideoURI(Uri.parse("file://" +
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES) + "/movie.mp4"));
Rejestrowanie multimediw
Jak pokazalimy, w systemie Android istnieje wiele sposobw odtwarzania multimediw.
W przypadku rejestrowania danych multimedialnych posiadamy nieco mniej moliwoci. Gwn
klas robocz suc do zapisywania takich danych jest MediaRecorder, ktra jest stosowana
zarwno do plikw audio, jak i wideo. W tym podrozdziale zaprezentujemy sposb korzystania
z tej klasy do rejestrowania obydwu rodzajw danych. Drug klas, pozwalajc na nagrywanie
dwiku, jest klasa AudioRecord. Aby pokaza, w jaki sposb j wykorzystywa, utworzymy
osobny przykadowy projekt. Czasami nie musimy tworzy w caoci kodu, skoro istnieje aplikacja, ktra wykonuje zamierzone czynnoci. Pokaemy wic, w jaki sposb mona uruchomi
intencj suc do rejestrowania dwiku, a take jak fotografowa za pomoc aplikacji Camera.
Jak wida na rysunku 19.5, aplikacja posiada cztery przyciski: dwa su do kontroli nagrywania, a dwa pozostae do odtwarzania i zatrzymywania odtwarzania nagranej treci. Na listingu
19.7 zawarto tre pliku ukadu graficznego oraz kod klasy aktywnoci tego interfejsu.
Listing 19.7. Nagrywanie i odtwarzanie dwiku w Androidzie
<?xml version="1.0" encoding="utf-8"?>
</LinearLayout>
// RecorderActivity.java
import
import
import
import
import
import
import
java.io.File;
android.app.Activity;
android.media.MediaPlayer;
android.media.MediaRecorder;
android.os.Bundle;
android.os.Environment;
android.view.View;
623
625
}
@Override
protected void onDestroy() {
super.onDestroy();
killMediaRecorder();
killMediaPlayer();
}
}
Zanim zajmiemy si omawianiem listingu 19.7, musimy umieci w pliku manifecie nastpujcy
wpis o uprawnieniu, aby mc rejestrowa dwik:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
W podrozdziale dotyczcym kart SD wspomnielimy rwnie, e w przypadku wartoci parametru minSdkVersion rwnej lub wikszej od 4 musimy doda znacznik uses-permission
dla klasy "android.permission.WRITE_EXTERNAL_STORAGE". Oczywicie, jeeli chcemy wyprbowa funkcj rejestrowania dwiku na emulatorze, musimy podczy mikrofon do stacji
roboczej.
Jeli przyjrzymy si metodzie onCreate() z listingu 19.7, zauwaymy, e jedyn niezbdn czynnoci jest wprowadzenie cieki do wyjciowego pliku dwikowego. Nasza metoda doClick()
wykorzystuje standardowy wzorzec przeczania pomidzy przyciskami, trzeba tylko przywoa
funkcj wykonujc waciwe zadanie. Metoda beginRecording() obsuguje proces rejestracji
dwiku. Aby nagrywa dwik, musimy utworzy wystpienie klasy MediaRecorder i skonfigurowa rdo dwiku, format pliku wynikowego, koder audio oraz ciek pliku wyjciowego.
W wersjach rodowiska Android SDK starszych od 1.6 jedynym obsugiwanym rdem dwiku by mikrofon. Od czasu wydania wersji Android SDK 1.6 dostpne s trzy dodatkowe rda
dwiku, wszystkie zwizane z rozmowami telefonicznymi. Moemy rejestrowa ca rozmow
(MediaRecorder.AudioSource.VOICE_CALL), transmisj prowadzon wycznie w gr sieci
(MediaRecorder.AudioSource.VOICE_UPLINK) lub transmisj prowadzon wycznie w d
sieci (MediaRecorder.AudioSource.VOICE_DOWNLINK). Transmisj prowadzon w gr sieci
moe by gos uytkownika telefonu. Transmisj prowadzon w d okrela si zazwyczaj dwiki
dochodzce z drugiego koca poczenia.
Wraz z wersj 2.1 Androida dodano obsug dwch nowych rde dwiku: CAMCORDER oraz
VOICE_RECOGNITION. rdo CAMCORDER moe by mikrofonem zwizanym z aparatem, w przeciwnym wypadku po wybraniu tej opcji bdzie stosowany domylny mikrofon urzdzenia.
Z kolei po wyborze trybu VOICE_RECOGNITION system wykorzystuje mikrofon przystosowany
do rozpoznawania mowy, a w przypadku jego braku znowu gwny mikrofon urzdzenia.
Poprzez stwierdzenie przystosowany do rozpoznawania mowy mamy na myli urzdzenie,
ktre zupenie nie przetwarza strumienia audio ani nie wprowadza adnych modyfikacji dwiku
na drodze pomidzy mikrofonem a aplikacj. Przykadem urzdze modyfikujcych rejestrowany dwik s niektre urzdzenia firmy HTC, ktrych mikrofony wyposaono w funkcj
automatycznej regulacji wzmocnienia (ang. Automatic Gain Control AGC). Wykorzystywanie tego urzdzenia w procesie rozpoznawania mowy moe stanowi problem. rdo
VOICE_RECOGNITION pomija tego typu dodatkowe przetwarzanie, dziki czemu proces przetwarzania mowy okazuje si skuteczniejszy.
Najpopularniejszym formatem wyjciowym dwiku jest format 3GPP (ang. 3rd Generation
Partnership Project partnerski projekt trzeciej generacji). W wersjach Androida starszych
android.app.Activity;
android.media.AudioFormat;
android.media.AudioRecord;
android.media.MediaRecorder;
android.os.Bundle;
android.util.Log;
627
Nasza przykadowa aplikacja jest raczej nieskomplikowana. Rozpoczynamy od zainicjalizowania obiektu AudioRecord. Musimy w tym celu wybra rdo dwiku, czstotliwo
prbkowania, konfiguracj kanaw (mono, stereo, lewy, prawy itd.), format kodowania
oraz rozmiar buforu wewntrznego. Jeli chodzi o rdo dwiku, mamy do dyspozycji zestaw opcji zdefiniowanych w klasie MediaRecorder.AudioSource. Musimy tylko wspomnie,
e nie wszystkie urzdzenia posiadaj zaimplementowane rdo VOICE_CALL, poniewa w rzeczywistoci wykorzystuje ono dwa urzdzenia wejciowe. Wrd czstotliwoci prbkowania
powinnimy wybra jedn ze standardowych wartoci: 8000, 16000, 44100, 22050 lub 11025 Hz.
629
false,
co
Zwrmy uwag, e obiekt nasuchujcy posiada dwie oddzielne metody zwrotne. Pierwsza
z nich jest wywoywana co 1000 ramek, co zostao zdefiniowane w metodzie inicjalizujcej. Ten
licznik ramek jest niezaleny od rozmiaru buforu. Nawet jeli jednorazowo bdziemy odczytywa 1600 ramek, metoda ta bdzie wywoywana co 1000 ramek. Zatem w tym przypadku
wspomniana metoda bdzie przywoana dwukrotnie w czasie jednej ptli. Druga metoda jest
wywoywana, gdy osigniemy cakowit liczb ramek. W naszym przykadzie zdefiniowalimy
t warto jako 10 000 ramek; po jej osigniciu rejestrowanie dwiku zostaje zakoczone poprzez wprowadzenie wartoci logicznej false. Gdybymy jedynie wypisali komunikat o osigniciu tej wartoci i nie wyczyli procesu rejestrowania, warto ta nie pojawiaby si ju
ponownie, niezalenie od liczby ramek odczytanych w przyszoci. Jest to znacznik relatywny
od momentu wywoania metody startRecording() w obiekcie AudioRecord.
631
SDK 1.6, wymagane jest, aby na obiekcie Surface by tworzony podgld rejestrowanego obrazu. W prostych aplikacjach nie stanowi to problemu, poniewa uytkownik bdzie chcia widzie podgld tego, co nagrywa. W bardziej zoonych programach moe si pojawi problem.
Nawet jeeli aplikacja nie musi pokazywa uytkownikowi podgldu wideo w trakcie nagrywania, obiekt Surface nadal musi by obecny, gdy takie jest wymaganie klasy camera.
Spodziewamy si, e wymg ten zostanie w przyszych wersjach rodowiska SDK zagodzony,
tak aby aplikacje mogy pracowa bezporednio na buforach wideo bez koniecznoci kopiowania ich do interfejsu UI, na razie jednak musimy korzysta z obiektu Surface i pokaemy,
jak naley to robi. Omawiany przykadowy kod jest dosy dugi, zatem podzielilimy go na
czci, dziki czemu atwiej nam bdzie omawia jego poszczeglne zagadnienia. Najprawdopodobniej Czytelnik zechce pobra ten projekt z naszej strony i zaimportowa go do rodowiska Eclipse. Niezbdne instrukcje znajd si na kocu rozdziau, w podrozdziale Odnoniki. Rozpoczniemy od ukazania na listingu 19.10 wykorzystanego ukadu graficznego.
Listing 19.10. Ukad graficzny aplikacji rejestrujcej dane wideo
<?xml version="1.0" encoding="utf-8"?>
Na rysunku 19.6 widzimy powyszy ukad graficzny w penej okazaoci. Zrzut ekranu zosta
wykonany na fizycznym urzdzeniu podczas rejestrowania obrazu wideo, przedstawiajcego
okno robocze rodowiska Eclipse na stacji roboczej.
Ukad ten skada si z dwch umieszczonych obok siebie pojemnikw LinearLayout, znajdujcych si w nadrzdnym kontenerze LinearLayout. Z lewej strony widzimy pi przyciskw,
ktre nasza aplikacja bdzie nam udostpniaa i wyczaa w trakcie analizowania przykadu.
Z prawej strony zosta umieszczony gwny widok VideoView, nad nim za znajduje si napis
REJESTROWANIE, ktry zostaje wywietlony w trakcie nagrywania obrazu. Jak ju si Czytelnik prawdopodobnie domyli, wymuszamy uoenie aplikacji w trybie krajobrazowym poprzez
umieszczenie atrybutu android:screenOrientation="landscape" w znaczniku <activity>
pliku AndroidManifest.xml. Rozpocznijmy analiz aplikacji od klasy MainActivity, zaprezentowanej na listingu 19.11.
Listing. 19.11. Gwna aktywno rejestratora wideo
public class MainActivity extends Activity implements
SurfaceHolder.Callback, OnInfoListener, OnErrorListener {
private
private
private
private
private
private
private
private
private
private
private
private
633
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "w metodzie onCreate");
setContentView(R.layout.main);
mInitBtn = (Button) findViewById(R.id.initBtn);
mStartBtn = (Button) findViewById(R.id.beginBtn);
mStopBtn = (Button) findViewById(R.id.stopBtn);
mPlayBtn = (Button) findViewById(R.id.playRecordingBtn);
mStopPlayBtn = (Button) findViewById(R.id.stopPlayingRecordingBtn);
mRecordingMsg = (TextView) findViewById(R.id.recording);
mVideoView = (VideoView)this.findViewById(R.id.videoView);
}
Korzystamy w tej aplikacji ze standardowej aktywnoci, jednak implementujemy w niej rwnie trzy interfejsy. Pierwszy z nich, SurfaceHolder.Callback, suy do otrzymywania informacji o tym, kiedy obiekt Surface bdzie przygotowany do wywietlania obrazu wideo. Obiekt
Surface w naszym przypadku pochodzi z klasy VideoView. Chcemy by rwnie informowani
o wszelkich komunikatach wychodzcych z klasy MediaRecorder, dlatego implementujemy dwa
pozostae interfejsy: OnInfoListener oraz OnErrorListener. Wkrtce zajmiemy si omwieniem metod wspomnianych interfejsw.
Nasza aktywno zawiera kilka pl czonkowskich, ktre bd potrzebne pniej. Cz z nich
inicjalizujemy w metodzie onCreate(). Na razie zamiecilimy jedynie komentarz wskazujcy,
gdzie zostanie umieszczona reszta klasy MainActivity. Wspomniane metody klasy zostan ukazane na kolejnych listingach, poczwszy od listingu 19.12, na ktrym prezentujemy standardowe metody onResume() i onPause().
Listing 19.12. Kod obsugujcy wstrzymywanie i ponawianie pracy rejestratora wideo
@Override
protected void onResume() {
Log.v(TAG, "w metodzie onResume");
super.onResume();
mInitBtn.setEnabled(false);
mStartBtn.setEnabled(false);
mStopBtn.setEnabled(false);
mPlayBtn.setEnabled(false);
mStopPlayBtn.setEnabled(false);
if(!initCamera())
finish();
}
@Override
protected void onPause() {
Log.v(TAG, "w metodzie onPause");
super.onPause();
releaseRecorder();
releaseCamera();
}
Jak wida, w prezentowanym kodzie umiecilimy cakowicie standardowe metody. W metodzie onResume() ustawiamy po prostu stan pocztkowy przyciskw, a nastpnie uruchamiamy
kamer (wkrtce zapoznamy si z t metod). W metodzie onPause() musimy zwolni zarwno
obiekt MediaRecorder, jak i Camera. W ten sposb po kadym schowaniu aplikacji rejestrowanie bdzie zatrzymane, a kamera zostanie zwolniona do dyspozycji innych programw. Jeeli
uytkownik powrci do naszej aplikacji, jej dziaanie zostanie wznowione i znowu bdzie mona
rejestrowa obraz wideo. Na listingu 19.13 pokazujemy kod inicjalizujcy kamer, metody
zwrotne interfejsu SurfaceHolder.Callback, a take metody obsugujce zwalnianie obiektw
Camera i MediaRecorder.
Listing 19.13. Metody initCamera() oraz zwalniajce kamer
private boolean initCamera() {
try {
mCamera = Camera.open();
Camera.Parameters camParams = mCamera.getParameters();
mCamera.lock();
//mCamera.setDisplayOrientation(90);
// Mona rwnie ustawi tutaj inne parametry i zastosowa:
//mCamera.setParameters(camParams);
mHolder = mVideoView.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
catch(RuntimeException re) {
Log.v(TAG, "Nie mozna zainicjalizowac obiektu Camera");
re.printStackTrace();
return false;
}
return true;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v(TAG, "w metodzie surfaceCreated");
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (IOException e) {
Log.v(TAG, "Nie mozna uruchomic podgladu");
e.printStackTrace();
}
mInitBtn.setEnabled(true);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
635
637
639
stopRecording();
Toast.makeText(this, "Nastpi bd rejestrowania. Zatrzymywanie rejestrowania.",
Toast.LENGTH_SHORT).show();
}
}
Te dwie metody zwrotne s do siebie bardzo podobne. Jedyna rnica pomidzy nimi polega
na okolicznociach, w jakich s wywoywane. W metodzie onInfo() komunikaty nie s uznawane za wyniki bdw. Metoda ta moe by wywoywana w przypadku osignicia limitu dugoci rejestrowanego materiau albo maksymalnego rozmiaru pliku, jeeli takie ograniczenia
znajd si w rejestratorze. W przypadku metody onError() dokumentacja nie wyraa si zbyt
jasno na temat okolicznoci jej wywoywania, ale moe tak by w przypadku, gdy wyczerpuje
si miejsce w magazynie, na ktrym zapisywane s rejestrowane dane. Jeeli metoda onInfo()
zostanie wywoana z powodu osignicia limitu czasowego lub jeli pojawi si jaki bd rejestrowania, zostanie ono zatrzymane.
Podobnie jak w przypadku procesu rejestrowania audio, tak i teraz musimy przydzieli uprawnienia dla rejestracji dwiku (android.permission.RECORD_AUDIO) i dostpu do karty SD
(android.permission.WRITE_EXTERNAL_STORAGE), a dodatkowo musimy jeszcze doda uprawnienie dostpu do kamery (android.permission.CAMERA). Na listingu zamieszczamy kod pliku
AndroidManifest.xml. Zauwamy, e wymuszamy poziom orientacj naszej aplikacji, dlatego
wanie plik ukadu graficznego znajduje si w katalogu /res/layout-land/main.xml.
Listing 19.16. Plik AndroidManifest.xml rejestratora wideo
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.record.video"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="4" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
</manifest>
641
import
import
import
import
import
import
import
import
android.app.Activity;
android.content.Intent;
android.net.Uri;
android.os.Bundle;
android.util.Log;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
startRecording();
}});
}
public void startRecording() {
Intent intt = new Intent("android.provider.MediaStore.RECORD_SOUND");
startActivityForResult(intt, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 0:
if (resultCode == RESULT_OK) {
Uri recordedAudioPath = data.getData();
Log.v("Demo", "Identyfikator Uri wynosi " + recordedAudioPath.toString());
}
}
}
}
Kod z listingu 19.17 tworzy intencj dajc od systemu przeprowadzenia procesu rejestracji dwiku. Intencja zostaje uruchomiona wobec aktywnoci poprzez wywoanie metody
startActivityForResult() oraz przekazanie tej intencji wraz z obiektem requestCode. Kiedy
dana aktywno wykona swoj prac, zostaje wywoana metoda onActivityResult() wraz
z obiektem requestCode. Jak wynika z kodu metody onActivityResult(), naley wyszuka
obiekt requestCode odpowiadajcy obiektowi przekazanemu klasie startActivityForResult(),
a nastpnie uzyska identyfikator URI zapisanego pliku poprzez wywoanie metody data.
getData(). Moemy nastpnie przekaza otrzymany identyfikator URI do intencji, aby odtworzy nagranie. Utworzony na listingu 19.17 interfejs UI zosta zilustrowany na rysunku 19.7.
Rysunek 19.7. Wbudowany rejestrator dwiku przed nagraniem (po lewej) i po nagraniu (po prawej)
643
Na rysunku 19.7 zostay zaprezentowane dwa zrzuty ekranu. Obraz z lewej strony przedstawia
rejestrator dwiku w trakcie procesu nagrywania, a na zrzucie ekranu z prawej strony widoczny
jest interfejs UI aktywnoci po zatrzymaniu procesu rejestracji.
W podobny sposb klasa MediaStore dostarcza intencj umoliwiajc wykonanie zdjcia.
Przykadem jest kod z listingu 19.18.
Listing 19.18. Uruchamianie intencji odpowiedzialnej za wykonywanie zdj
<?xml version="1.0" encoding="utf-8"?>
import
import
import
import
import
import
import
import
import
import
android.app.Activity;
android.content.ContentValues;
android.content.Intent;
android.net.Uri;
android.os.Bundle;
android.provider.MediaStore;
android.provider.MediaStore.Images.Media;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
Klasa aktywnoci pokazana na listingu 19.18 definiuje metod captureImage(). Dziki niej
system tworzy intencj, ktrej dziaanie nosi nazw MediaStore.ACTION_IMAGE_CAPTURE.
Po uruchomieniu tej intencji na pierwszym planie ekranu wywietla si okno aplikacji aparatu fotograficznego i uytkownik moe wykona zdjcie. Poniewa wczeniej utworzylimy
identyfikator URI, moemy umieci dodatkowe informacje na temat zdjcia, zanim zostanie
zrobione. Do tego celu suy nam klasa ContentValues. Poza atrybutami TITLE i DESCRIPTION
mona doda do obiektu values rwnie inne parametry. Pen list atrybutw mona znale,
przegldajc klas MediaStore.Images.ImageColumns. Po wykonaniu zdjcia zostaje wywoana metoda zwrotna onActivityResult(). W naszym przykadzie uylimy dostawcy treci
multimediw do utworzenia nowego pliku. Moglibymy rwnie utworzy nowy identyfikator
URI z nowego pliku na karcie SD, tak jak poniej:
myPicture = Uri.fromFile(new
File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM) +
"/100ANDRO/imageCaptureIntent.jpg"));
645
Innymi sowy, jeli w magazynie multimediw nie znalazy si informacje o nowych plikach,
dane tego typu mona dodawa za pomoc klasy MediaScannerConnection. Informacje te mog
by pniej wykorzystywane przez inne aplikacje. Zobaczmy, jak to dziaa (listing 19.19).
Listing 19.19. Dodawanie pliku do magazynu MediaStore
<?xml version="1.0" encoding="utf-8"?>
import
import
import
import
import
import
import
import
import
import
import
java.io.File;
android.app.Activity;
android.content.Intent;
android.media.MediaScannerConnection;
android.media.MediaScannerConnection.MediaScannerConnectionClient;
android.net.Uri;
android.os.Bundle;
android.util.Log;
android.view.View;
android.widget.EditText;
android.widget.Toast;
Na listingu 19.19 zostaa ukazana klasa aktywnoci umoliwiajca dodawanie pliku do magazynu
MediaStore. Jeli proces dodawania przebiegnie pomylnie, informacja o danym pliku zostanie
wywietlona uytkownikowi poprzez intencj. Poza wzrokiem uytkownika klasa MediaStore
sprawdza typ pliku i inne dotyczce go informacje. W przypadku klasy MediaStore moemy
jako drugi argument metody scanFile() poda typ MIME. Jeeli klasa MediaStore nie potrafi
rozpozna typu pliku po rozszerzeniu jego nazwy, nie zostanie on dodany do magazynu. Jeeli
natomiast plik jest akceptowany przez klas MediaStore, zostaje umieszczony wpis wewntrz
bazy danych dostawcy multimediw. Sam plik nie zostaje przeniesiony. Teraz jednak dostawca
multimediw ma dostp do wszystkich wanych informacji na temat tego pliku. Jeeli dodalimy plik obrazu, moemy uruchomi aplikacj Gallery i go obejrze. W przypadku pliku muzycznego zostanie on odtworzony w aplikacji Music.
647
Jeeli chcemy przejrze zawarto bazy danych dostawcy multimediw, otwieramy okno narzdzi, nastpnie uruchamiamy aplikacj adb shell i otwieramy plik /data/data/com.android.
providers.media/databases znajdujcy si na urzdzeniu. Powinny si tu rwnie znajdowa
zewntrzne pliki bazodanowe, kady reprezentujcy kart SD. Poniewa w telefonie obsugujcym system Android mona umieci kilka kart SD, w katalogu tym moe si znajdowa wiele
plikw odpowiadajcych tym kartom. W celu przegldania tabel bazodanowych umieszczonych w tych plikach moemy posuy si aplikacj sqlite3. Istniej oddzielne tabele dla plikw
audio, wideo i obrazw. W rozdziale 4. mona znale dodatkowe informacje na temat korzystania z aplikacji sqlite3.
W ramach wiczenia Czytelnik moe napisa prost aplikacj wykonujc wycznie powysze
polecenie w metodzie onCreate().
Na tym zakoczymy omawianie interfejsw API multimediw. Mamy nadziej, e dla Czytelnika
odtwarzanie i rejestrowanie multimediw nie bdzie skomplikowanym procesem.
Odnoniki
Poniej prezentujemy cza do zasobw dotyczcych zagadnie, ktre Czytelnik moe zechcie
dokadniej zgbi:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu peen zestaw projektw
bezporednio zwizanych z niniejsz ksik. Projekty dotyczce tego rozdziau
zostay umieszczone w katalogu ProAndroid3_R19_Multimedia. Doczylimy tu take
plik Czytaj.TXT, w ktrym zosta dokadnie opisany sposb importowania tych
projektw do rodowiska Eclipse.
http://developer.android.com/guide/topics/media/jet/jetcreator_manual.html
instrukcja obsugi narzdzia JETCreator. Za jego pomoc moemy utworzy plik
dwikowy JET, ktry nastpnie bdzie odtwarzany poprzez klas JetPlayer.
Narzdzie to jest dostpne wycznie w systemach Windows i Mac OS. Aby sprawdzi
dziaanie klasy JetPlayer, naley wczyta, skompilowa i wczy przykadowy
projekt JetBoy, dostpny w zestawie SDK. Warto wspomnie, e przycisk Fire znajduje
si na rodku podkadki kierunkowej.
Podsumowanie
W tym rozdziale zajmowalimy si struktur multimediw w Androidzie. Pokazalimy, w jaki
sposb mona odtwarza pliki audio i wideo. Omwilimy take sposoby rejestrowania dwikw
i obrazw wideo zarwno bezporednio, jak i za pomoc intencji.
W nastpnym rozdziale zwrcimy uwag na grafik trjwymiarow poprzez omwienie zastosowania technologii OpenGL w aplikacjach tworzonych dla systemu Android.
R OZDZIA
20
Programowanie grafiki
trjwymiarowej
za pomoc biblioteki OpenGL
651
Standard OpenGL jest obecnie zarzdzany przez konsorcjum nazwane grup Khronos
(www.khronos.org), zaoone w 2000 roku przez takie firmy, jak NVIDIA, Sun Microsystems,
ATI Technologies oraz SGI. Informacje dotyczce specyfikacji technologii OpenGL mona
znale na stronie konsorcjum:
www.khronos.org/opengl/
Pod poniszym adresem jest dostpna oficjalna strona dokumentacji biblioteki OpenGL:
www.opengl.org/documentation/
Po otwarciu powyszej strony uzyskujemy dostp do podrcznikw oraz zasobw internetowych dotyczcych biblioteki OpenGL. Spord nich klasyczn pozycj jest ksika OpenGL
Programming Guide: The Official Guide to Learning OpenGL, Version 1.1, znana take jako
czerwona ksiga technologii OpenGL. Jest ona dostpna pod adresem:
www.glprogramming.com/red/
Podrcznik ten jest cakiem dobrze i przystpnie napisany. Mielimy jednak pewne problemy
ze zrozumieniem natury jednostek i wsprzdnych, niezbdnych podczas rysowania. Sprbujemy wyjani te wane pojcia na podstawie sporzdzanych przez nas i widzianych na
ekranie obiektw za pomoc standardu OpenGL. Pojcia te dotycz konfigurowania kamery
OpenGL i definiowania bryy widzenia (ang. viewing box), zwanej take pojemnoci widzenia
(ang. viewing volume) lub ostrosupem widzenia (ang. frustum).
OpenGL ES
Grupa Khronos jest rwnie odpowiedzialna za dwa dodatkowe standardy powizane z technologi OpenGL: interfejs OpenGL ES oraz interfejs graficzny platformy natywnej EGL (zwany
w skrcie interfejsem EGL). Jak ju wspomnielimy, interfejs OpenGL ES jest mniejsz wersj
standardu OpenGL, przeznaczon dla systemw wbudowanych.
Proces JCP (ang. Java Community Process) rwnie umoliwia zaprojektowanie abstrakcji
obiektowej standardu OpenGL dla urzdze mobilnych. Abstrakcja ta nosi nazw interfejsu
M3G (ang. Mobile 3D Graphics mobilna grafika trjwymiarowa). Omwimy krtko
ten interfejs w punkcie M3G inny standard grafiki trjwymiarowej rodowiska Java.
Zasadniczo standard EGL jest interfejsem czcym system operacyjny z interfejsami renderujcymi, dostpnymi w rodowisku OpenGL ES. Poniewa standardy OpenGL i OpenGL ES
s oglnymi interfejsami sucymi do rysowania, kady system operacyjny musi im zapewni
standardowe rodowisko bazowe umoliwiajce wspdziaanie. Od wersji 1.5 rodowiska Android SDK informacje dotyczce tych parametrw platformy s cakiem skutecznie ukrywane.
Zajmiemy si tym dokadniej w podrozdziale Tworzenie interfejsu pomidzy standardem
OpenGL ES a Androidem.
Docelowymi urzdzeniami standardu OpenGL ES s telefony komrkowe, sprzt RTV, a nawet
pojazdy. Poniewa standard ten musia zosta mocno okrojony w porwnaniu do podstawowej
wersji biblioteki OpenGL, usunito wiele przydatnych funkcji. Na przykad nie ma funkcji bezporedniego rysowania prostoktw; naley w tym celu narysowa dwa trjkty.
Podczas nauki obsugi biblioteki OpenGL w Androidzie naley koncentrowa si przede wszystkim na interfejsie OpenGL ES i jego powizaniach z systemem poprzez jzyki Java oraz EGL.
Dokumentacj dla interfejsu OpenGL ES mona znale tutaj:
www.khronos.org/opengles/documentation/opengles1_0/html/index.html
653
Obiektowa natura interfejsu M3G izoluje go od rodowiska OpenGL ES. Ze szczegami mona
si zapozna na poniszej stronie specyfikacji JSR 184:
www.jcp.org/en/jsr/detail?id=184
Interfejsy API dla rodowiska M3G s dostpne w pakiecie Java noszcym nazw javax.
microedition.m3g.*.
W rodowisku M3G zaimplementowano interfejs API wyszego poziomu w stosunku do rodowiska OpenGL ES, wic jego nauka powinna przebiega atwiej. Jednak na razie trudno oceni, jak ta technologia bdzie si sprawowaa na handheldach. Na razie Android nie obsuguje
technologii M3G.
Dotychczas wymienilimy opcje dostpne w przestrzeni OpenGL pod ktem urzdze przenonych. Omwilimy rodowisko OpenGL ES i wspomnielimy o standardzie M3G. Przejdmy
teraz do zapoznania si z podstawami biblioteki OpenGL.
Wymienione poniej interfejsy API, jako niezbdne do zrozumienia dziaania bibliotek OpenGL
i OpenGL ES, zostan szczegowo omwione:
glVertexPointer,
glDrawElements,
glColor,
glClear,
gluLookAt,
glFrustum,
glViewport.
Podczas omawiania tych interfejsw przedstawimy:
wykorzystywanie podstawowych interfejsw API rysowania za pomoc rodowiska
OpenGL ES,
czyszczenie palety,
okrelanie kolorw,
uywanie wsprzdnych i kamery biblioteki OpenGL.
//p1: (x1,y1,z1)
0.5f, -0.5f, 0,
0.0f, 0.5f, 0
655
//p2: (x1,y1,z1)
//p3: (x1,y1,z1)
};
Moemy rwnie zaoy, e pocztek ukadu wsprzdnych znajduje si porodku wywietlanego obrazu. O z posiada wartoci ujemne w kierunku dalszego planu (oddala si od widza),
a dodatnie w stron pierwszego planu (zblia si w kierunku widza). Wartoci osi x s dodatnie
w kierunku prawym, a ujemne w kierunku lewym. Jednak te wsprzdne zale rwnie od
kierunku, z jakiego ogldamy scen.
Aby narysowa punkty widoczne na listingu 20.1, musimy przekaza je bibliotece OpenGL ES poprzez metod glVertexPointer. Jednak w celu zachowania wydajnoci metoda glVertexPointer
przyjmuje natywny bufor niezaleny od jzyka programowania, a nie macierz wartoci zmiennoprzecinkowych. W tym celu musimy przeksztaci macierz jzyka Java do akceptowanego
bufora natywnego, przypominajcego struktur jzyka C. Dokonujemy tego za pomoc klas
java.nio. Na listingu 20.2 zosta zaprezentowany przykad wykorzystania buforw nio.
Listing 20.2. Tworzenie zmiennoprzecinkowych buforw nio
jva.nio.ByteBuffer vbb = java.nio.ByteBuffer.allocateDirect(3 * 3 * 4);
vbb.order(ByteOrder.nativeOrder());
java.nio.FloatBuffer mFVertexBuffer = vbb.asFloatBuffer();
glDrawElements
Po okreleniu zbioru punktw za pomoc metody glVertexPointer stosujemy metod
glDrawElements do narysowania tych punktw w postaci jednego z prostych ksztatw dopuszczalnych przez bibliotek OpenGL ES. Odnotujmy fakt, e biblioteka OpenGL jest maszyn
stanow. Zapamituje w sposb narastajcy wartoci ustanowione przez jedn metod podczas
wywoywania kolejnej metody. Nie musimy wic jawnie przekazywa punktw ustanowionych
przez metod glVertexPointer metodzie glDrawElements. Ta ostatnia bdzie z nich korzystaa
w sposb niejawny. Listing 20.4 ukazuje przykad zastosowania tej metody wraz z dopuszczalnymi argumentami.
657
// Rodzaj ksztatu
GL10.GL_TRIANGLE_STRIP,
// Liczba indeksw
3,
Zauwamy, e pierwszy trjkt skada si z punktw p1, p2, p3, a drugi z punktw p2, p3, p4.
Dziki tej wiedzy moemy poprzez drugi argument metody glDrawElements okreli liczb indeksw w buforze indeksw.
Trzeci argument metody glDrawElements (listing 20.4) wskazuje typ wartoci w macierzy
indeksw czy jest to typ unsigned short (GL_UNSIGNED_SHORT), czy unsigned byte
(GL_UNSIGNED_BYTE).
//Umieszczamy go w buforze
for (int i=0;i<3;i++)
{
mIndexBuffer.put(myIndecesArray[i]);
}
Skoro wiemy ju, jak dziaa metoda mIndexBuffer (listing 20.5), moemy cofn si do listingu
20.4, aby lepiej zrozumie ide tworzenia bufora indeksu i jego przeksztacania.
Bufor indeksu, zamiast tworzy nowe punkty, indeksuje jedynie macierz punktw
wskazan przez metod glVertexPointer. Jest to moliwe, poniewa biblioteka
OpenGL zapamituje za pomoc stanw zasoby ustanowione przez poprzednie wywoania.
glClear
Metod glClear stosujemy do czyszczenia powierzchni rysowania. Za jej pomoc moemy wyzerowa kolor, gbi oraz rodzaj wykorzystywanych szablonw. Zerowany element okrelamy
poprzez odpowiedni sta: GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT lub GL_STENCIL_
BUFFER_BIT.
Bufor koloru jest odpowiedzialny za widoczne na ekranie piksele, zatem jego wyczyszczenie
spowoduje zniknicie wszelkich kolorw z powierzchni. Bufor gbi jest zwizany z pikselami
widzianymi w trjwymiarowej scenie, w zalenoci od odlegoci obiektu od kamery.
Bufor szablonowy jest nieco zbyt skomplikowany, aby go tutaj omwi, wystarczy jednak wiedzie, e jest uywany do tworzenia efektw graficznych na podstawie pewnych dynamicznych
kryteriw, a metoda glClear umoliwia jego wyczyszczenie.
659
Szablon jest obiektem, dziki ktremu moemy wielokrotnie powiela proces rysowania.
Jeli na przykad uywamy aplikacji Microsoft Office Visio, wszystkie obiekty zapisywane
jako pliki *.vss s szablonami. W wiecie rzeczywistym tworzymy szablon poprzez
wycicie wzoru w papierze lub innej paskiej powierzchni. Nastpnie przerysowujemy
za pomoc szablonu jego obwiedni na arkuszu, a po zdjciu szablonu powstaje
wraenie powielenia rysunku. Widziany obraz zaley od aktywnych szablonw. Wyczyszczenie
wszystkich szablonw spowoduje, e wszystkie rysowane elementy bd widoczne.
Dla naszych celw moemy zastosowa poniszy kod do wyczyszczenia bufora koloru:
//Czyci powierzchni ze wszelkich kolorw
gl.glClear(gl.GL_COLOR_BUFFER_BIT);
glColor
Metoda glColor jest uywana do ustawienia domylnego koloru dla nastpnego rysowanego
obiektu. W poniszym segmencie kodu metoda glColor4f generuje kolor czerwony:
//Ustanawia biecy kolor
glColor4f(1.0f, 0, 0, 0.5f);
Kamera i wsprzdne
W procesie rysowania w przestrzeni trjwymiarowej musimy w pewnym momencie rzutowa
trjwymiarowy widok na dwuwymiarowy ekran tak samo jak w wiecie rzeczywistym podczas rejestrowania trjwymiarowej sceny za pomoc kamery. Taka symbolika jest formalnie
uznana w standardzie OpenGL, zatem wiele koncepcji jest w nim wyjanianych za pomoc pojcia kamery.
Jak zobaczymy w tym punkcie, widoczna cz obiektu rysowanego zaley od pooenia kamery,
kierunku ustawienia jej obiektywu, orientacji kamery (moe by na przykad postawiona do gry
nogami lub przechylona), poziomu przyblienia oraz rozmiaru kliszy.
Wymienione aspekty rzutowania obrazu trjwymiarowego na dwuwymiarowy ekran s kontrolowane przez trzy metody:
gluLookAt kontroluje kierunek, w jakim jest ustawiony obiektyw kamery.
glFrustum kontroluje objto widzenia, poziom powikszenia lub odlego
(od bd do obiektu).
glViewport kontroluje rozmiar ekranu lub kliszy.
Przygldajc si rysunkowi 20.1, moemy si zastanawia, dlaczego mamy do czynienia z osiami x i z, a nie x i y. Stosujemy tu standardow konwencj biblioteki OpenGL, zgodnie z ktr
kamera jest skierowana na o z, gdy paszczyzn scenerii tworz osie x i y. Taka konwencja
sprawdza si, poniewa zazwyczaj o z zostaje powizana z osi gbi.
661
Po umieszczeniu aparatu na statywie musimy sprawdzi, jaki fragment sceny chcemy uchwyci
w obiektywie. Skierujmy obiektyw urzdzenia w stron, w ktr patrzymy. Ten odlegy punkt,
na ktry spogldamy, nazywany jest punktem widoku (ang. viewing point) lub punktem spogldania (ang. look-at point). Okrelajc ten punkt, definiujemy w rzeczywistoci kierunek patrzenia. Jeli nasz punkt widoku bdzie posiada wsprzdne (0, 0, 0), to aparat bdzie spoglda z odlegoci 5 przy zaoeniu, e wsprzdne aparatu wynosz (0, 0, 5) jednostek
wzdu osi z na pocztek ukadu wsprzdnych. Zostao to ukazane na rysunku 20.1.
Wyobramy sobie dalej, e w punkcie pocztkowym ukadu wsprzdnych zosta umieszczony
prostopadocienny budynek. Chcemy zrobi mu zdjcie nie w pozycji wertykalnej, a w horyzontalnej. Co robimy? Oczywicie nie zmieniamy pooenia ani kierunku spogldania aparatu,
musimy go jednak obrci o 90 stopni (analogicznie do przechylania gowy na boki). Jest to
orientacja aparatu spogldajcego na okrelony punkt widoku. Orientacja taka nosi nazw
wektora gry (ang. up vector).
Wektor gry w prosty sposb okrela orientacj aparatu (gra, d, lewo, prawo lub pod ktem).
Orientacja aparatu jest rwnie okrelana za pomoc punktu. Wyobramy sobie lini odchodzc od rodka ukadu wsprzdnych nie od rodka aparatu, a od rodka ukadu wsprzdnych wiata do tego punktu. Kt utworzony pomidzy osiami a t lini wyznacza wanie orientacj aparatu.
Na przykad wektor gry dla aparatu moe przybra warto (0, 1, 0), a nawet (0, 15, 0), co da
ten sam efekt. Punkt (0, 1, 0) wskazuje punkt odchodzcy od pocztku ukadu wsprzdnych
po osi y w gr. Oznacza to, e ustawimy aparat w pozycji pionowej. W przypadku wektora
(0, 1, 0) obrcimy urzdzenie do gry nogami. W obydwu przypadkach aparat umieszczony
jest cigle w tym samym punkcie (0, 0, 5) i spoglda na rodek ukadu wsprzdnych (0, 0, 0).
Wymienione wsprzdne moemy podsumowa w nastpujcy sposb:
(0, 0, 5) punkt oczny (pooenie kamery).
(0, 0, 0) punkt spogldania (kierunek, w ktrym kamera jest zwrcona).
(0, 1, 0) wektor gry (orientacja pozioma, pionowa lub nachylona).
Wszystkie trzy punkty punkt oczny, punkt spogldania oraz wektor gry mog zosta
zdefiniowane w metodzie gluLookAt w nastpujcy sposb:
gluLookAt(gl, 0,0,5,
0,0,0,
0,1,0);
Poniewa w kodzie z listingu 20.6 przypisalimy szczytowi bryy warto 1, a jej spodniej cianie
warto -1, wysoko przedniej ciany wynosi 2 jednostki. Rozmiary lewej i prawej strony ostrosupa okrelamy za pomoc proporcjonalnych liczb, biorc pod uwag proporcj obrazu. Z tego
wanie powodu kod wykorzystuje wysoko i szeroko okna do okrelenia proporcji. Zakada
on rwnie, e obszar dziaania bdzie znajdowa si pomidzy 3. a 7. jednostk wzdu osi
z. Wszystkie obiekty narysowane poza tymi wsprzdnymi, wzgldnymi wobec aparatu, bd
niewidoczne.
663
Poniewa umiecilimy aparat w punkcie (0, 0, 5) i skierowalimy go w stron punktu (0, 0, 0),
punkt zlokalizowany trzy jednostki od aparatu w stron pocztku ukadu wsprzdnych znajduje si w punkcie (0, 0, 2), a siedem jednostek od kamery znajduje si punkt (0, 0, 2). W ten
sposb paszczyzna pocztku ukadu wsprzdnych znajduje si dokadnie porodku trjwymiarowej bryy.
Teraz ju wiemy, jak wielka jest nasza objto widzenia. Istnieje jeszcze jeden interfejs API,
odwzorowujcy te rozmiary na ekranie glViewport.
Jeeli wysoko naszego okna lub widoku wynosi 100 pikseli, a wysoko ostrosupa widzenia
wynosi 10 pikseli, to kada jednostka logiczna wsprzdnych zostanie przetumaczona na
10 pikseli wsprzdnych wiata.
Dotychczas omwilimy niektre podstawowe, istotne pojcia dotyczce grafiki OpenGL. Zrozumienie tych podstaw przyda si podczas nauki programowania za pomoc biblioteki OpenGL.
Po spenieniu tego warunku moemy rozpocz omawianie elementw wymaganych do wywoania opisanych powyej interfejsw API.
665
Klasa ta jest bardzo poyteczna, poniewa pozwala na skupienie si jedynie na metodach rysowania. Wykorzystamy j do utworzenia naszej prostej klasy SimpleTriangleRenderer; listing
20.10 ukazuje jej kod rdowy.
Listing 20.10. Klasa SimpleTriangleRenderer
//nazwa pliku: SimpleTriangleRenderer.java
public class SimpleTriangleRenderer extends AbstractRenderer
{
0.5f, -0.5f, 0,
0.0f, 0.5f, 0
};
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i*3+j]);
}
}
short[] myIndecesArray = {0,1,2};
for (int i=0;i<3;i++)
{
mIndexBuffer.put(myIndecesArray[i]);
}
mFVertexBuffer.position(0);
mIndexBuffer.position(0);
}
//przesonita metoda
protected void draw(GL10 gl)
{
gl.glColor4f(1.0f, 0, 0, 0.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, VERTS,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
}
Chocia wydaje si, e powyszy listing ma znaczne rozmiary, wikszo kodu suy do definiowania wierzchokw i konwertowania ich z buforw kodu jzyka Java do buforw nio. Sama
metoda draw skada si jedynie z trzech wierszy: ustanowienia koloru, ustanowienia wierzchokw i rysowania.
W naszym kodzie nigdy nie uwalniamy buforw nio, chocia przydzielamy im pami.
Zatem w jaki sposb s one uwalniane? Jak wykorzystanie tej pamici wpywa na
bibliotek OpenGL?
Na podstawie bada stwierdzilimy, e pakiet java.nio przydziela przestrze pamici
spoza stosu Java. Pami ta moe by bezporednio wykorzystana przez takie systemy,
jak OpenGL, File I/O i tak dalej. W rzeczywistoci bufory nio s obiektami Java, ktre
ostatecznie wskazuj na bufor natywny. Te obiekty nio s odmiecane (ang. garbage
collection gc). Oznacza to, e po wykonaniu pracy oczyszczaj pami natywn.
Programy Java nie musz przeprowadza adnych specjalnych operacji, aby zwolni
pami.
667
Jednak proces odmiecania nie zostanie przeprowadzony, dopki w stosie Java istnieje
wykorzystywana pami. Oznacza to, e mimo wykorzystania pamici natywnej proces
gc moe nie zosta uruchomiony. W internecie mona znale informacje na temat
wyjtku braku pamici uruchamiajcego odmiecanie. Po wystpieniu tego wyjtku
mona sprawdzi, czy pami staa si ju dostpna.
W warunkach standardowych to jest istotne w przypadku biblioteki OpenGL
moemy przydziela bufory natywne i nie musimy si martwi jawnym zwalnianiem
przydzielonej pamici, poniewa czyszczenie pamici wykonuje proces gc.
//mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
setContentView(mTestHarness);
}
@Override
protected void onResume() {
super.onResume();
mTestHarness.onResume();
}
@Override
protected void onPause() {
super.onPause();
mTestHarness.onPause();
}
}
Wyjanijmy kilka kluczowych elementw tego kodu rdowego. Fragment, ktry tworzy
widok GLSurfaceView, wyglda nastpujco:
mTestHarness = new GLSurfaceView(this);
Nastpny wiersz oznacza, e nie wymagamy wyboru specjalnej konfiguracji biblioteki EDL i e
wystarcz ustawienia domylne:
mTestHarness.setEGLConfigChooser(false);
Jedna z dwch metod umieszczonych w dalszej czci kodu umoliwia proces animacji:
mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
//mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
Oczywicie niezbdne jest zarejestrowanie aktywnoci w pliku manifecie Androida, na przykad tak:
<activity android:name=".OpenGLTestHarnessActivity"
android:label="rodowisko testowe OpenGL"/>
Chocia utworzenie samodzielnej aktywnoci, takiej jak OpenGLTestHarnessActivity z listingu 20.11, jest cakowicie rozsdnym zachowaniem, chcielibymy zaproponowa alternatyw,
ktra bdzie o wiele lepiej pasowaa do tematyki poruszanej w niniejszym rozdziale.
Nasza propozycja jest zwizana z umieszczeniem wielu przykadowych fragmentw kodu w obrbie rozdziau. Gdybymy osobno przedstawiali aktywnoci dla poszczeglnych aplikacji, zapenilibymy rozdzia kodami bardzo podobnymi do przedstawionego na listingu 20.11, ktre
nie wprowadzayby niczego nowego. Ponadto kada taka aktywno musi by zarejestrowana
w pliku manifecie.
Majc to na uwadze, utworzymy ogln aktywno pozwalajc na testowanie wszystkich omawianych tu przykadowych projektw. Jej kod zosta umieszczony na listingu 20.12. Moe si on
wydawa do rozbudowany w porwnaniu do pierwotnej wersji, jeeli jednak przyjrzymy si
odpowiedzi menu zawartej w zasobie R.id.mid_OpenGL_SimpleTriangle, zauwaymy, e zasadniczo zachowanie aplikacji nie ulega zmianie. Wraz ze zwikszaniem liczby elementw menu
ronie liczba instrukcji if, po jednej dla kadego przykadu.
Pozostae opcje menu bd omawiane w dalszej czci rozdziau. Po kodzie z listingu 20.12 zaprezentujemy zawarto pliku main_menu.xml, po czym dokadniej omwimy nasz wielozadaniow aktywno.
669
Kod menu z listingu 20.13 obsuguje aktywno widoczn na listingu 20.12. Dokadniej mwic,
jest to plik res/menu/main_menu.xml. Wyprzedzilimy nieco fakty i utworzylimy wszystkie
elementy menu dotyczce przykadw zamieszczonych w tym rozdziale.
Listing 20.13. Plik gwnego menu
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Przygldajc si zawartoci pliku menu, moemy si domyla, jakie silniki renderujce zostan
zademonstrowane. Jeeli cofniemy si do aktywnoci z listingu 20.12, stwierdzimy, e przecza
ona silniki renderowania na podstawie zdefiniowanych w tym pliku identyfikatorw menu.
W jaki sposb ta aktywno uzyskuje identyfikatory poszczeglnych elementw menu? Odbywa
si to dziki nastpujcemu fragmentowi kodu (zosta on skopiowany z listingu 20.12):
Intent intent = getIntent();
int mid = intent.getIntExtra("com.ai.menuid",
R.id.mid_OpenGL_Current);
671
Do umoliwienia procesu kompilacji potrzebny jest nam jeszcze plik ukadu graficznego.
Zosta on zaprezentowany na listingu 20.15.
Listing 20.15. Ukad graficzny aktywnoci TestOpenGLMainDriver (layout/main.xml)
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Prosta aktywno gwna. Kliknij przycisk menu, aby kontynuowa"
/>
</LinearLayout>
Oczywicie, w systemie Android konieczny jest jeszcze plik manifest. Zapoznamy si z jego kodem na listingu 20.16.
Listing 20.16. Plik AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.OpenGL"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
673
0,0,0,
0,-1,0);
Jeli przeprowadzimy tak operacj, ujrzymy odwrcony trjkt z rysunku 20.4. Aby wprowadzi
tak zmian, powinnimy znale waciw metod w pliku AbstractRenderer.java (listing 20.9).
Spjrzmy teraz, co si stanie, jeli zmienimy ostrosup widzenia (zwany take objtoci lub
bry widzenia). Dziki poniszej linii kodu zwiksza si wysoko i szeroko bryy widzenia
o wspczynnik 4 (wymiary zostay zilustrowane na rysunku 20.1). Przypominamy, e pierwsze
cztery argumenty klasy glFrustum definiuj przedni cian bryy widzenia. Mnoc kad
warto przez 4, powikszylimy bry widzenia czterokrotnie, tak jak poniej:
gl.glFrustumf(-ratio * 4, ratio * 4, -1 * 4, 1 *4, 3, 7);
W wyniku tego kodu trjkt zostaje zmniejszony, poniewa jego rozmiary nie ulegy zmianie,
ale brya widzenia zostaa powikszona (rysunek 20.5). Wywoanie tej metody pojawia si w klasie
AbstractRenderer.java (listing 20.9).
675
1.0f, -1.0f, 0,
0.0f, 1.0f, 0,
1.0f, 1.0f, 0
};
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i*3+j]);
}
}
short[] myIndecesArray = {0,1,2, 0,2,3};
for (int i=0;i<6;i++)
{
mIndexBuffer.put(myIndecesArray[i]);
}
mFVertexBuffer.position(0);
mIndexBuffer.position(0);
Po dodaniu tego fragmentu moemy ponownie uruchomi aplikacj i wybra tym razem opcj
Dwa trjkty (rysunek 20.6). Zwrmy uwag, e dziki konstrukcji klasy MultiViewTestHarness
nie ma ju koniecznoci tworzenia nowej aktywnoci, a tym samym rwnie rejestrowania jej
w pliku manifecie. W dalszym cigu bdziemy wykorzystywa ten mechanizm poprzez ustawiczne dodawanie klauzuli if dla kadego nastpnego przykadu.
677
AnimatedSimpleTriangleRenderer
Klasa AnimatedSimpleTriangleRenderer bardzo przypomina klas SimpleTriangleRenderer
(listing 20.10), nie liczc tego, co si dzieje w metodzie onDraw. W metodzie tej definiujemy
nowy kt dla obrotu wykonywanego co cztery sekundy. Poniewa obraz bdzie systematycznie
przerysowywany, odniesiemy wraenie powoli obracajcego si trjkta. Listing 20.20 zawiera
pen implementacj klasy AnimatedSimpleTriangleRenderer.
Listing 20.20. Kod rdowy klasy AnimatedSimpleTriangleRenderer
//nazwa pliku: AnimatedSimpleTriangleRenderer.java
public class AnimatedSimpleTriangleRenderer extends AbstractRenderer
{
private int scale = 1;
0.5f, -0.5f, 0,
0.0f, 0.5f, 0
};
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i*3+j]);
//przesonita metoda
protected void draw(GL10 gl)
{
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
gl.glRotatef(angle, 0, 0, 1.0f);
gl.glColor4f(1.0f, 0, 0, 0.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, VERTS,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
}
Po dodaniu tego kodu moemy ponownie uruchomi aplikacj i wybra z menu opcj Animowany trjkt, aby ujrze obracajcy si trjkt widoczny na rysunku 20.3. <<F1-p>>
679
Rysowanie prostokta
Zanim przejdziemy do pojcia ksztatw, musimy rozszerzy znajomo rysowania za pomoc
jawnego definiowania wierzchokw na przykadzie prostokta utworzonego z dwch trjktw.
W ten sposb rwnie Czytelnik przygotuje si do rozwijania trjkta do dowolnego wieloboku.
Mamy ju wystarczajc wiedz na temat podstawowego trjkta, pokaemy wic teraz opatrzony krtkim komentarzem kod sucy do utworzenia prostokta (listing 20.22).
Listing 20.22. Silnik renderujcy prostokt
public class SimpleRectangleRenderer extends AbstractRenderer
{
0.5f, -0.5f, 0,
0.5f, 0.5f, 0,
-0.5f, 0.5f, 0,
};
for (int i = 0; i < VERTS; i++) {
for(int j = 0; j < 3; j++) {
mFVertexBuffer.put(coords[i*3+j]);
}
//przesonita metoda
protected void draw(GL10 gl)
{
gl.glColor4f(1.0f, 0, 0, 0.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, 6,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
}
MultiViewTestHarness
Po dodaniu powyszego kodu moemy uruchomi ponownie nasz program i tym razem wybra opcj Prostokt, aby ujrze figur geometryczn ukazan na rysunku 20.7.
Praca z ksztatami
Jawne okrelanie wierzchokw figur geometrycznych moe by nuc czynnoci. Jeeli na
przykad chcemy narysowa dwudziestobok, musimy zdefiniowa 20 wierzchokw, gdzie kada
definicja wymaga okrelenia do trzech parametrw. cznie narysowanie dwudziestoboku
wymaga podania 60 wartoci. W przypadku bardziej skomplikowanych rysunkw staje si to
niewykonalne.
681
Zobaczmy, w jaki sposb uzyskalimy niezbdne wierzchoki i indeksy z ksztatu square. Chocia nie omwilimy idei uzyskiwania wierzchokw i indeksw wobec prostego ksztatu, moliwe
jest, e klasa RegularPolygon moe pochodzi od takiego podstawowego ksztatu, definiujcego
interfejs dla takiego prostego kontraktu. Na listingu 20.25 pokazalimy stosowny przykad:
Listing 20.25. Interfejs Shape
public interface Shape
{
FloatBuffer
ShortBuffer
int
}
getVertexBuffer();
getIndexBuffer();
getNumberOfIndices();
683
Skoro ju zarysowalimy sposb dziaania naszej implementacji klasy RegularPolygon, zademonstrujemy kod rdowy odpowiedzialny za implementacj tych dziaa. Listing 20.26 prezentuje cay kod tego obiektu (zwrmy uwag, e mieci si on na kilku stronach). Aby zachowa przejrzysto, zaznaczylimy nazwy funkcji oraz zamiecilimy komentarze na pocztku
kadej z nich.
Definiujemy najwaniejsze funkcje, ktrych lista zostaa umieszczona po listingu 20.26. Wane
jest, aby zrozumie proces okrelania i przekazywania wierzchokw. Jeeli nasz przykad okae
si zbyt trudny, Czytelnik nie powinien mie problemu z napisaniem wasnej wersji kodu definiujcego wierzchoki. Zauwamy take, e w kodzie tym zostay rwnie umieszczone funkcje przeprowadzajce proces nakadania tekstury. Zostan one omwione w punkcie Praca
z teksturami.
Listing 20.26. Implementacja ksztatu RegularPolygon
public class RegularPolygon
{
//**********************************************
// Constructor
//**********************************************
public RegularPolygon(float incx, float incy, float incz,
float inr,
// promie
int insides) // liczba bokw
{
cx = incx;
cy = incy;
cz = incz;
r = inr;
sides = insides;
// rodek (x, y, z)
//**********************************************
//Pobiera i konwertuje wsprzdne wierzchokw
//na podstawie rodka i promienia.
//Dziaania logiczne na ktach s przeprowadzane wewntrz funkcji
//getMultiplierArray()
//**********************************************
private void calcArrays()
{
//**********************************************
//Oblicza macierze tekstury
//Wicej informacji mona znale w punkcie powiconym teksturom
//Bardzo podobne rozwizanie.
//Tutaj wielokt musi zosta odwzorowany na kwadratowej przestrzeni
//**********************************************
private void calcTextureArrays()
{
float[] xmarray = this.getXMultiplierArray();
float[] ymarray = this.getYMultiplierArray();
//Oblicza xarray
for(int i=0;i<sides;i++)
685
{
float curm = xmarray[i];
float xcoord = 0.5f + 0.5f * curm;
sarray[i] = xcoord;
}
this.printArray(sarray, "sarray");
//Oblicza yarray
for(int i=0;i<sides;i++)
{
float curm = ymarray[i];
float ycoord = 0.5f + 0.5f * curm;
tarray[i] = ycoord;
}
this.printArray(tarray, "tarray");
}
//**********************************************
//Konwertuje macierz java wierzchokw
//do zmiennoprzecinkowego bufora nio
//**********************************************
public
{
int
int
int
int
FloatBuffer getVertexBuffer()
vertices = sides + 1;
coordinates = 3;
floatsize = 4;
spacePerVertex = coordinates * floatsize;
//x
//y
//z
totalPuts += 3;
}
Log.d("cznie wstawiono:",Integer.toString(totalPuts));
return mFVertexBuffer;
}
//**********************************************
//Konwertuje bufor tekstury do bufora nio
//**********************************************
public FloatBuffer getTextureBuffer()
{
vertices = sides + 1;
coordinates = 2;
floatsize = 4;
spacePerVertex = coordinates * floatsize;
//x
//y
totalPuts += 2;
}
Log.d("czna liczba wstawionych tekstur:",Integer.toString(totalPuts));
return mFTextureBuffer;
}
//**********************************************
//Oblicza indeksy tworzce wiele trjktw.
//Rozpoczyna od rodkowego wierzchoka (punkt 0)
//Nastpnie numeruje je zgodnie z kierunkiem ruchu wskazwek zegara, na przykad
//0, 1, 2; 0, 2, 3; 0, 3, 4... itd.
//**********************************************
public ShortBuffer getIndexBuffer()
{
short[] iarray = new short[sides * 3];
ByteBuffer ibb = ByteBuffer.allocateDirect(sides * 3 * 2);
ibb.order(ByteOrder.nativeOrder());
ShortBuffer mIndexBuffer = ibb.asShortBuffer();
for (int i=0;i<sides;i++)
{
short index1 = 0;
short index2 = (short)(i+1);
short index3 = (short)(i+2);
if (index3 == sides+1)
{
index3 = 1;
}
mIndexBuffer.put(index1);
mIndexBuffer.put(index2);
mIndexBuffer.put(index3);
iarray[i*3 + 0]=index1;
iarray[i*3 + 1]=index2;
iarray[i*3 + 2]=index3;
}
this.printShortArray(iarray, "index array");
return mIndexBuffer;
//**********************************************
//Std jest pobierana macierz ktw
//dla kadego wierzchoka i zostaje obliczony ich wspczynnik rzutowania
//na o x
//**********************************************
private float[] getXMultiplierArray()
{
float[] angleArray = getAngleArrays();
float[] xmultiplierArray = new float[sides];
for(int i=0;i<angleArray.length;i++)
{
float curAngle = angleArray[i];
float sinvalue = (float)Math.cos(Math.toRadians(curAngle));
float absSinValue = Math.abs(sinvalue);
if (isXPositiveQuadrant(curAngle))
{
sinvalue = absSinValue;
}
else
{
sinvalue = -absSinValue;
}
xmultiplierArray[i] = this.getApproxValue(sinvalue);
}
this.printArray(xmultiplierArray, "xmultiplierArray");
return xmultiplierArray;
}
//**********************************************
//Std jest pobierana macierz ktw
//dla kadego wierzchoka i zostaje obliczony ich wspczynnik rzutowania
//na o y
//**********************************************
private float[] getYMultiplierArray() {
float[] angleArray = getAngleArrays();
float[] ymultiplierArray = new float[sides];
for(int i=0;i<angleArray.length;i++) {
float curAngle = angleArray[i];
float sinvalue = (float)Math.sin(Math.toRadians(curAngle));
float absSinValue = Math.abs(sinvalue);
if (isYPositiveQuadrant(curAngle)) {
sinvalue = absSinValue;
}
else {
sinvalue = -absSinValue;
}
ymultiplierArray[i] = this.getApproxValue(sinvalue);
}
this.printArray(ymultiplierArray, "ymultiplierArray");
return ymultiplierArray;
}
//**********************************************
//Ta funkcja moe by niepotrzebna
687
//**********************************************
//Ta funkcja moe by niepotrzebna
//Naley j samodzielnie sprawdzi i usun, jeli nie okae si przydatna
//**********************************************
private boolean isYPositiveQuadrant(float angle) {
if ((0 <= angle) && (angle <= 90)) { return true; }
if ((angle < 180) && (angle >= 90)) {return true;}
return false;
}
//**********************************************
//Tutaj s obliczane kty
//dla kadej linii wychodzcej ze rodka do wierzchoka
//**********************************************
private float[] getAngleArrays() {
float[] angleArray = new float[sides];
float commonAngle = 360.0f/sides;
float halfAngle = commonAngle/2.0f;
float firstAngle = 360.0f - (90+halfAngle);
angleArray[0] = firstAngle;
float curAngle = firstAngle;
for(int i=1;i<sides;i++)
{
float newAngle = curAngle - commonAngle;
angleArray[i] = newAngle;
curAngle = newAngle;
}
printArray(angleArray, "angleArray");
return angleArray;
}
//**********************************************
//Opcjonalne zaokrglanie
//**********************************************
private float getApproxValue(float f) {
return (Math.abs(f) < 0.001) ? 0 : f;
}
//**********************************************
//Zwraca liczb potrzebnych indeksw
//na podstawie liczby bokw
//Jest to liczba trjktw potrzebnych do
//pomnoenia wielokta przez warto 3
//Tak si skada, e liczba trjktw jest
// rwna liczbie bokw
//**********************************************
public int getNumberOfIndices() {
689
return sides * 3;
}
public static void test() {
RegularPolygon triangle = new RegularPolygon(0,0,0,1,3);
}
private void printArray(float array[], String tag) {
StringBuilder sb = new StringBuilder(tag);
for(int i=0;i<array.length;i++) {
sb.append(";").append(array[i]);
}
Log.d("hh",sb.toString());
}
private void printShortArray(short array[], String tag) {
StringBuilder sb = new StringBuilder(tag);
for(int i=0;i<array.length;i++) {
sb.append(";").append(array[i]);
}
Log.d(tag,sb.toString());
}
}
//przesonita metoda
691
Po dodaniu powyszego fragmentu kodu moemy ponownie uruchomi program i wybra element menu Kwadrat, aby ujrze figur przedstawion na rysunku 20.8.
//przesonita metoda
protected void draw(GL10 gl)
{
long curtime = SystemClock.uptimeMillis();
if ((curtime - prevtime) > 2000)
{
prevtime = curtime;
sides += 1;
if (sides > 20)
{
sides = 3;
}
this.prepareBuffers(sides);
}
gl.glColor4f(1.0f, 0, 0, 0.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, this.numOfIndices,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
}
Powyszy kod jedynie zmienia warto zmiennej sides co cztery sekundy. Animacja powstaje
dziki temu, e klasa Renderer jest rejestrowana z widokiem powierzchni.
Skoro ju posiadamy t klas renderujc, musimy doda kod widoczny na listingu 20.30 do
klasy MultiViewTestHarness.
Listing 20.30. Element menu umoliwiajcy testowanie wielokta
if (mid == R.id.mid_polygon)
{
mTestHarness.setRenderer(new PolygonRenderer(this));
setContentView(mTestHarness);
return;
}
Jeeli ponownie uruchomimy nasz program i wybierzemy opcj Wielokt, ujrzymy zbir przeksztacajcych si figur geometrycznych, ktrych liczba wierzchokw bdzie ustawicznie rosa.
693
Warto si jednak przyjrze, w jaki sposb wielokty ulegaj przeksztaceniom. Na rysunku 20.9
zosta pokazany pocztek cyklu przeksztacania wielokta.
Moemy rozwin koncepcj ksztatw na bardziej zoone figury geometryczne, a nawet graf
sceny, skadajcy si z duej liczby innych obiektw, ktre mog zosta zdefiniowane w jzyku
XML, a nastpnie renderowane w rodowisku OpenGL poprzez te wanie ksztaty.
Praca z teksturami
Jednym z podstawowych poj stosowanych w terminologii OpenGL s tekstury. W rodowisku
OpenGL wi si one z wieloma niuansami. Omwimy tu jedynie podstawowe koncepcje,
umoliwiajce rozpoczcie pracy z teksturami w rodowisku OpenGL. W celu pogbienia wiedzy
na temat tekstur mona skorzysta z listy zasobw umieszczonej w kocowej czci rozdziau.
Tekstury
Tekstur OpenGL nazywamy map bitow umieszczan na danej powierzchni w rodowisku
OpenGL (w tym rozdziale zajmujemy si jedynie powierzchniami). Moemy na przykad uy
obrazu znaczka pocztowego i umieci go na powierzchni kwadratu, dziki czemu uzyskamy
obraz znaczka pocztowego. Moemy take wykorzysta map bitow przedstawiajc wizerunek cegy, umieci j na powierzchni prostokta i poprzez powielanie takich obrazw cegy
utworzy obraz muru.
Proces przyczania mapy bitowej tekstury do powierzchni w rodowisku OpenGL przypomina
proces naklejania fragmentu tapety (kwadratowej) na boku obiektu posiadajcego regularny
lub nieregularny ksztat. Ksztat powierzchni nie ma znaczenia, dopki wymiary papieru
umoliwiaj cakowite pokrycie powierzchni.
Aby jednak umieci papier w odpowiedniej orientacji, pozwalajcej na waciwe uformowanie
obrazu, musimy pobra kady wierzchoek ksztatu i dokadnie zaznaczy go na tapecie, co
spowoduje idealne dopasowanie tapety do ksztatu obiektu. Jeeli mamy do czynienia z niestandardowym ksztatem posiadajcym wiele wierzchokw, kady z nich musi zosta zaznaczony
na tapecie.
Mona to sobie wyobrazi rwnie w inny sposb: kadziemy obiekt na ziemi, przedni cian
skierowan do gry, rozkadamy na tej cianie tapet i obracamy j, dopki nie zostanie zorientowana we waciwym kierunku. Teraz dziurkami zaznaczamy na tapecie kady wierzchoek ksztatu. cigamy tapet, sprawdzamy pooenie wierzchokw i zapisujemy ich wsprzdne, przy zaoeniu, e tapeta jest wykalibrowana. S to tak zwane wsprzdne tekstury.
695
calcTextureArray()
getTextureBuffer()
// Ignorujemy.
}
}
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
public void onDrawFrame(GL10 gl)
{
gl.glDisable(GL10.GL_DITHER);
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_MODULATE);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_REPEAT);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_REPEAT);
draw(gl);
}
}
697
//przesonita metoda
protected void draw(GL10 gl)
{
prepareBuffers(sides);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mFTextureBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, this.numOfIndices,
GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
}
}
699
Jak wida, wikszo pracy wykonuje abstrakcyjna klasa silnika renderowania tekstury i obiekt
RegularPolygon, obliczajcy wierzchoki odwzorowania tekstury (listing 20.26).
Po utworzeniu tej klasy renderujcej, aby przetestowa teksturowany kwadrat, musimy doda
kod z listingu 20.33 do aktywnoci MultiViewTestHarness z listingu 20.12.
Listing 20.33. Odpowied na element menu Teksturowany kwadrat
if (mid == R.id.mid_textured_square)
{
mTestHarness.setRenderer(new TexturedSquareRenderer(this));
mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
setContentView(mTestHarness);
return;
}
Ponowne uruchomienie programu i wybranie elementu menu Teksturowany kwadrat spowoduje wywietlenie figury geometrycznej widocznej na rysunku 20.11.
701
this.mIndexBuffer.position(0);
this.mFTextureBuffer.position(0);
}
//przesonita metoda
protected void draw(GL10 gl)
{
long curtime = SystemClock.uptimeMillis();
if ((curtime - prevtime) > 2000)
{
prevtime = curtime;
sides += 1;
if (sides > 20)
{
sides = 3;
}
this.prepareBuffers(sides);
}
gl.glEnable(GL10.GL_TEXTURE_2D);
Ponowne uruchomienie programu i wybranie elementu menu Wiele figur spowoduje narysowanie na pocztku animacji dwch zestaww zmieniajcych si wieloktw (widocznych na rysunku 20.12). Zwrmy uwag, e zosta zdefiniowany cigy tryb renderowania.
Na rysunku 20.13 zosta ukazany ten sam przykad w poowie procesu animacji.
Na tym zakoczymy omawianie kolejnego istotnego aspektu rodowiska OpenGL. W tym
podrozdziale pokazalimy, w jaki sposb mona zebra wiele rnych figur geometrycznych lub
scen i narysowa je razem, dziki czemu mona otrzyma dosy zoon sceneri w rodowisku OpenGL.
Zajmiemy si teraz obsug rodowiska OpenGL ES 2.0 w Androidzie.
703
OpenGL ES 2.0
Dobre wieci s takie, e oprcz samej obsugi rodowiska OpenGL ES 2.0 system Android od
wersji 2.2 (poziom 8. interfejsw API) posiada take odpowiednie powizania jzyka Java.
Musimy jednak pamita o nastpujcych ograniczeniach:
rodowisko OpenGL ES 2.0 nie jest jeszcze obsugiwane przez emulator.
rodowisko OpenGL ES 2.0 rni si znacznie od poprzedniej wersji, wic wikszo
ksiek powiconych rodowisku OpenGL posiada nowe wydania, w ktrych omwiono
ten aspekt. Programowanie w OpenGL odbywa si w obrbie jednostki GPU, co powoduje,
e emulowanie takiego kodu staje si skomplikowan czynnoci. Z tego powodu nie
jest nawet pewne, czy emulator bdzie kiedykolwiek obsugiwa rodowisko OpenGL
ES 2.0.
Jedynym sposobem testowania czy te poznawania rodowiska OpenGL ES 2.0
w zestawie Android SDK jest wykorzystanie fizycznego urzdzenia. Wkrtce wikszo
urzdze bdzie pracowaa pod kontrol wersji 2.2 Androida, jednak istnieje pewne
prawdopodobiestwo, e cz z nich nie bdzie obsugiwaa nowej wersji rodowiska
OpenGL.
rodowisko OpenGL ES 2.0 jest znaczco odmienne od wersji 1.x. Dodatkowe komplikacje
wynikaj z braku wstecznej kompatybilnoci. Pocztkujcym programistom najwicej problemu sprawi jego inicjalizowanie oraz nauka rysowania najprostszych obiektw.
Dokadna analiza rodowiska OpenGL ES 2.0 wymaga skrupulatnego przeczytania wielu stron
rnorodnych informacji. Zamiast tego zapoznamy Czytelnika z tematem w stopniu umoliwiajcym korzystanie z tego rodowiska. Po utworzeniu podstawowego rodowiska testowego
Czytelnik bdzie mg skorzysta z odnonikw umieszczonych na kocu rozdziau, w ktrych
znajdzie informacje pozwalajce na wdroenie rodowiska OpenGL ES 2.0 do struktury aplikacji.
Sprawdmy najpierw za pomoc kodu zawartego na listingu 20.36, czy dane urzdzenie lub
emulator obsuguje wersj 2.0 rodowiska OpenGL ES.
Listing 20.36. Wykrywanie dostpnoci rodowiska OpenGL ES 2.0
private boolean detectOpenGLES20() {
ActivityManager am =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return (info.reqGlEsVersion >= 0x20000);
}
// glview.setEGLConfigChooser(false);
glview.setEGLContextClientVersion(2);
glview.setRenderer(new YourGLES20Renderer(this));
glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
setContentView(glview);
}
705
W tworzonym przez nas przykadzie klasa renderujca bdzie nosia nazw ES20Simple
TriangleRenderer. Wkrtce zajmiemy si jej omwieniem, jednak tymczasem przyjrzyjmy si aktywnoci z listingu 20.38, do ktrej wstawilimy fragmenty kodw z listingw
20.36 oraz 20 37.
Listing 20.38. Aktywno OpenGL20MultiViewTestHarness
public class OpenGL20MultiViewTestHarnessActivity extends Activity
{
final String tag="es20";
private GLSurfaceView mTestHarness;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (detectOpenGLES20())
{
mTestHarness = new GLSurfaceView(this);
Nasza aplikacja musi posiada wczony tryb debugowania za pomoc odpowiedniego atrybutu
w wle aplikacji, poniewa bdziemy mogli testowa aplikacje wykorzystujce rodowisko
OpenGL ES 2.0 wycznie na urzdzeniach fizycznych (listing 20.40).
Listing 20.40. Definiowanie aplikacji wczonej w trybie debugowania
<application android:icon="@drawable/icon"
android:label="rodowisko testowe OpenGL"
android:debuggable="true">
W celu przywoania nowej aktywnoci rodowiska testowego musimy zmieni aktywno sterujc z listingu 20.14 w taki sposb, aby wygldaa jak na listingu 20.41.
Listing 20.41. Nowa gwna aktywno sterujca
public class TestOpenGLMainDriverActivity extends Activity {
707
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater(); //z aktywnoci
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() >= R.id.mid_es20_triangle)
{
this.invoke20MultiView(item.getItemId());
return true;
}
this.invokeMultiView(item.getItemId());
return true;
}
private void invokeMultiView(int mid)
{
Intent intent = new Intent(this,MultiViewTestHarnessActivity.class);
intent.putExtra("com.ai.menuid", mid);
startActivity(intent);
}
private void invoke20MultiView(int mid)
{
Intent intent = new Intent(this,OpenGL20MultiViewTestHarnessActivity.class);
intent.putExtra("com.ai.menuid", mid);
startActivity(intent);
}
}
Etapy renderowania
Renderowanie figury geometrycznej w rodowisku OpenGL ES 2.0 skada si z nastpujcych
etapw:
1. Zaprogramuj jednostki cieniujce, ktre, dziaajc we wntrzu jednostki GPU,
wykorzystuj takie elementy, jak wsprzdne rysowania oraz macierze modelu lub
widoku bd rzutowania pobrane z pamici klienta, i rysuj dane obiekty. W rodowisku
OpenGL ES 1.0 nie znajdziemy odpowiednika tego etapu. W uproszczonym ujciu
mamy tu do czynienia z kolejnym etapem porednim pomidzy rysowaniem
wierzchokw i powierzchni.
2. Skompiluj w ukadzie GPU jednostki cieniujce utworzone w punkcie 1.
Jednostki cieniujce
Nawet najprostsze obiekty w rodowisku OpenGL 2.0 wymagaj wykorzystania fragmentw
kodw nazywanych jednostkami cieniujcymi. Stanowi one rdze biblioteki OpenGL ES 2.0.
Przekaemy teraz minimaln ilo informacji wymaganych do utworzenia prostego trjkta;
zalecamy zapoznanie si z materiaami rdowymi wymienionymi na kocu rozdziau.
Kady obiekt zawierajcy wierzchoki podlega przetwarzaniu przez jednostki cieniujce wierzchoki (ang. vertex shader). Z kolei kady obiekt zwizany z fragmentami, czyli przestrzeni
pomidzy wierzchokami, bdzie objty dziaaniem jednostek cieniujcych fragmenty (ang.
fragment shader). Zatem jednostka cieniujca wierzchoki przetwarza wycznie wsprzdne
wierzchokw, mimo e jednostka cieniujca fragmenty przetwarza kady piksel.
Listing 20.42 stanowi prosty przykad jednostki cieniujcej wierzchoki.
Listing 20.42. Prosta jednostka cieniujca wierzchoki
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
void main() {
gl_Position = uMVPMatrix * aPosition;
}
Jest to kod zapisany w jzyku cieniowania. Z pierwszego wiersza dowiadujemy si, e zmienna
uMVPMatrix jest zmienn wejciow programu, zadeklarowan dla typu mat4 (macierz 44).
Jest to rwnie macierz typu uniform, poniewa zostaa ona zdefiniowana dla wszystkich
wierzchokw, a nie tylko jednego.
Z drugiej strony mamy zmienn aPosition, ktra suy do definiowania wsprzdnych wierzchoka. Zostaa ona okrelona jako atrybut wierzchoka i jest niepowtarzalna dla kadego z nich.
Wrd innych atrybutw wierzchoka znajdziemy takie, jak kolor, tekstura itp. Rwnie zmienna aPosition jest wektorem o wartoci rwnej 4. Nastpnie sam program (listing 20.42) pobiera
wsprzdne wierzchoka i przeksztaca je za pomoc macierzy MVP (ang. Model View
709
Projection rzutowanie widoku modelu), ktra bdzie ustanawiana przez program wywoujcy, po czym przemnaa wsprzdne wierzchoka, aby uzyska ostateczne pooenie, okrelane przez zarezerwowan zmienn gl_Position jednostki cieniujcej.
Jednostka cieniujca zapewnia rysowanie lub pozycjonowanie wierzchokw. Na przykad program wywoujcy ustanowi bufor dla wierzchokw trjkta w sposb zaprezentowany na
listingu 20.43.
Listing 20.43. Ustanawianie danych wierzchokw
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mFVertexBuffer);
Bufor wierzchoka jest ostatnim argumentem tej metody GLES20. Bardzo przypomina ona
metod glVertexPointer z biblioteki OpenGL ES 1.0, oprcz pierwszego argumentu, ktry
tutaj stanowi uchwyt pooenia (positionHandle). Argument ten wskazuje atrybut aPosition
z jednostki cieniujcej, zamieszczonej na listingu 20.42. Taki uchwyt uzyskujemy za pomoc
kodu podobnego do przedstawionego poniej:
positionHandle = GLES20.glGetAttribLocation(shaderProgram, "aPosition");
711
float[]
float[]
float[]
float[]
713
}
}
return program;
}
private int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Nie mozna skompilowac jednostki cieniujacej "
+ shaderType + ":");
Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
private final String mVertexShader =
"uniform mat4 uMVPMatrix;\n" +
"attribute vec4 aPosition;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
"}\n";
private final String mFragmentShader =
"void main() {\n" +
" gl_FragColor = vec4(0.5, 0.25, 0.5, 1.0);\n" +
"}\n";
protected void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
protected void setupMatrices()
{
Matrix.setIdentityM(mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
}
protected abstract void draw(GL10 gl, int positionHandle);
}
Wikszo powyszego kodu stanowi poczenie uprzednio omawianych koncepcji, za wyjtkiem jednego szczegu. Funkcja setupMatrices ukazuje nam sposb, w jaki klasa Matrix jest
wykorzystywana do czenia wielu macierzy w jedn wspln, zwan mMVPMatrix, poprzez
przemnoenie przez inne macierze, poczwszy od macierzy jednostkowej.
// X, Y, Z
-1.0f, -0.5f, 0,
1.0f, -0.5f, 0,
0.0f, 1.11803399f, 0 };
public ES20SimpleTriangleRenderer(Context context)
{
ByteBuffer vbb = ByteBuffer.allocateDirect(mTriangleVerticesData.length
* FLOAT_SIZE_BYTES);
vbb.order(ByteOrder.nativeOrder());
mFVertexBuffer = vbb.asFloatBuffer();
mFVertexBuffer.put(mTriangleVerticesData);
mFVertexBuffer.position(0);
}
Jeeli teraz wywoamy aktywno z listingu 20.38, ujrzymy trjkt rysowany w danym kierunku.
Aby aplikacja zadziaaa, potrzebne nam bd dodatkowe pliki:
ES20AbstractRenderer.java (listing 20.48),
ES20SimpleTriangleRenderer.java (listing 20.49),
OpenGL20MultiveTestHarnessActivity.java (listing 20.38).
715
Po skompilowaniu tych plikw moemy ponownie uruchomi program i wybra z menu opcj
Trjkt ES20. Zostanie wywietlony jeden trjkt, taki jak ten na rysunku 20.3.
Jednak, jak ju stwierdzilimy, powyszy przykadowy projekt nie zadziaa na emulatorze.
W celu jego przetestowania musimy podczy fizyczne urzdzenie do rodowiska Eclipse.
Sprawdzalimy go na pierwszym modelu Motorola Droid firmy Verizon. Instrukcje dotyczce
podczenia urzdzenia znajdziemy w rozdziale 2. W podrozdziale z odnonikami zamiecilimy
take adres URL do witryny, w ktrej zamierzamy dodawa zaktualizowane informacje powizane
rwnie z innymi urzdzeniami.
Odnoniki
Nastpujce zasoby uznalimy za przydatne w zrozumieniu oraz podczas pracy w rodowisku
OpenGL:
http://developer.android.com/reference/android/opengl/GLSurfaceView.html adres
odnoszcy si do pakietu Androida android.opengl.
www.khronos.org/opengles/documentation/opengles1_0/html/index.html podrcznik
referencyjny rodowiska OpenGL ES utworzony przez grup Khronos.
http://www.glprogramming.com/red/ podrcznik programowania w rodowisku
OpenGL (czerwona ksiga). Chocia materiay tu zawarte s bardzo przydatne, zakres
informacji koczy si na wersji OpenGL ES 1.1. W celu zapoznania si z najnowszymi
informacjami, w tym dotyczcymi jednostek cieniujcych, musimy zaopatrzy si
w sidm edycj ksigi.
http://msdn.microsoft.com/en-us/library/ms970772(printer).aspx bardzo dobry artyku
firmy Microsoft, dotyczcy mapowania tekstur.
Podsumowanie
Powicilimy mnstwo miejsca standardowi OpenGL, co moe by przydatne zwaszcza dla
osb majcych z nim do czynienia po raz pierwszy. Chcielibymy, aby byo to znakomite wprowadzenie do rodowiska OpenGL nie tylko dla systemu Android, lecz rwnie dla innych systemw obsugujcych ten standard.
W rozdziale tym przedstawilimy podstawowe informacje na temat biblioteki OpenGL. Pokazalimy interfejs API (dostpny wycznie w systemie Android), umoliwiajcy prac ze standardowymi interfejsami rodowiska OpenGL. Omwilimy pojcia ksztatw i tekstur, a take
zademonstrowalimy, w jaki sposb mona wykorzysta potok rysowania do tworzenia wielu
obiektw. Poznalimy podstawy rodowiska OpenGL ES 2.0, jego jzyk cieniowania, podstawowe rnice pomidzy t a poprzedni wersj bibliotek, a take rnorodne odnoniki pozwalajce na dalsze zgbianie zagadnienia.
R OZDZIA
21
Badanie aktywnych folderw
719
Jeeli na ekranie startowym wykonamy dugie kliknicie, Android wywietli jego menu kontekstowe (rysunek 21.2).
Po klikniciu opcji Nowy aktywny folder na stronie startowej Androida zostanie utworzona
ikona reprezentujca aktywny folder. W naszym przykadzie nazw tej ikony bdzie Kontakty
AF skrt od Kontakty Aktywny Folder (rysunek 21.4). W folderze tym bd wywietlane
kontakty z bazy kontaktw. W trakcie implementacji aktywnego folderu pokaemy sposb,
w jaki jest definiowana jego nazwa.
W nastpnym podrozdziale bdzie si mona przekona, e to aktywno zapewnia utworzenie
folderu Kontakty AF. Na razie interesuj nas wraenia uytkownika, zatem kliknijmy ikon Kontakty AF, aby ujrze list kontaktw wywietlon w widoku ListView (rysunek 21.5). Jeszcze raz
przypominamy, e w zalenoci od posiadanej wersji systemu lista ta moe wyglda inaczej.
Wygld listy moe by rny od zaprezentowanego, gdy zaley on od liczby posiadanych
kontaktw. Po klikniciu jednego z kontaktw ujrzymy jego szczegy (rysunek 21.6). Zwrmy
uwag, e szczegy kontaktu s wywietlane poprzez aplikacj obsugujc kontakty, zatem wygld tego aspektu rwnie zaley od wersji systemu Android.
721
Moemy klikn znajdujcy si u dou ekranu przycisk Menu, aby zobaczy moliwoci edycyjne
danego kontaktu (rysunek 21.7). Take ten etap jest obsugiwany przez aplikacj, wic jego
stylistyka jest zalena od rodzaju urzdzenia i wersji Androida.
Po wybraniu opcji edycji kontaktu pojawi si ekran (rwnie o wygldzie zalenym od wersji
systemu) ukazany na rysunku 21.8.
Aby ujrze aktywne zachowanie si tego folderu, mona zaktualizowa imi lub nazwisko
kontaktu. Po powrocie do widoku folderu Kontakty AF ujrzymy, e wprowadzone zmiany zostay uwzgldnione. W tym celu naley wielokrotne klika przycisk cofania, dopki nie wrcimy
do folderu Kontakty AF.
723
Omwimy kady plik po kolei, aby atwiej byo zrozumie zasad dziaania aktywnych folderw.
AndroidManifest.xml
Mielimy ju wielokrotnie do czynienia z plikiem AndroidManifest.xml. Jest on konieczny do
dziaania wszystkich aplikacji. Fragment pliku dotyczcy aktywnych folderw, ktry zosta oddzielony komentarzem, wskazuje istnienie aktywnoci AllContactsLiveFolderCreatorActivity
Etykieta tej aktywnoci, Nowy aktywny folder, pojawi si w menu kontekstowym strony startowej (rysunek 21.3). Jak zostao wyjanione w punkcie W jaki sposb uytkownik korzysta z aktywnych folderw, dostp do menu kontekstowego strony startowej uzyskujemy poprzez dugie
kliknicie na obszarze tego ekranu startowego.
Kolejnym godnym uwagi elementem kodu z listingu 21.1 jest deklaracja provider, zakotwiczona
do identyfikatora URI content://com.androidbook.livefolders.contacts i obsugiwana
przez klas dostawcy MyContactsProvider. Dostawca ten dostarcza kursor wypeniajcy kontrolk
ListView, ktra zostaje otwarta po klikniciu odpowiedniej ikony aktywnego folderu (rysunek
21.5). Aktywno AllContactsLiveFolderCreatorActivity takiego folderu musi wiedzie,
czym jest ten identyfikator URI, i przekaza go po wywoaniu przez system. Android przywouje
t aktywno po wybraniu nazwy aktywnego folderu, aby utworzy jego ikon na ekranie
startowym.
Zgodnie z protokoem aktywnych folderw intencja CREATE_LIVE_FOLDER bdzie pozwalaa
wywietla aktywno AllContactsLiveFolderCreatorActivity jako opcj zatytuowan
Nowy aktywny folder w menu kontekstowym strony startowej (rysunek 21.3). Kliknicie tej opcji
spowoduje utworzenie ikony na stronie startowej, co zostao pokazane na rysunku 21.4.
725
AllContactsLiveFolderCreatorActivity.java
Klasa AllContactsLiveFolderCreatorActivity ma do spenienia jedn rol: generatora
czy te kreatora aktywnego folderu (listing 21.2). Moemy j sobie wyobrazi jako szablon takiego folderu. Po kadym klikniciu tej aktywnoci (poprzez opcj Foldery w menu kontekstowym ekranu startowego) zostanie wygenerowany aktywny folder.
Listing 21.2. Kod rdowy klasy AllContactsLiveFolderCreatorActivity
public class AllContactsLiveFolderCreatorActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
final String action = intent.getAction();
if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
setResult(RESULT_OK,
createLiveFolder(MyContactsProvider.CONTACTS_URI,
"Kontakty AF",
R.drawable.icon)
);
}
else {
setResult(RESULT_CANCELED);
}
finish();
}
private Intent createLiveFolder(Uri uri, String name, int icon)
{
final Intent intent = new Intent();
intent.setData(uri);
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
Intent.ShortcutIconResource.fromContext(this, icon));
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
LiveFolders.DISPLAY_MODE_LIST);
return intent;
}
}
Aby wykona to zadanie, aktywno podaje obiektowi wywoujcemu stronie startowej lub,
w naszym przypadku, strukturze aktywnego folderu nazw aktywnego folderu, obraz ikony
tego folderu, identyfikator URI danych oraz tryb wywietlania (lista lub siatka). Z kolei struktura
zapewnia utworzenie ikony aktywnego folderu na ekranie startowym.
MyContactsProvider.java
Przed klas MyContactsProvider stoj nastpujce zadania:
1. Rozpoznanie przychodzcego identyfikatora URI
content://com.androidbook.livefolders.contacts/contacts.
//Identyfikator Uri biorcy udzia w procesie tworzenia aktywnego folderu jako dane
//wejciowe
public static final Uri CONTACTS_URI = Uri.parse("content://" +
AUTHORITY + "/contacts" );
//nazwa
//opis
};
727
//sortowanie
while(allContacts.moveToNext())
{
String timesContacted = "Nawizane poczenia: "+allContacts.getInt(2);
Object[] rowObject = new Object[]
{
allContacts.getLong(0),
allContacts.getString(1),
//identyfikator
//nazwa
timesContacted,
Uri.parse("content://contacts/people/"
+allContacts.getLong(0)),
cp.getContext().getPackageName(),
R.drawable.icon
729
//opis
//id. Uri intencji
//pakiet
//ikona
};
mc.addRow(rowObject);
}
return mc;
}
finally
{
allContacts.close();
}
}
@Override
public String getType(Uri uri)
{
Zwrmy uwag, e wymagany przez struktur aktywnego folderu zbir kolumn zostaje zainicjalizowany w kodzie z listingu 21.3, a na listingu 21.4 zostaje wywoany w postaci natychmiastowego odniesienia.
Listing 21.4. Kolumny potrzebne do wypenienia kontraktu aktywnego folderu
private static final String[] CURSOR_COLUMNS = new String[]
{
BaseColumns._ID,
Poza elementem INTENT przeznaczenie pozostaych obiektw jest oczywiste. Jeeli przyjrzymy
si rysunkowi 21.5, zauwaymy, e obiekt NAME dotyczy nazwy elementu na licie. Atrybut
DESCRIPTION zosta umieszczony na tej samej licie pod obiektem NAME.
Pole INTENT jest w rzeczywistoci polem typu string, wskazujcym identyfikator URI danego
elementu w dostawcy treci. W przypadku kliknicia tego elementu Android zastosuje dziaanie
VIEW poprzez ten identyfikator URI. Dlatego wanie pole to nosi nazw pola INTENT, poniewa
wewntrznie Android uzyska obiekt INTENT z identyfikatora URI.
Dwa ostatnie elementy s zwizane z obiektem ICON, wywietlanym jako cz listy. Przyjrzyjmy si ponownie rysunkowi 21.5, aby zobaczy ikony, oraz listingowi 21.3, aby sprawdzi,
w jaki sposb kolumny te dostarczaj wartoci z bazy kontaktw.
Zwrmy rwnie uwag, e klasa MyContactsContentProvider (osonowy dostawca treci)
wykonuje kod z listingu 21.5 wymuszajc na podstawowym kursorze obsug wszelkich zmian
danych.
Listing 21.5. Rejestrowanie identyfikatora URI za pomoc kursora
MatrixCursor mc = loadNewData(this);
mc.setNotificationUri(getContext().getContentResolver(),
Uri.parse("content://contacts/people/"));
Funkcja loadNewData() uzyskuje od dostawcy treci zbir kontaktw i tworzy obiekt Matrix
Cursor, ktrego kolumny s widoczne na listingu 21.4. Nastpnie obiekt ten otrzymuje
informacj, e ma si zarejestrowa wraz z klas ContentResolver, aby moga ona przekaza do kursora powiadomienie o jakiejkolwiek zmianie danych wskazywanych przez identyfikator URI (content://contacts/people).
Interesujcy jest fakt, e ledzonym identyfikatorem URI nie jest identyfikator naszego dostawcy treci MyContactsProvider, lecz identyfikator dostawcy treci kontaktw dostarczony
przez Androida. Wynika to z faktu, e dostawca MyContactsProvider stanowi jedynie oson
prawdziwego dostawcy treci. Zatem kursor ten musi ledzi waciwego dostawc treci,
a nie jego oson.
Wane jest rwnie, aby osoni obiekt MatrixCursor we wasnym kursorze, co zostao pokazane na listingu 21.6.
Listing 21.6. Osanianie kursora
MatrixCursor mc = loadNewData(this);
mc.setNotificationUri(getContext().getContentResolver(),
Uri.parse("content://contacts/people/"));
MyCursor wmc = new MyCursor(mc,this);
731
Aby zrozumie sens osaniania kursora, musimy dowiedzie si, w jaki sposb widoki przeprowadzaj aktualizacj zmienionej treci. Taki dostawca treci, jak Contacts, zazwyczaj rejestruje identyfikator URI jako cz implementacji metody query i w ten sposb powiadamia
kursor o potrzebie ledzenia zmian. Do tego suy metoda cursor.setNotificationUri. Kursor moe nastpnie zarejestrowa ten identyfikator URI oraz jego wszystkie podrzdne identyfikatory wraz z dostawc treci. Podczas przeprowadzenia na dostawcy treci operacji wstawienia
lub usunicia danych kod obsugujcy te operacje musi wprowadzi zdarzenie oznaczajce
zmian danych w krotkach definiowanych przez okrelony identyfikator URI.
W ten sposb kursor bdzie aktualizowany za pomoc operacji requery, a widok zostanie stosownie odwieony. Niestety, klasa MatrixCursor nie jest dostosowana do operacji requery.
Obsuguje j kursor SQLiteCursor, jednak nie moemy z niego tutaj skorzysta, poniewa odwzorowujemy kolumny zgodnie z nowym zestawem kolumn.
Aby pomin to ograniczenie, umiecilimy obiekt MatrixCursor w osonie kursora i przesonilimy metod requery w celu pozostawienia tego obiektu i utworzenia nowego, zawierajcego zaktualizowane dane. Chcemy rwnie, eby przy kadej zmianie danych by generowany
nowy obiekt MatrixCursor. Jednak do struktury aktywnego folderu w Androidzie zwracamy
jedynie zewntrzny kursor osaniajcy. Szkielet aktywnego folderu bdzie rozpoznawa tylko jeden kursor, w jego wntrzu jednak bd pojawiay si nowe kursory w miar wprowadzania
zmian w danych.
Do tego su dwie nastpne klasy.
MyCursor.java
Zauwamy, w jaki sposb jest inicjalizowany obiekt MyCursor zawierajcy na pocztku klas
MatrixCursor (listing 21.7). Podczas przeprowadzania operacji requery kursor MyCursor
bdzie zwrotnie wywoywa dostawc w celu przekazania obiektu MatrixCursor. Nowy obiekt
MatrixCursor zastpi stary za pomoc metody set.
Listing 21.7. Kod rdowy klasy MyCursor
public class MyCursor extends BetterCursorWrapper
{
private ContentProvider mcp = null;
public MyCursor(MatrixCursor mc, ContentProvider inCp)
{
super(mc);
mcp = inCp;
}
public boolean requery()
{
MatrixCursor mc = MyContactsProvider.loadNewData(mcp);
this.setInternalCursor(mc);
return super.requery();
}
}
BetterCursorWrapper.java
Klasa BetterCursorWrapper (listing 21.8) przypomina klas CursorWrapper struktury bazodanowej w Androidzie. Potrzebne s jednak dwa elementy, ktrych brakuje klasie Cursor
Wrapper. Po pierwsze, nie zawiera ona metody set, sucej do zastpienia wewntrznego
kursora, pochodzcego z metody requery. Po drugie, obiekt CursorWrapper nie jest czci
klasy CrossProcessCursor. Aktywne foldery wymagaj klasy CrossProcessCursor, a nie
zwykego kursora, poniewa przekraczaj one granice procesw.
Listing 21.8. Kod rdowy klasy BetterCursorWrapper
public class BetterCursorWrapper implements CrossProcessCursor
{
Na listingu 21.8 nie pokazalimy caej klasy BetterCursorWrapper, mona j jednak atwo
wygenerowa w rodowisku Eclipse. Po wczytaniu powyszego fragmentu umieszczamy kursor w zmiennej internalCursor. Klikamy prawym przyciskiem myszy i wybieramy opcj
Source/Generate Delegated Methods. W ten sposb zostanie zapeniona reszta klasy. Po wygenerowaniu delegowanych klas przez rodowisko Eclipse musimy je oddelegowa do wewntrznej klasy kursora, tak jak to zrobilimy w przypadku metody fillWindow z listingu 21.8 (jeeli
nie chcemy przeprowadza tego procesu, odpowiedni plik znajdziemy w pliku ZIP zawierajcym gotowy projekt).
733
Posiadamy teraz wszystkie klasy niezbdne do zbudowania, wdroenia i uruchomienia przykadowego projektu demonstrujcego dziaanie aktywnych folderw w rodowisku Eclipse.
Poniewa adna aktywno nie zostaa zarejestrowana w kategorii MAIN, nie ujrzymy interfejsu
uytkownika po wdroeniu projektu, ale w konsoli rodowiska Eclipse pojawi si informacja o jego
instalacji zakoczonej sukcesem.
Podsumujmy ten podrozdzia omwieniem zjawisk zachodzcych podczas uzyskiwania dostpu
do aktywnego folderu.
Odnoniki
Ponisze adresy mog si okaza bardzo przydatne podczas nauki korzystania z aktywnych
folderw oraz pracy z nimi:
http://developer.android.com/reference/android/provider/LiveFolders.html ten adres
umoliwi zapoznanie si z dokumentacj klasy LiveFolders.
http://developer.android.com/resources/articles/contacts.html w tym artykule
znajdziemy informacje dotyczce korzystania z interfejsu kontaktw. Okae si
on szczeglnie przydatny podczas pracy z aktywnymi folderami wykorzystujcymi
kontakty.
ftp://ftp.helion.pl/przyklady/and3ta.zip pod tym adresem znajduj si projekty
utworzone na potrzeby niniejszej ksiki. Waciwy plik zosta umieszczony w katalogu
o nazwie ProAndroid3_R21_AktywneFoldery.
Podsumowanie
Dziki aktywnym folderom otrzymujemy innowacyjny, obsugiwany jednym klikniciem mechanizm, wywietlajcy zmienione dane na ekranie startowym. Potencjalnie moemy umieszcza tu dane dowolnego rodzaju pod warunkiem e istnieje moliwo ich zdefiniowania
w postaci listy tworzonej przez wiersze. Wszystkie dane musz posiada waciwoci nazwy
i opisu, pozwalajce na ich zidentyfikowanie. Niemal kady typ danych spenia ten wymg, gdy
mog one zosta w jaki sposb nazwane i opisane. Przydatna okazuje si take obecno aktywnoci wywietlajcej szczegowe informacje na temat kliknitego obiektu w aktywnym folderze. Dane mog by lokalne, na przykad kontakty, a nawet sieciowe, czego przykadem moe
by spis blogw.
Rozdzia ten zawiera opis nowoci w kursorach aktywnych folderw oraz mechanizmw wymaganych do wyeksponowania istniejcych dostawcw treci jako rde aktywnych folderw.
Wyjanilimy przyczyny osaniania kursorw, a take zaprezentowalimy sposb rejestrowania
klasy ContentResolver w celu otrzymywania aktualizacji danych.
Nastpny rozdzia zosta powicony kolejnej innowacji ekranu startowego, znanej pod nazw
widetw ekranu startowego (ang. home screen widgets).
R OZDZIA
22
Widety ekranu startowego
737
Innymi sowy, przed nauk programowania widetu naley si przekona, w jaki sposb si go
uytkuje.
W naszym przykadzie zajmiemy si widetem noszcym nazw Urodziny, utworzonym specjalnie na potrzeby tego rozdziau. W dalszej czci rozdziau zaprezentujemy jego kod rdowy.
Najpierw posuy nam on do wyjanienia zasady dziaania widetw. Kod rdowy tego widetu zamiecilimy w dalszej czci rozdziau, zatem teraz zalecamy Czytelnikowi skoncentrowanie si na treci rozdziau i ogldaniu zrzutw ekranu, a dopiero pniej na samodzielnym
testowaniu widetu. Jeeli Czytelnik przeanalizuje rysunki i doczone wyjanienia, nie powinien
mie problemw ze zrozumieniem natury i zachowania widetu Urodziny, dziki czemu
opanowanie kodu rdowego stanie si atwiejszym zadaniem.
Rozpocznijmy od zlokalizowania poszukiwanego widetu i utworzenia jego instancji na ekranie
startowym.
Po wybraniu opcji Widety pojawi si kolejny ekran, zawierajcy list dostpnych widetw, co
zostao przedstawione na rysunku 22.2.
Wikszo widetw stanowi integraln cz Androida. Lista dostpnych widetw moe
wyglda inaczej w zalenoci od wersji uywanego oprogramowania. W celach demonstracyjnych wybralimy widok Birthday Widget. Po jego klikniciu zostanie utworzona odpowiednia
instancja widetu na ekranie startowym, wygldajca jak przykadowy widet Urodziny z rysunku 22.3.
W nagwku widetu Urodziny s wywietlane takie dane, jak imi osoby, liczba dni do jej urodzin, a take data urodzin oraz cze do sklepu z upominkami.
739
Konfigurator widetw
Definicja widetu moe opcjonalnie zawiera specyfikacj aktywnoci, zwanej aktywnoci konfiguratora widetw. Po wybraniu widetu z listy dostpnych widetw w celu utworzeniu jego
instancji Android wywouje powizan z nim aktywno konfiguracji widetu. Pokaemy, w jaki
sposb samodzielnie napisa tak aktywno. Umoliwia ona konfiguracj wystpienia widetu.
W przypadku naszego widetu urodzinowego aktywno konfiguracji wywietli monit o wprowadzenie imienia jubilata oraz daty jego urodzin, tak jak zostao pokazane na rysunku 22.4.
Zadaniem konfiguratora jest zapisanie tych informacji w staym miejscu, aby po wywoaniu
aktualizacji przez dostawc widetu dostawca ten mg zlokalizowa informacje i zaktualizowa
je o nowe wartoci, wstawiane nastpnie do widetu przez konfigurator.
Android wewntrznie ledzi instancje widetw poprzez przypisywanie im identyfikatora. Identyfikator ten jest przekazywany metodom zwrotnym kodu Java oraz klasie konfiguracyjnej w celu
skierowania pocztkowej konfiguracji i aktualizacji do waciwej instancji. Na rysunku 22.3,
w cigu znakw satya:3, cyfra 3 stanowi identyfikator widetu a cilej, identyfikator
instancji widetu. Sam za widet jest identyfikowany za pomoc nazwy (na ktr skada si nazwa klasy oraz pakietu, w ktrym ta klasa si znajduje); w tym rozdziale pojcia identyfikator
widetu oraz identyfikator instancji widetu s uywane zamiennie i odnosz si do identyfikatora wystpienia widetu. Identyfikator instancji widetu zosta pokazany na rysunku 22.3
w celach demonstracyjnych.
Po oglnym omwieniu widetu nadszed czas na szczegowe zapoznanie si z jego cyklem ycia.
Definicja ta wskazuje istnienie odbiorcy komunikatw klasy Java noszcego nazw BdayWidget
Provider (jak si przekonamy, wywodzi si on z podstawowej klasy Androida AppWidget
Provider mieszczcej si w pakiecie widget), ktry odbiera komunikaty zawierajce aktualizacje
widetu.
741
Definicja widetu z listingu 22.1 jest rwnie zwizana z plikiem XML w katalogu /res/xml,
ktry z kolei okrela widok widetu oraz czstotliwo odwieania, co zostao zaprezentowane
na listingu 22.2.
Listing 22.2. Definicja widoku widetu w pliku XML informacji o dostawcy widetu
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="150dp"
android:minHeight="120dp"
android:updatePeriodMillis="43200000"
android:initialLayout="@layout/bday_widget"
android:configure="com.ai.android.BDayWidget.ConfigureBDayWidgetActivity"
>
</appwidget-provider>
Plik ten jest nazywany plikiem informacji o dostawcy widetu. Zostaje on wewntrznie przetumaczony na klas Java AppWidgetProviderInfo. Wartoci szerokoci i wysokoci ukadu graficznego zostaj tu ustalone odpowiednio na 150dp i 120dp. Zostaje tu rwnie okrelony wyraony
w milisekundach czas przedziau czasowego rwny 12 godzinom. Definicja ta wskazuje take
plik ukadu graficznego (listing 22.7), opisujcy widok widetu (rysunek 22.5).
Zauwamy jednak, e ukad graficzny tych widokw widetw moe zawiera jedynie niektre
rodzaje elementw widoku. Dopuszczalne kontrolki w ukadzie graficznym widetu nale
wycznie do klasy widokw znanej jako RemoteViews ta klasa widokw zdalnych akceptuje
jedynie okrelone rodzaje widokw potomnych. Te dopuszczalne podelementy widoku s wypisane na listingu 22.3.
Listing 22.3. Dopuszczalne kontrolki widoku w klasie RemoteViews
FrameLayout
LinearLayout
RelativeLayout
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
Powysza lista moe w przyszoci rozrasta si z kad kolejn wersj rodowiska SDK. Zasadniczym powodem ograniczenia dopuszczalnych typw elementw w widoku zdalnym jest fakt, e s
one odcite od kontrolujcych je procesw. Te widoki widetw s obsugiwane przez takie aplikacje, jak na przykad Home. Kontrolerami tych widokw s przetwarzane w tle procesy, wywoywane przez zegary. Z tego powodu obiekty te nosz nazw widokw zdalnych. Istnieje odpowiednia klasa Java, nazwana RemoteViews, udzielajca dostpu do tych widokw. Innymi sowy,
programici nie musz uzyskiwa bezporedniego dostpu do tych widokw, aby wywoa wobec
nich metody. Dostp do nich nastpuje wycznie poprzez klas RemoteViews (peni rol bramki).
Android bdzie powtarza ten proces dla kadej instancji widetu utworzonej przez uytkownika. Warto rwnie zauway, e nie istniej udokumentowane ograniczenia zmuszajce uytkownika do korzystania z tylko jednej instancji widetu.
Poza przywoywaniem aktywnoci konfiguratora Android wywouje zwrotnie rwnie metod onEnabled klasy AppWidgetProvider. Powimy chwil metodom zwrotnym klasy
AppWidgetProvider i przyjrzyjmy si powoce naszego dostawcy BDayWidgetProvider (listing
22.4). Peny kod tego pliku zosta umieszczony na listingu 22.9.
743
Metoda zwrotna onEnabled() wskazuje, e istnieje co najmniej jedna instancja widetu dziaajca na ekranie startowym. Oznacza to, e uytkownik musia umieci na stronie startowej przynajmniej jeden widet. A zatem w wywoaniu musimy uruchomi otrzymywanie komunikatw dla tego skadnika (z listingu 22.9 dowiemy si, jak tego dokona). W Androidzie
klasy s nazywane czasami skadnikami, szczeglnie gdy tworz wielokrotnie wykorzystywane
jednostki, takie jak aktywno, usuga lub odbiorca transmisji. W naszym przypadku klasa
AppWidgetProvider jest skadnikiem odbiorcy transmisji; moemy j wcza lub wycza
w celu otrzymywania transmitowanych komunikatw.
Metoda zwrotna onDeleted() jest wywoywana podczas przenoszenia przez uytkownika
instancji widetu do kosza. To wanie tu musimy usun wszystkie przechowywane wartoci
dla instancji widetu.
Metoda zwrotna onDisabled() jest wywoywana po usuniciu ostatniej instancji widetu
z ekranu startowego. Nastpuje to w momencie przeniesienia ostatniej instancji do kosza. Powinnimy uywa tej metody do wyrejestrowania procesu otrzymywania transmitowanych
komunikatw przez ten skadnik (zobaczymy to na listingu 22.9).
Metoda zwrotna onUpdate() jest wywoywana za kadym razem, gdy wyganie zegar zaprezentowany na listingu 22.2. Metoda ta jest rwnie wywoywana na samym pocztku, podczas generowania instancji widetu, w przypadku gdy nie zdefiniowalimy aktywnoci konfiguratora. Jeeli aktywno konfiguratora jest dostpna, ta metoda nie jest wywoywana podczas
procesu utworzenia instancji widetu. Nastpnie bdzie ona wywoywana z czstotliwoci
rwn wyganiciom zegara.
W przypadku krtszych przedziaw czasowych, rzdu pojedynczych sekund, musimy samodzielnie wywoa metod onUpdate() za pomoc funkcji dostpnych w klasie AlarmManager.
Jeeli korzystamy z klasy AlarmManager, zamiast wywoywaniem metody onUpdate() mona
si posuy metodami zwrotnymi alarmu. Stosowanie menedera alarmu zostao omwione
w rozdziale 15.
Poniej wymienilimy standardowe czynnoci wymagane podczas pracy z metod onUpdate():
1. Upewnij si, e konfigurator zakoczy prac; jeli nie skoczy, po prostu sprawd
to ponownie po chwili. Nie powinno to stanowi problemu w wersji oprogramowania
co najmniej 2.0, gdzie s oczekiwane dusze interway czasowe. Jeeli bdzie inaczej,
istnieje moliwo, e metoda onUpdate() zostanie wywoana przed zakoczeniem
procesu konfiguracji widetu przez uytkownika w konfiguratorze.
2. Pobierz dane przechowywane dla tej instancji widetu.
3. Pobierz ukad graficzny widoku widetu i utwrz wraz z nim obiekt RemoteViews.
4. Wywoaj metody klasy RemoteViews w celu ustawienia wartoci dla pojedynczych
obiektw widoku, na przykad dla tekstu, obrazu i tak dalej.
5. Zarejestruj wszelkie zdarzenia onClick w dowolnym widoku poprzez wykorzystanie
oczekujcych intencji.
6. Zaprogramuj klas AppWidgetManager, aby narysowaa obiekt RemoteViews,
korzystajc z identyfikatora instancji.
Jak wida, istnieje dua zbieno pomidzy dziaaniem konfiguratora a dziaaniem metody
onUpdate(). Istnieje moliwo wykorzystywania tej zbienoci.
745
Przykadowy widet
Do tej pory przedstawilimy podstawy teoretyczne, pokazalimy te sposoby tworzenia widetw. Wykorzystajmy t wiedz do utworzenia przykadowego widetu, ktrego zachowanie
byo wykorzystane jako przykad podczas omawiania architektury widetw. Zaprojektujemy,
wdroymy i przetestujemy ten stary-nowy widet Urodziny.
Implementacja tego widetu skada si z wymienionych poniej plikw. W zalenoci od stosowanego rdowego pakietu Java pliki Java bd przechowywane w podkatalogu src wraz ze
struktur katalogw, ktr wykorzystamy w przypadku tych pakietw. W celu zachowania
zwizoci te podkatalogi s reprezentowane przez wyraenie ....
Omwimy kady z wymienionych plikw oraz wyjanimy wszelkie pominite do tej pory pojcia. Pod koniec lektury tego podrozdziau Czytelnik zapewne bdzie mg utworzy te pliki
i przetestowa widet urodzinowy we wasnym rodowisku.
747
<!-**********************************************************************
* Odbiorca komunikatw dostawcy widetu urodzinowego
**********************************************************************
-->
<receiver android:name=".BDayWidgetProvider">
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/bday_appwidget_provider" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<!-**********************************************************************
* Aktywno konfiguratora widetu urodzinowego
**********************************************************************
-->
<activity android:name=".ConfigureBDayWidgetActivity"
android:label="Konfiguruj widet Urodziny">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
stanowi nazw widetu wywietlan na licie dostpnych widetw (rysunek 22.2). Jeeli tworzymy po raz pierwszy definicj widetu, upewnijmy si, e poniszy wiersz zostanie dokadnie
skopiowany:
<meta-data android:name="android.appwidget.provider"
Okrelenie android.appwidget.provider jest charakterystyczne dla Androida i powinno zosta umieszczone w kodzie; to samo dotyczy poniszego fragmentu:
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
W pliku tym okrela si w pikselach szeroko i wysoko widetu, jednak Android zaokrgli te
wymiary do rozmiaru najbliszej wielokrotnoci komrki. Obszar ekranu startowego jest zorganizowany w macierz komrek: kada komrka stanowi kwadrat o boku 74 dp (piksele niezalene od gstoci). W dokumentacji Androida mona znale zalecenie, aby wymiary tworzonych widetw stanowiy wielokrotno wymiarw tych komrek minus 2 piksele (margines
dla zaokrglenia rogw i tak dalej).
Mona tu take znale warto czstotliwoci wywoywania metody onUpdate(). Zaleca si,
aby ta warto nie przekraczaa kilku razy na dob. Wprowadzenie wartoci 0 oznacza cakowity
brak wywoywania aktualizacji. Mona j wykorzysta, w przypadku gdy chcemy sami
kontrolowa aktualizacje za pomoc klasy AlarmManager.
749
Warto atrybutu initialLayout wskazuje rzeczywisty ukad graficzny widetu (listing 22.7).
Natomiast atrybut configure okrela klas aktywnoci konfiguracji. Naley umieci pen nazw tej klasy w tej definicji.
Przyjrzyjmy si teraz waciwemu ukadowi graficznemu widetu.
Aby osign zamierzony efekt, ukad graficzny wykorzystuje zagniedone wzy LinearLayout.
Niektre kontrolki uywaj rwnie pliku definicji ksztatu box1.xml do zdefiniowania granic.
Skorzystalimy z takiej metody tworzenia ukadu graficznego, poniewa jest przydatna nie tylko
w przypadku widetw, lecz take do tworzenia innych ukadw graficznych.
Dobrze jest utworzy aktywno i oddzielnie przetestowa ukady graficzne przed wprowadzeniem ich do widetu (przynajmniej my tak zrobilimy). Uzyskanie odpowiedniego wygldu i dziaania widetu kosztowao nas wiele prb. Bezporednie eksperymentowanie na widetach
moe si okaza dosy nuc czynnoci; po kadym uruchomieniu aplikacji naley kolejno
usun, odinstalowa, zainstalowa i ponownie umieci widety na stronie startowej.
Do tej pory omwione pliki stanowi pene definicje XML, niezbdne do dziaania typowego
widetu. Zobaczmy teraz, w jaki sposb bdziemy reagowa na wydarzenia cyklu ycia widetu.
Aby si tego dowiedzie, przebadamy klasy dostawcy widetu.
751
753
W metodzie onDeleted() utworzylimy obiekt BDayWidgetModel, a nastpnie zaprogramowalimy jego automatyczne skasowanie z magazynu przechowywanych preferencji.
Poniewa metoda onEnabled() jest wywoywana tylko raz, podczas tworzenia pierwszej instancji, wyczycilimy wszystkie przechowywane dane modeli widetu. Instancja ta zatem rozpoczyna dziaanie jako czysta bez danych. Taka sama czynno jest przeprowadzana w przypadku metody onDisabled(), dziki czemu pami po instancjach widetw zostaje cakowicie
oczyszczona.
W metodzie onEnabled() uruchamiamy dostawc treci, ktry moe teraz odbiera transmitowane komunikaty. Metoda onDisabled() wycza ten skadnik, wic nie bdzie on ju wyszukiwa rozgaszanych komunikatw.
Szczeglnym przypadkiem jest metoda onReceive(). Przed wprowadzeniem
oprogramowania w wersji 1.6 wystpowa bd uniemoliwiajcy wywoanie metody
onDeleted(). Mona byo obej ten problem poprzez jawne dostarczenie metody
onReceive(). Od wersji 1.6 Androida metoda ta staa si zbyteczna; wystarczajca
okazuje si ta sama metoda z bazowej klasy.
Dziki implementacji modeli widetw kod pozostaje czysty. Zajmiemy si teraz kwesti modeli
widetw i sposobem ich implementacji.
Ten interfejs jest zaprojektowany w taki sposb, e wywodzca si z niego abstrakcyjna klasa
przeprowadza implementacj za pomoc okrelonego, trwaego magazynu. Wczeniej ju wspomnielimy, e bdziemy korzysta z tej funkcji wspdzielonych preferencji Androida jako
z trwaego magazynu. Jak wskazuje sama nazwa interfejsu, jest to kontrakt sucy wycznie
do zapisywania. Takie klienty jak BDayWidgetProvider nadal bd zalene od najbardziej wydzielonej klasy tego interfejsu, posiadajcej swoiste metody.
Realizator tego interfejsu musi dostarczy nazw pliku preferencji w odpowiedzi na metod
getPrefName(). Plik ten zostaje nastpnie wykorzystany do zapisania pary klucz warto, uzyskiwanej poprzez metod getPrefsToSave(). W odwrotnej operacji (metoda setValueFor
Pref()) pochodna klasa ma za zadanie ustanowienie wewntrznej wartoci za pomoc danej
pary klucz warto, uzyskanej z magazynu preferencji.
Na koniec nastpuje wywoanie metody init() w pochodnej klasie w celu zaznaczenia, e wartoci zostay odczytane z trwaego magazynu, oraz umoliwienia przeprowadzenia wszelkich
innych inicjalizacji.
Pamitajmy, e w uytkowej aplikacji wprowadza si nieco inn struktur dziedziczenia:
zamiast dziedziczenia bdziemy prawdopodobnie wykorzystywa mechanizm delegacji
umoliwiajcy wielokrotne wykorzystywanie obiektw. Jednak hierarchia dziedziczenia
bdzie si dobrze sprawowaa w naszym testowym widecie, przedstawionym jako przykad
modeli widetw.
755
//abstrakcyjne metody
public abstract String getPrefname();
public abstract void init();
public Map<String,String> getPrefsToSave(){ return null;}
public void savePreferences(Context context){
Map<String,String> keyValuePairs = getPrefsToSave();
if (keyValuePairs == null){
return;
}
757
savePref(prefs,key,value);
}
Dziaanie tej metody rozpoczyna si od uzyskania od pochodnych klas odwzorowania par klucz
warto, gdzie kluczami s atrybuty modelu, a wartociami cigi znakw reprezentujce
wartoci tych atrybutw. Metoda ta nastpnie przekazuje obiektowi context plik SharedPreferences
poprzez metod context.getSharedPreferences(). Interfejs API wymaga unikatowej nazwy
dla tego pakietu. Model pochodny jest odpowiedzialny za jej dostarczenie.
Po uzyskaniu wspdzielonych preferencji zgodnie z dokumentacj Androida naley
uzyska ich modyfikowalne wersje. Nastpnie naley je kolejno zaktualizowa. Po przeprowadzeniu procesu aktualizacji uruchamiamy metod commit(), co spowoduje zapisanie preferencji.
Wicej informacji mona uzyska, przegldajc dokumentacj dotyczc interfejsw API klas
SharedPreferences i SharedPreferences.Editor oraz rozdzia 9.; w zamieszczonym na kocu
rozdziau podrozdziale Odnoniki znajduj si adresy URL, dziki ktrym mona uzyska
powysze informacje. Warto rwnie zwrci uwag na fakt, e pliki wspdzielonych preferencji s napisane w jzyku XML i s przechowywane w katalogu danych pakietu.
Poniewa do przechowywania danych uylimy jednego pliku dla wszystkich instancji widetu,
potrzebny jest mechanizm rozrniania nazw pl pomidzy wieloma instancjami widetu. Jeli
na przykad posiadamy dwie instancje widetu nazwane 1 i 2, wymagane bdzie zastosowanie
dwch kluczy przechowujcych atrybut Name, tak e bd istniay wartoci name_1 oraz name_2.
Takie przeksztacenie przeprowadzamy w nastpujcej metodzie:
protected String getStoredKeyForFieldName(String fieldName) {
return fieldName + "_" + iid;
}
759
Jak wida, w klasie tej znalazy si pewne funkcje zwizane z dat. Zanim przejdziemy do omwienia implementacji aktywnoci konfiguracji, zademonstrujemy kody rdowe tych funkcji.
//DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
df.setLenient(false);
return df;
}
761
//aktualizuje nazw
views.setTextViewText(R.id.bdw_w_days,Long.toString(widgetModel.howManyDays()));
Intent defineIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://www.google.com"));
PendingIntent pendingIntent =
PendingIntent.getActivity(context,
0 /* brak requestCode */,
defineIntent,
0 /* brak flag */);
views.setOnClickPendingIntent(R.id.bdw_w_button_buy, pendingIntent);
763
i tworzy kolejn intencj w celu uruchomienia aktywnoci. I na odwrt trwajca intencja moe
zosta rwnie wykorzystana do uruchomienia usugi. Odnotujmy fakt, e funkcja ta dziaa
z klasami RemoteViews i AppWidgetManager. Zwrmy uwag na nastpujce zadania tej funkcji:
Uzyskanie widoku RemoteViews z ukadu graficznego.
Ustanowienie wartoci tekstowych w widoku RemoteViews.
Zarejestrowanie trwajcej intencji poprzez widok RemoteViews.
Wywoanie klasy AppWidgetManager w celu wysania widoku RemoteViews do widetu.
Przekazanie wyniku kocowego.
Funkcja statyczna updateAppWidget moe zosta wywoana z dowolnego miejsca,
pod warunkiem e znamy identyfikator widetu. Wynika z tego wniosek, e mona
aktualizowa widet z dowolnego miejsca na urzdzeniu oraz z dowolnego procesu
zarwno widocznego, jak i niewidocznego.
765
Odnoniki
Podczas przygotowywania materiaw do tego rozdziau odkrylimy nastpujce przydatne zasoby (zostay one uporzdkowane pod wzgldem przydatnoci):
http://developer.android.com/guide/topics/appwidgets/index.html pod tym
adresem jest dostpna oficjalna dokumentacja zestawu Android SDK dotyczca
widetw.
http://developer.android.com/reference/android/content/SharedPreferences.html
na tej stronie mona znale informacje na temat interfejsu SharedPreferences,
ktrego znajomo jest wymagana do zarzdzania stanami.
http://developer.android.com/reference/android/content/SharedPreferences.Editor.html
pod tym adresem mona poczyta na temat interfejsu SharedPreferences.Editor,
ktry jest zwizany ze wspdzielonymi preferencjami.
http://developer.android.com/guide/practices/ui_guidelines/widget_design.html
informacje dostpne pod tym adresem przydadz si w procesie projektowania
miych dla oka ukadw graficznych widetw.
http://developer.android.com/reference/android/widget/RemoteViews.html na tej
stronie dostpne s informacje na temat interfejsu RemoteViews, ktry musimy
opanowa, aby rysowa i kontrolowa widoki widetu.
http://developer.android.com/reference/android/appwidget/AppWidgetManager.html
widety s zarzdzane przez klas menedera widetw; interfejs API tej klasy
zosta omwiony na powyszej stronie.
http://www.androidbook.com/item/3300 pod tym adresem jeden ze wspautorw
ksiki zamieci informacje oraz uyteczne fragmenty kodu, ktre mog si okaza
przydatne, jeeli musimy szybko zapoyczy kod stanowicy podstaw widetw.
Podsumowanie
Poznawanie moliwoci widetw ekranu startowego w Androidzie okazao si dla nas przyjemnym zajciem. Widety te stanowi nieskomplikowan koncepcj, ktra moe w znaczcy
sposb wpyn na wraenia uytkownika.
Omwilimy teori dotyczc widetw oraz zademonstrowalimy dziaajcy przykad, uatwiajcy zrozumienie ich koncepcji. Wyjanilimy konieczno stosowania modeli widetw i zarzdzania ich stanem. Mamy te nadziej, e Czytelnikom przyda si zaprezentowany przez nas
kod zarzdzania stanem podczas tworzenia wasnych widetw. Na kocu poruszylimy tematyk problemw projektowych oraz ogranicze widetw. W rozdziale 31. znajdziemy znacznie
dokadniejsz charakterystyk widetw zaprezentowanych w wersji 3.0 Androida.
R OZDZIA
23
Wyszukiwanie w Androidzie
Wyszukiwanie w Androidzie
Wyszukiwanie w Androidzie rozszerza moliwoci znanego nam internetowego paska wyszukiwania Google o zdolno przeszukiwania zarwno lokalnej zawartoci urzdzenia, jak i treci
zewntrznych, udostpnionych w internecie. Mechanizm ten moe by rwnie wykorzystany
do bezporedniego wywoywania aplikacji z poziomu paska wyszukiwania na stronie startowej.
Android umoliwia korzystanie z tych funkcji poprzez wprowadzenie struktury wyszukiwania
pozwalajcej na wspuczestniczenie w niej aplikacji lokalnych.
Protok przeszukiwania w Androidzie jest prosty. Dostpne jest pojedyncze pole wyszukiwania,
w ktrym uytkownicy wpisuj poszukiwany cig znakw. Jest tak zarwno w przypadku stosowania pola wyszukiwania globalnego na stronie startowej, jak i przeszukiwania wasnej
aplikacji wykorzystywane jest to samo pole wyszukiwania.
Tekst wprowadzany przez uytkownika jest pobierany przez system ju w trakcie wpisywania
i przekazywany rnym aplikacjom, ktre zostay zarejestrowane do odpowiadania na proces
wyszukiwania. Zareaguj one w ten sposb, e przeka zestaw odpowiedzi. Android zbiera te
odpowiedzi z rnych aplikacji i wywietla je w postaci listy moliwych propozycji.
Po klikniciu jednej z tych odpowiedzi system wywouje aplikacj przedkadajc wybran propozycj. W tym sensie mamy do czynienia z wyszukiwaniem sfederowanym (pojcie to oznacza
mechanizm umoliwiajcy zintegrowany dostp do rozproszonych zasobw) wewntrz zbioru
wspuczestniczcych aplikacji.
Chocia oglna idea jest cakiem prosta, szczegy protokou wyszukiwania s dosy zoone.
W dalszej czci rozdziau bdziemy je objania na dziaajcych przykadach. W tym podrozdziale przyjrzymy si procesowi wyszukiwania z perspektywy uytkownika.
769
Rysunek 23.1. Strona startowa Androida z widocznym polem QSB i przyciskiem wyszukiwania
Poniewa pole QSB zostao wstawione w formie widetu (rozdzia 22. zosta powicony tematyce widetw), moemy przenie je na ekran startowy, jeeli jeszcze go tam nie ma. Rwnie dobrze moemy to pole usun z ekranu startowego wystarczy je przenie do kosza.
Oczywicie zawsze moemy je pniej przywrci z poziomu ekranu widetw.
W celu rozpoczcia wyszukiwania moemy bezporednio pisa w polu QSB. Moemy wtedy
zaobserwowa interesujcy efekt spowodowany tym, e pole QSB jest widetem: bezporednio
po uaktywnieniu pola QSB na ekranie startowym system uruchamia aktywno wyszukiwania
globalnego (rysunek 23.2), w wyniku czego opuszczamy kontekst tego ekranu. Rysunek 23.2 zosta
wykonany w wersji 2.2 Androida. Ekran ten wyglda identycznie w wersji 2.3 systemu.
Jak ju wspomnielimy, moemy take wywoa proces wyszukiwania, klikajc odpowiedni przycisk dziaania. Przyciski dziaania stanowi zestaw przyciskw widocznych na rysunku 23.1 po
prawej stronie. Na interesujcym nas przycisku zosta umieszczony symbol lupy.
Podobnie jak w przypadku przycisku ekranu startowego, moemy klikn przycisk wyszukiwania w dowolnym momencie, bez wzgldu na uruchomion aplikacj. Jednak jeeli aplikacja
zostaa umieszczona w gwnym wtku, umoliwia ona zawenie wyszukiwania, czym zajmiemy si w dalszej czci rozdziau. Takie zawone wyszukiwanie nazywane jest wyszukiwaniem lokalnym. Bardziej oglne, powszechne i niewyspecjalizowane wyszukiwanie nosi miano
wyszukiwania globalnego.
W wersjach systemu starszych od 2.2 pole wyszukiwania globalnego nie rozrniao poszczeglnych dostawcw propozycji wyszukiwania (lub wyszukiwarek). Poczwszy od wersji 2.2,
aplikacja Android Search pozwala wybra konkretny kontekst wyszukiwania (synonim dostawcy
propozycji).
W tym celu naley klikn ikon widoczn po lewej stronie pola QSB. Zostanie otwarta lista
poszczeglnych aplikacji przeszukujcych. Lista taka (dla wersji 2.2 Androida) zostaa zaprezentowana na rysunku 23.3. W przypadku wersji 2.3 systemu widok ten ulega niewielkiej zmianie w prawej grnej czci rozwinitej sekcji kryteriw wyszukiwania zostaa wprowadzona
maa ikona ustawie przeszukiwania.
Jest to domylny zbir aplikacji wyszukujcych (lub kontekstw czy te typw wyszukiwania,
ewentualnie dostawcw propozycji), dostpnych w emulatorze dla wersji 2.2 i 2.3 systemu.
W nowszych wersjach lista ta moe wyglda inaczej. Kontekst wyszukiwania Wszystko zaj
miejsce wyszukiwania globalnego, znanego z wczeniejszych wersji Androida.
Moemy rwnie utworzy wasny kontekst wyszukiwania poprzez napisanie dostawcw propozycji wyszukiwania oraz aktywnoci przeszukiwania lokalnego. Zajmiemy si tym zagadnieniem podczas omawiania rnorodnych przykadw zawartych w tym rozdziale.
771
Rysunek 23.3. Pole wyszukiwania globalnego z widocznymi kontekstami rnych aplikacji wyszukujcych
Przyjrzyjmy si jednak ponownie propozycjom. Android pobiera tekst wpisany w polu wyszukiwania i wyszukuje obiekty znane jako dostawcy propozycji. Android wywouje asynchronicznie kadego dostawc propozycji w celu uzyskania pasujcych propozycji, przybierajcych
posta zbioru krotek. Android oczekuje, e te krotki (zwane propozycjami wyszukiwania) bd
pasowa do zestawu predefiniowanych kolumn (kolumny propozycji). W miar przeszukiwania
tych znanych kolumn Android bdzie kompletowa list propozycji. Po zmianie tekstu wpisanego w polu QSB Android przeprowadzi cay proces od pocztku. Taki sposb pracy, polegajcy na wywoywaniu wszystkich dostawcw propozycji w celu uzyskania propozycji wyszukiwania,
jest waciwy w przypadku kontekstu odpowiedzialnego za wyszukiwanie globalne. Jeeli jednak wybierzemy konkretny kontekst wyszukiwania, na przykad taki, jaki jest widoczny na rysunku 23.3, w celu odczytania propozycji wyszukiwania zostanie przywoany jedynie dostawca
propozycji zdefiniowany dla tej aplikacji.
Zbir propozycji wyszukiwania zwany jest take kursorem propozycji. Wynika to z faktu,
e dostawca treci reprezentujcy dostawc propozycji przekazuje obiekt cursor.
Jeeli w tym momencie ponownie klikniemy w polu QSB, system znowu wywietli klawiatur
programow. Kolejn rzecz z rysunku 23.4 wart odnotowania jest zaleno pomidzy zaznaczon propozycj a tekstem wyszukiwania w polu QSB. Tekst wyszukiwania cigle skada si
wycznie z litery a, mimo e zosta zaznaczony konkretny element, w naszym wypadku aplikacja Aparat. Jednak nie zawsze tak jest, co wida na rysunku 23.5, ktry przedstawia zaznaczenie
propozycji wskazujcej adres sklepu Amazon.
Zauwamy, e wstawiona przez nas litera a zostaa zastpiona przez peny adres URL serwisu
Amazon. Moemy teraz klikn strzak (ktr bdziemy nazywa strzak nawigacji), aby
otworzy stron Amazon, lub zwyczajnie klikn zaznaczon propozycj. W obydwu przypadkach skutek bdzie identyczny.
773
775
Dostp do tego widoku uzyskujemy poprzez kliknicie ikony reprezentujcej list aplikacji,
umieszczonej w dolnej czci ekranu urzdzenia (ekran startowy zosta pokazany na rysunku 23.1).
Naley wyszuka tam aplikacj Ustawienia, ktrej ikon pokazalimy na rysunku 23.8, i uruchomi
j. Zostanie wywietlona strona z ustawieniami Androida, przypominajca ekran z rysunku 23.9.
Znajdujemy w tej aktywnoci zakadk Okno szybkiego wyszukiwania i wybieramy opcj Wyszukiwane elementy (Wybierz elementy do wyszukania w telefonie). Ukae si zaprezentowana
na rysunku 23.11 lista dostpnych dostawcw propozycji (nazywanych czasem aplikacjami wyszukujcymi). Przypominamy, e zalenie od wersji systemu lista ta moe wyglda inaczej.
777
Dostawcy propozycji (lub stanowice ich czci aplikacje), wczone do wyszukiwania globalnego, s pokazane na rysunku 23.11 jako zaznaczone. Nowy dostawca propozycji nie jest domylnie zaznaczony. Aby zosta doczony do procesu wyszukiwania, trzeba klikn jego nazw.
Po takim wczeniu dany dostawca bdzie umieszcza propozycje w oknie globalnego wyszukiwania. Taki wczony dostawca propozycji zostanie rwnie wywietlony pord aplikacji wyszukujcych, widocznych na rysunku 23.3.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
779
Zadaniem tego kodu jest odgrywanie roli prostej aktywnoci, niezwizanej z wyszukiwaniem.
Jednak ta przykadowa aktywno steruje rwnie wywoywaniem pozostaych testowanych
przez nas aktywnoci. Dlatego wida w kodzie dodatkowe elementy menu reprezentujce te
aktywnoci. Kada funkcja rozpoczynajca si od instrukcji typu invoke zawiera kod pozwalajcy na uruchomienie pozostaych rodzajw aktywnoci.
Wszystkie pliki niezbdne do kompilacji zostay umieszczone jeden po drugim, jednak ju teraz
Czytelnik moe oznaczy jako komentarze funkcje typu invoke lub doczy do projektu kod
tych klas. Aby uatwi zadanie, opatrzylimy ju znakami komentarzy odpowiednie wiersze.
Spjrzmy na plik manifest, aby dowiedzie si, w jaki sposb omawiana aktywno jest definiowana (listing 23.2). Widoczne s rwnie definicje pozostaych aktywnoci, zostan one jednak omwione w dalszej czci rozdziau. Take tym razem oznaczylimy jako komentarze te
dodatkowe aktywnoci a do czasu, gdy bd potrzebne.
Listing 23.2. Interakcja aktywnoci z przyciskiem wyszukiwania plik manifest
//nazwa pliku: manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.search.nosearch">
<application android:icon="@drawable/icon"
781
-->
</application>
<uses-sdk android:minSdkVersion="4" />
</manifest>
Zauwamy, e klasa RegularActivity jest zdefiniowana jako gwna aktywno tego projektu
i nie posiada innych parametrw zwizanych z wyszukiwaniem.
Plik ukadu graficznego tej aktywnoci zosta ukazany na listingu 23.3.
Listing 23.3. Plik ukadu graficznego standardowej aktywnoci
//nazwa pliku: layout/main.xml
<?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"
>
<TextView
android:id="@+id/text1"
Zaprezentujemy teraz wykorzystywane w tym projekcie zasoby znakowe. Na listingu 23.4 zostay
umieszczone cigi znakw wykorzystywane w innych aktywnociach. Jednak te dodatkowe zasoby nie powinny mie wpywu na proces kompilowania biecej aktywnoci, nawet jeli nie zostay wprowadzone pozostae klasy.
W ten sposb na listingu 23.4 zostaa zaprezentowana zawarto pliku strings.xml przechowujcego tekst wywietlany przez nasz aktywno. Pogrubion czcionk i jako komentarze
zaznaczylimy sekcje odpowiedzialne za poszczeglne aktywnoci.
Listing 23.4. Interakcja aktywno przycisk wyszukiwania plik strings.xml
//nazwa pliku: /res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!
**************************************************
* regular_activity_prompt
**************************************************
-->
<string name="regular_activity_prompt">
Jest to przykadowa aplikacja, w ktrej testowana jest interakcja pomidzy polem QSB
z przyciskiem wyszukiwania a aktywnoci. Zostay w niej zawarte cztery aktywnoci,
wcznie z niniejsz. Obecnie jest uruchomiona standardowa aktywno. Dostp do
pozostaych trzech aktywnoci uzyskujemy poprzez menu.
\n\n
Jest to standardowa aktywno, nieposiadajca funkcji zwizanych z wyszukiwaniem.
Jeeli klikniemy teraz przycisk wyszukiwania, zostanie wywoane przeszukiwanie
globalne.
\n
\nPozostae aktywnoci to:
\n\n1) Aktywno braku wyszukiwania: aktywno, w ktrej wyszukiwanie zostao
wyczone.
\n2) Wywoanie wyszukiwania: programowe wywoanie wyszukiwania globalnego.
\n3) Aktywno wyszukiwania lokalnego: przywouje wyszukiwania lokalne.
\n
\nTutaj bd si pojawiay informacje o bdach.
</string>
<!-**************************************************
* no_search_activity_prompt
**************************************************
-->
<string name="no_search_activity_prompt">
W tej aktywnoci metoda onSearchRequested
zwraca warto false. Przycisk wyszukiwania
bdzie teraz ignorowany.
783
\n
\nMona klikn przycisk powrotu, aby uzyska dostp
do poprzedniej aktywnoci i wybra w menu
inn aktywno.
</string>
<!-**************************************************
* search_activity_prompt
**************************************************
-->
<string name="search_activity_prompt">
Jest to tak zwana aktywno wyszukiwania lub aktywno wynikw wyszukiwania.
Ta aktywno jest wywoywana podczas wcinicia przycisku wyszukiwania w trakcie
uywania tej aktywnoci przez inn aktywno w roli aktywnoci wynikw wyszukiwania.
\n\n
Zazwyczaj moemy uzyska z intencji cig znakw kwerendy,
aby dowiedzie si, czym jest ta kwerenda.
</string>
<!-**************************************************
* search_invoker_activity_prompt
**************************************************
-->
<string name="search_invoker_activity_prompt">
W tej aktywnoci element menu wyszukiwania jest stosowany
do wywoania domylnego wyszukiwania. W tym przypadku
nie zostao dla tej aktywnoci okrelone wyszukiwanie lokalne, wic
zostaje wywoane wyszukiwanie globalne. Kliknicie
przycisku menu spowoduje wywietlenie menu wyszukiwania. Po jego
klikniciu zostanie uruchomione wyszukiwanie globalne.
</string>
<!-**************************************************
* local_search_enabled_activity_prompt
**************************************************
-->
<string name="local_search_enabled_activity_prompt">
Jest to bardzo prosta aktywno, dla ktrej w pliku manifecie
zostaa wskazana powizana aktywno wyszukiwania.
W ten sposb po wciniciu przycisku wyszukiwania zostaje wywietlone
wyszukiwanie lokalne.
\n\n
Jego lokalno jest widoczna w etykiecie pola QSB oraz w jego podpowiedzi.
Obydwa te elementy pochodz z metadanych wyszukiwania.
\n\n
Po klikniciu ikony kwerendy zostaniemy przeniesieni
do aktywnoci wyszukiwania lokalnego.
</string>
<!-**************************************************
* Inne wartoci
**************************************************
-->
Podobnie jak w przypadku pliku manifestu, plik strings.xml jest wykorzystywany przez wszystkie
aktywnoci tego projektu. Moemy zauway, e obecna w tym pliku staa typu string regular_activity wskazuje tekst widoczny na ekranie aktywnoci standardowej.
Aby wspomc proces kompilacji standardowej aktywnoci, na listingu 23.5 zaprezentujemy
plik z zasobami menu. Chocia zawarte s w nim elementy zwizane z pozostaymi aktywnociami, nie bd one wpywa na proces kompilowania i umoliwi nam korzystanie ze standardowej aktywnoci, dostpnej na listingu 23.1.
Listing 23.5. Plik menu standardowej aktywnoci
<menu xmlns:android="http://schemas.android.com/apk/res/android">
785
Po umieszczeniu powyszego kodu w pliku RegularActivity.java moemy wcisn przycisk wyszukiwania, co spowoduje wywoanie pola globalnego przeszukiwania. Metoda startSearch()
wraz z argumentami zostanie omwiona w dalszej czci rozdziau. Pole globalnego wyszukiwania bdzie wygldao tak jak przedstawione na rysunku 23.2.
787
Po utworzeniu tych dwch plikw (listingi 23.6 oraz 23.7) musimy usun znaki komentarzy
dla kilku sekcji w dwch nastpujcych plikach:
RegularActivity.java (listing 23.1),
AndroidManifest.xml (listing 23.2).
W pliku RegularActivity.java (listing 23.1) usuwamy znaki komentarza z kodu znajdujcego si
w segmencie funkcji invokeNoSearchActivity().
W pliku AndroidManifest.xml (listing 23.2) usuwamy znaki komentarza z definicji aktywnoci
NoSearchActivity. Zwrmy uwag, e mamy do czynienia z plikiem XML. Sposb zaznacza-
nia jako komentarz i usuwania tego oznaczenia w przypadku pliku XML rni si od analogicznej czynnoci przeprowadzanej w pliku Java.
Moemy teraz wywoa aktywno NoSearchActivity, klikajc element menu Aktywno braku
wyszukiwania, widoczny na rysunku 23.13.
Po jego klikniciu zostanie wywietlony ekran pokazany na rysunku 23.14. Teraz wcinicie przycisku wyszukiwania nie spowoduje adnej reakcji; jego wcinicie nie zostanie odnotowane.
W przypadku obecnoci aktywnoci wyczajcej wyszukiwanie kliknicie przycisku
wyszukiwania spowoduje zablokowanie wywoywania zarwno lokalnego, jak i globalnego
wyszukiwania.
789
}
@Override
public boolean onSearchRequested()
{
this.startSearch("test",true,null,true);
return true;
}
}
Po utworzeniu tych trzech plikw (listingi 23.8, 23.9 oraz 23.10) musimy usun znaki komentarza z kilku sekcji w dwch nastpujcych plikach:
RegularActivity.java (listing 23.1),
AndroidManifest.xml (listing 23.2).
W pliku RegularActivity.java (listing 23.1) usuwamy znaki komentarza z kodu znajdujcego si
w segmencie funkcji invokeSearchInvokerActivity().
W pliku AndroidManifest.xml (listing 23.2) usuwamy znaki komentarza z definicji aktywnoci
SearchInvokerActivity.
Rysunek 23.15 ukazuje wygld tej aktywnoci po wywoaniu jej z menu gwnego aktywnoci
RegularActivity (na rysunku 23.13 widzimy element menu Aktywno wywoywania wyszukiwania).
W przypadku tej aktywnoci kliknicie przycisku Szukaj spowoduje wywoanie znanego nam
pola wyszukiwania globalnego, widocznego na rysunku 23.2. Wcinicie przycisku wyszukiwania
rwnie spowoduje wywietlenie globalnego pola QSB, poniewa przesonilimy metod onSearch
Requested() bazowej aktywnoci.
791
QSB jest wywoywane, w przypadku gdy aktywno deklaruje w pliku manifecie potrzeb wyszukiwania lokalnego. Lokalne pole QSB mona odrni od pola QSB globalnego po nagwku
(rysunek 23.18) i podpowiedzi (tekcie znajdujcym si wewntrz pola wyszukiwania) tego
widoku. Jak si bdzie mona przekona, te dwie wartoci pochodz z pliku XML metadanych
wyszukiwania.
Drugim elementem wyszukiwania lokalnego jest aktywno odbierajca z lokalnego pola QSB
wpisany cig znakw oraz wywietlajca zbir wynikw lub danych wyjciowych, zwizanych
z tym cigiem znakw. Aktywno ta czsto jest nazywana aktywnoci wyszukiwania lub aktywnoci wynikw wyszukiwania.
Trzecim, opcjonalnym skadnikiem wyszukiwania lokalnego jest aktywno, ktra moe wywoywa dopiero co wspomnian aktywno wynikw wyszukiwania (drugiego skadnika). Aktywno ta jest czsto nazywana wywoaniem wyszukiwania lub aktywnoci wywoujc wyszukiwanie. Jest ona opcjonalna, poniewa istnieje moliwo bezporedniego wywoania
aktywnoci wyszukiwania lokalnego (drugiego elementu) z poziomu wyszukiwania globalnego
za pomoc propozycji.
Na rysunku 23.16 zostao przedstawione oddziaywanie tych skadnikw midzy sob wewntrz
kontekstu.
Najwaniejsze interakcje zaprezentowane na rysunku 23.16 zostay oznaczone numerami. Poniej zostay dokadniej omwione zjawiska ukazane na tym rysunku.
Aktywno SearchActivity musi posiada zdefiniowan w pliku manifecie
moliwo odbierania da wyszukiwania. Wykorzystuje ona rwnie obowizkowy
plik XML do odpowiedniego wywietlenia lokalnego pola QSB (na przykad jego
tytuu, podpowiedzi itd.) oraz powiadomienia o obecnoci powizanego z nim
dostawcy propozycji (listing 23.12). Na rysunku 23.16 etap ten zosta zaznaczony
liniami nazwanymi Definicja, przebiegajcymi pomidzy aktywnoci SearchActivity
a dwoma plikami XML (plikiem manifestem oraz plikiem metadanych wyszukiwania).
Kad z tych interakcji przeanalizujemy pod ktem odpowiedniego kodu rdowego. Rozpoczniemy od listingu 23.11, zawierajcego kod rdowy aktywnoci SearchActivity (ktrej zadaniem jest odbieranie zapyta i wywietlanie wynikw wyszukiwania).
Listing 23.11. Aktywno SearchActivity oraz jej ukad graficzny
//nazwa pliku: SearchActivity.java
public class SearchActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_activity);
return;
}
}
793
Naley okreli dwa elementy dla aktywnoci wyszukiwania. Musi ona zadeklarowa
moliwo odpowiedzi na dziaania SEARCH, a take okrela plik XML, w ktrym
zawarty jest opis metadanych wymaganych do interakcji z t aktywnoci.
Na listingu 23.12 prezentujemy zawarto pliku XML metadanych wyszukiwania dla aktywnoci
SearchActivity.
Listing 23.12. Plik searchable.xml metadane wyszukiwania
<!-- /res/xml/searchable.xml -->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
/>
Omwienie rnych opcji dostpnych w tym pliku XML znajduje si pod adresem
http://developer.android.com/reference/android/app/SearchManager.html.
795
A teraz moemy skompletowa wszystkie nowe pliki w celu przetestowania dwch nowych
aktywnoci: LocalSearchEnabledActivity oraz SearchActivity. Nazwy plikw oraz numery
odpowiadajcych im listingw znajdziemy poniej:
SearchActivity.java (listing 23.11),
layout/search_activity.xml (cz listingu 23.11),
res/xml/searchable.xml (listing 23.12),
LocalSearchEnabledActivity.java (listing 23.13),
local_search_enabled_activity (listing 23.14).
Po utworzeniu tych plikw musimy usun znaki komentarza z kilku sekcji w dwch nastpujcych plikach:
RegularActivity.java (listing 23.1),
AndroidManifest.xml (listing 23.2).
W pliku RegularActivity.java (listing 23.1) usuwamy znaki komentarza z kodu znajdujcego si
w segmencie funkcji invokeLocalSearchActivity().
W pliku AndroidManifest.xml (listing 23.2) usuwamy znaki komentarza z definicji aktywnoci
LocalSearchEnabledActivity oraz SearchActivity.
Po udanym usuniciu znakw komentarzy z kodu w obydwu plikach mona ponownie skompilowa projekt.
Po uzyskaniu tych aktywnoci wraz z ich ukadami graficznymi moemy wywoa aktywno
z poziomu gwnej aktywnoci RegularActivity, klikajc
element menu Aktywno wyszukiwania lokalnego (na rysunku 23.13 s widoczne elementy
menu). Po wywoaniu aktywno bdzie wygldaa tak jak na rysunku 23.17.
LocalSearchEnabledActivity
Spjrzmy na etykiet oraz podpowied tego pola wyszukiwania. Rni si one od analogicznych elementw pola globalnego wyszukiwania (rysunek 23.2). Pochodz one z metadanych
wyszukiwania zdefiniowanych dla aktywnoci SearchActivity (plik searchable.xml z listingu
23.12). Jeeli wprowadzimy teraz jaki tekst w polu QSB i klikniemy ikon wyszukiwania, zostanie wywoana aktywno SearchActivity (listing 23.11). Ekran tej aktywnoci zosta pokazany na rysunku 23.19.
Rysunek 23.19. Wyniki wyszukiwania w odpowiedzi na dane wpisane w polu wyszukiwania lokalnego
797
lub
this.setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL);
Teraz wprowadzenie jakiej litery, np. t, na ekranie widocznym na rysunku 23.12 spowoduje
wywoanie pola przeszukiwania globalnego.
Na tym zakoczymy omawianie interakcji wyszukiwania z aktywnociami w Androidzie oraz
mechanizmw korzystania z funkcji wyszukiwania. Dowiemy si teraz, w jaki sposb moemy
nie tylko korzysta z procesu wyszukiwania, lecz rwnie wpywa na to, jak przebiega. Zaimplementujemy w tym celu prostego dostawc propozycji wobec globalnego oraz lokalnego pola
wyszukiwania.
799
Jeeli Czytelnik planuje kompilowanie tego projektu bezporednio poprzez kopiowanie i wklejanie kodu rdowego z tej ksiki, radzimy dokonywa tego zgodnie z numeracj listingw.
Alternatywnym rozwizaniem jest pobranie projektw zwizanych z tym rozdziaem, umieszczonych pod adresem URL, ktry znajduje si na kocu rozdziau.
Rozpocznijmy analiz tych plikw od implementacji klasy SimpleSuggestionProvider.
setup
801
Oto te tryby:
DATABASE_MODE_QUERIES
Warto queryString stanowi cig znakw wpisany przez uytkownika. Zostanie ona wywietlona jako propozycja, a jeeli uytkownik j kliknie, cig ten zostanie przesany do aktywnoci wyszukiwania (jako nowe zapytanie wyszukiwania).
Poniej przedstawiamy opis dokumentacji Androida na temat argumentu line2:
Po utworzeniu kodu rdowego naszego prostego dostawcy treci sprawdmy, jak moemy go
zarejestrowa w pliku manifecie.
<!-****************************************************************
* Kod zwizany z wyszukiwaniem: aktywno wyszukiwania
****************************************************************
-->
<activity android:name=".SearchActivity"
android:label="SSSP: Aktywno wyszukiwania"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
803
Zwrmy uwag na uprawnienie prostego dostawcy propozycji w pliku kodu rdowego (listing
23.15) oraz w pliku manifecie (listing 23.16). W obydwu przypadkach jego warto jest nastpujca:
com.androidbook.search.simplesp.SimpleSuggestionProvider
//this.setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_GLOBAL);
this.setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL);
805
queryIntent.getStringExtra(SearchManager.QUERY);
Po zapoznaniu si z zawartoci listingu 23.17 przeledmy, w jaki sposb aktywno wyszukiwania sprawdza dziaanie i odczytuje cig znakw kwerendy.
807
Metadane wyszukiwania
Definiowanie wyszukiwania w Androidzie rozpoczyna si od aktywnoci wyszukiwania. Definiujemy j najpierw w pliku manifecie. Czci jej definicji jest okrelenie miejsca, w ktrym
znajdzie si plik XML metadanych wyszukiwania. Przyjrzyjmy si listingowi 23.16, w ktrym
zdefiniowalimy aktywno wyszukiwania wraz ze ciek do pliku metadanych (searchable.xml).
Listing 23.18 przedstawia plik metadanych wyszukiwania wykorzystywany przez nasz aplikacj.
Listing 23.18. Metadane wyszukiwania dla dostawcy SimpleSuggestionProvider
<!-- nazwa pliku: /res/xml/searchable.xml -->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:queryAfterZeroResults="true"
android:includeInGlobalSearch="true"
android:searchSuggestAuthority=
"com.androidbook.search.simplesp.SimpleSuggestionProvider"
android:searchSuggestSelection=" ? "
/>
809
android:layout_height="wrap_content"
android:text="@string/main_activity_text"
/>
</LinearLayout>
Jeli spojrzymy na definicj tej aktywnoci w pliku manifecie (listing 23.16), stwierdzimy, e
klasa SearchActivity nie zostaa jawnie zdefiniowana dla tej aktywnoci jako domylne wyszukiwanie lokalne. Wynika to z faktu, e t specyfikacj zastosowalimy na poziomie aplikacji,
a nie aktywnoci, wprowadzajc w pliku manifecie nastpujcy fragment kodu:
<meta-data android:name="android.app.default_searchable"
android:value=".SearchActivity" />
Zwrmy uwag, e powyszy fragment zosta umieszczony w pliku manifecie poza aktywnociami (listing 23.16). Dziki tej specyfikacji Android wie, e dla wszystkich aktywnoci
zawartych w tej aplikacji, w tym dla samej aktywnoci SearchActivity, domyln aktywnoci
wyszukiwania jest SearchActivity. Mona wykorzysta t wiedz do wywoania metody
onNewIntent(), klikajc przycisk wyszukiwania podczas sprawdzania wynikw w klasie Search
Activity. W przypadku zdefiniowania domylnego wyszukiwania jedynie na poziomie
prostej aktywnoci wywoania wyszukiwania, a nie na poziomie caej aplikacji, wspomniana
technika nie ma prawa bytu.
811
Rysunek 23.22. Prosty dostawca propozycji aktywno wywietlajca wyniki wyszukiwania lokalnego
poniewa mamy do czynienia z wyszukiwaniem lokalnym i wyranie wida, e propozycja wywodzi si z naszej aplikacji. Jednak podpowied ta pozwoli nam odrni element test1 od innych
elementw o podobnej nazwie w procesie wyszukiwania globalnego.
Nadarza si teraz dobra sposobno wywoania metody onNewIntent(). Na ekranie aktywnoci
wyszukiwania (rysunek 23.22) moemy wpisa na przykad liter t, w wyniku czego zostanie
wywoane wyszukiwanie za pomoc trybu type-to-search, a wywoanie metody onNewIntent()
zostanie zapisane w pliku dziennika.
Zobaczmy, co naley zrobi, aby nasze propozycje zostay wywietlone w polu wyszukiwania
globalnego. Poniewa zamiecilimy obiekt includeInGlobalSearch w pliku searchable.xml,
propozycje te powinny by widoczne rwnie w przypadku wyszukiwania globalnego. Musimy
jednak przedtem zezwoli tej aplikacji na obsug globalnych propozycji wyszukiwania, co zostao zaprezentowane na rysunku 23.24.
Na pocztku rozdziau podalimy instrukcj wywietlenia tego ekranu. Utworzony przez nas
prosty, niestandardowy dostawca propozycji znajduje si teraz na licie aplikacji pozwalajcych
na wyszukiwanie, pod nazw SSSP: Aktywno wyszukiwania. Cig znakw definiujcy t nazw
wywodzi si od nazwy aktywnoci SearchActivity (listing 23.16).
813
Jeeli w polu wyszukiwania globalnego wprowadzimy na przykad liter t, zobaczymy propozycje pochodzce od omawianego w tym podrozdziale dostawcy. Gdy w trybie wyszukiwania globalnego poszukujemy okrelonego elementu, zauwaymy aktywno wyszukiwania lokalnego,
zilustrowan na rysunku 23.22.
Na tym zakoczymy analiz prostych dostawcw propozycji. Dowiedzielimy si, w jaki sposb
mona wykorzysta wbudowan klas SearchRecentSuggestionsProvider do zapamitywania zapyta swoistych dla danej aplikacji. Korzystajc z tej techniki, moemy nawet wprowadza
propozycje lokalne do kontekstu globalnego.
Jednak dziki temu prostemu wiczeniu nie pokazalimy sposobu pisania dostawcw propozycji od podstaw. Co waniejsze, nie przedstawilimy jeszcze jakichkolwiek informacji o sposobie przekazywania zestawu propozycji przez dostawc. Nie powiedzielimy, jakie kolumny
s dostpne w takim zestawie. Aby zrozumie te oraz inne kwestie, trzeba zaimplementowa od
podstaw wasnego dostawc propozycji.
Implementacja niestandardowego
dostawcy propozycji
Technologia wyszukiwania w Androidzie jest zbyt elastyczna, aby nie mona byo jej dostosowa
do wasnych potrzeb. Poniewa w poprzednim podrozdziale pokazalimy, jak korzysta z wbudowanego dostawcy propozycji, wiele funkcji w klasie SearchRecentSuggestionsProvider
pozostao ukrytych i nieomwionych. Przeanalizujemy te pominite szczegy, implementujc
niestandardowego dostawc treci, nazwanego SuggestUrlProvider.
815
817
case SEARCH_SUGGEST:
Log.d(tag,"wywolana propozycja wyszukiwania");
return getSuggestions(query);
case SHORTCUT_REFRESH:
Log.d(tag,"wywolane odswiezenie skrotu");
return null;
default:
throw new IllegalArgumentException("Nieznany adres URL " + uri);
}
}
private Cursor getSuggestions(String query)
{
if (query == null) return null;
String word = getWord(query);
if (word == null)
return null;
Log.d(tag,"kwerenda przekracza dlugosc 3 liter");
MatrixCursor cursor = new MatrixCursor(COLUMNS);
cursor.addRow(createRow1(word));
cursor.addRow(createRow2(word));
return cursor;
}
private Object[] createRow1(String query)
{
return columnValuesOfQuery(query,
"android.intent.action.VIEW",
"http://www.thefreedictionary.com/" + query,
"Wyszukaj na stronie freedictionary.com wyraz",
query);
}
private Object[] createRow2(String query)
{
return columnValuesOfQuery(query,
"android.intent.action.VIEW",
"http://www.google.com/search?hl=en&source=hp&q=define%3A/"
+ query,
"wyszukaj na stronie google.com wyraz",
query);
}
private Object[] columnValuesOfQuery(String query,
String intentAction,
String url,
String text1,
String text2)
{
return new String[] {
query, // _id
text1,
text2,
// text1
// text2
url,
to Android bdzie wysya dwa moliwe identyfikatory URI. Pierwszy typ identyfikator URI
wyszukiwania wyglda nastpujco:
content://com.androidbook.search.suggesturlprovider/search_suggest_query
819
albo
content://com.androidbook.search.suggesturlprovider/search_suggest_query/<kwerenda>
Identyfikator tego typu jest nadawany na pocztku procesu wpisywania tekstu w polu QSB.
W jednej z odmian tego identyfikatora kwerenda jest przekazywana na jego kocu jako dodatkowy element (segment cieki). Moliwo doczania kwerendy jako segmentu cieki jest
definiowana w pliku metadanych wyszukiwania searchable.xml. Powrcimy do tego tematu
podczas szczegowego omwienia metadanych wyszukiwania.
Drugi rodzaj identyfikatora URI jest przeznaczony dla dostawcy propozycji i jest zwizany ze
skrtami wyszukiwania. Skrty wyszukiwania w Androidzie s propozycjami (rysunek 23.3),
ktre system przechowuje w pamici podrcznej, zamiast wywoywa dostawc propozycji w celu
uzyskania nowej treci. Tematyk skrtw wyszukiwania poruszymy podczas analizowania
kolumn propozycji. Na razie wystarczy wiedzie, e drugi rodzaj identyfikatora propozycji przybiera nastpujce ksztaty:
content://com.androidbook.search.suggesturlprovider/search_suggest_shortcut
lub
content://com.androidbook.search.suggesturlprovider/search_suggest_shortcut/
<identyfikator-skrtu>
Identyfikator tego typu jest nadawany przez system podczas prby okrelenia wanoci skrtw
przechowywanych w pamici podrcznej. Taki identyfikator URI nosi nazw identyfikatora URI
skrtu. Jeeli dostawca przekae pojedynczy wiersz, biecy skrt zostanie zastpiony nowym.
Jeeli zostanie przesana warto null, bieca propozycja przestanie by uznawana za wan.
Klasa SearchManager definiuje w Androidzie dwie stae, pozwalajce na odrnianie tych
segmentw identyfikatorw URI (search_suggest_search i search_suggest_shortcut).
S to odpowiednio:
SearchManager.SUGGEST_URI_PATH_QUERY
SearchManager.SUGGEST_URI_PATH_SHORTCUT
W strukturze wyszukiwania poprzez klas SearchManager wprowadzono par staych, wspomagajcych przetwarzanie takich typw MIME. Tymi typami MIME s:
SearchManager.SUGGEST_MIME_TYPE
SearchManager.SHORTCUT_MIME_TYPE
case SEARCH_SUGGEST:
Log.d(tag,"wywolana propozycja wyszukiwania");
return getSuggestions(query);
case SHORTCUT_REFRESH:
Log.d(tag,"wywolane odswiezenie skrotu");
return null;
default:
throw
new IllegalArgumentException("Nieznany adres URL " + uri);
}
Aby zrozumie, jakie dane s przekazywane za pomoc tych dwch argumentw (selection
oraz selectionArgs), musimy spojrze na plik metadanych wyszukiwania searchable.xml.
Na listingu 23.21 zosta przedstawiony kod tego pliku.
Listing 23.21. Metadane wyszukiwania dla niestandardowego dostawcy propozycji
//xml/searchable.xml
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
821
android:searchMode="showSearchLabelAsBadge"
android:searchSettingsDescription="proponuje adresy url"
android:includeInGlobalSearch="true"
android:queryAfterZeroResults="true"
android:searchSuggestAuthority="com.androidbook.search.custom.suggesturlprovider"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestSelection=" ? "
/>
Zwrmy uwag na atrybut searchSuggestSelection. Jest on bezporednio zwizany z argumentem selection metody query() znajdujcej si w naszym dostawcy treci. Jak pamitamy z rozdziau 4., argument ten suy przewanie do przekazywania klauzuli where
wraz z wymienialnymi symbolami ?.
Nastpnie tablica wymienialnych wartoci jest przekazywana do argumentu tablicy selection
Args. Tak si rzeczywicie dzieje. W przypadku okrelenia atrybutu searchSuggestSelection
istnieje zaoenie, e nie chcemy otrzymywa tekstu wyszukiwania poprzez identyfikator URI,
lecz za pomoc argumentu selection metody query(). W takim wypadku proces wyszukiwania w Androidzie bdzie wysya symbol ? (zwrmy uwag na spacj poprzedzajc znak ?)
jako warto argumentu selection i przekazywa tekst kwerendy w postaci pierwszego elementu tablicy argumentw selection.
Jeli nie zdefiniujemy argumentu searchSuggestSelection, tekst wyszukiwania zostanie
przekazany w formie ostatniego segmentu identyfikatora URI. Moemy wybra dowolny sposb.
W naszym przykadzie wykorzystalimy argument selection, a nie identyfikator URI.
//dziaanie kwerendy
final String queryAction = queryIntent.getAction();
Zobaczymy ten kod umieszczony w kontekcie (listing 23.23), gdzie klasa SearchActivity
oczekuje dziaania VIEW lub SEARCH poprzez sprawdzanie wartoci dziaania danej intencji.
Kolejny atrybut, ktrego nie wykorzystalimy, lecz ktry jest dostpny dla dostawcw propozycji, nosi nazw searchSuggestPath. Po okreleniu tej wartoci o typie string zostanie ona
przyczona do identyfikatora URI (wywoujcego dostawc propozycji) tu po segmencie
SUGGEST_URI_PATH_QUERY. Umoliwia to pojedynczemu, niestandardowemu dostawcy propozycji
przetworzenie dwch rnych aktywnoci wyszukiwania. Kada aktywno SearchActivity
bdzie posiadaa inny sufiks identyfikatora URI. Dostawca propozycji moe korzysta z tego sufiksu cieki, aby przekazywa rne zestawy wynikw do docelowej aktywnoci wyszukiwania.
Podobnie jak w przypadku dziaania Intent, rwnie i teraz moemy okreli dane intencji
za pomoc atrybutu searchSuggestIntentData. Jest to identyfikator URI danych, ktry
podczas wywoania moe by przekazany jako cz intencji, wraz z dziaaniem do aktywnoci wyszukiwania.
Atrybut searchSuggestThreshold definiuje liczb znakw, jak naley wpisa w polu QSB,
aby wywoa dostawc propozycji. Domyln wartoci progow jest 0.
Kolejny atrybut, queryAfterZeroResults (przyjmujcy wartoci true lub false), wskazuje,
czy dla nastpnego zestawu znakw ma zosta wywoany dostawca, w przypadku gdy dla biecego
zestawu znakw otrzymano zerowy zestaw wynikw. W przypadku naszego adresu URL istotne
jest, aby wczy t flag, dziki czemu cay tekst kwerendy staje si za kadym razem widoczny.
Po zapoznaniu si z identyfikatorami URI, argumentami selection i metadanymi wyszukiwania zajmijmy si najistotniejszym aspektem dostawcy propozycji kursorem propozycji.
823
Tak jak wszystkie kursory, rwnie kursor propozycji musi zawiera kolumn _id. Jest to kolumna
obowizkowa. Nazwy pozostaych kolumn rozpoczynaj si od przedrostka SUGGEST_COLUMN_.
Stae te s zdefiniowane jako cz odniesienia do interfejsu API SearchManager. Poniej zostan omwione najczciej uywane kolumny. Pen ich list mona znale w umieszczonych
na kocu rozdziau zasobach dotyczcych tego interfejsu API.
text_1 jest to pierwszy wiersz tekstu propozycji (rysunek 23.3).
text_2 jest to drugi wiersz tekstu propozycji (rysunek 23.3).
icon_1 jest to ikona umieszczona po lewej stronie propozycji; przechowuje ona
zazwyczaj identyfikator zasobu.
icon_2 jest to ikona umieszczona po prawej stronie propozycji; przechowuje ona
zazwyczaj identyfikator zasobu.
intent_action jest to argument przekazywany aktywnoci SearchActivity
podczas jej wywoywania w postaci dziaania intencji. W przypadku obecnoci tej
kolumny w metadanych wyszukiwania bdzie ona przesaniaa odpowiednie dziaanie
intencji (listing 23.21).
intent_data s to informacje przekazywane aktywnoci SearchActivity podczas
jej wywoywania w postaci danych intencji. W przypadku obecnoci tej kolumny
w metadanych wyszukiwania bdzie ona przesaniaa odpowiednie dziaanie intencji
(listing 23.21). Jest to identyfikator URI danych.
intent_data_id zostaje ona dodana do identyfikatora URI danych. Jest szczeglnie
przydatna, gdy chcemy jednorazowo wspomnie o gwnej partii danych w metadanych,
a nastpnie zmienia t parti dla kadej propozycji. Dziki tej kolumnie mona
nieco skuteczniej przeprowadzi tak czynno.
query cig znakw kwerendy, wysyany do aktywnoci wyszukiwania.
shortcut_id jak zostao wczeniej wspomniane, wyszukiwanie w Androidzie
przechowuje w pamici podrcznej propozycje dostarczane przez dostawc. Takie
przechowywane propozycje nosz nazw skrtw. Jeeli ta kolumna jest nieobecna,
Android bdzie przechowywa propozycj i nigdy nie zada jej aktualizacji. Jeeli
zostanie umieszczona warto SUGGEST_NEVER_MAKE_SHORTCUT, Android przestanie
przechowywa propozycje w pamici podrcznej. Jeeli wstawimy tu dowoln inn
warto, identyfikator ten zostanie przekazany jako ostatni segment identyfikatora
URI skrtu (wicej informacji znajduje si w podpunkcie Identyfikatory URI
dostawcy propozycji).
spinner_while_refreshing ta warto logiczna pozwala okreli, czy podczas
procesu aktualizowania skrtw ma by uywana kontrolka Spinner.
Istnieje rwnie zmienny zestaw dodatkowych kolumn, umoliwiajcych reagowanie na wcinicie przyciskw dziaania. Zajmiemy si t kwesti podczas omawiania przyciskw dziaania.
Zobaczmy, w jaki sposb nasz niestandardowy dostawca propozycji przekazuje te kolumny.
825
uruchomienia dziaania wyszukiwania jest kliknicie ikony wyszukiwania, bdcej czci pola
QSB; drugi natomiast polega na bezporednim klikniciu propozycji.
Aktywno wyszukiwania musi ustali powd, dla ktrego zostaa wywoana. Informacja ta jest
umieszczona w intencji dziaania. Dlatego intencja ta musi zosta przeanalizowana przez aktywno wyszukiwania. W wielu przypadkach dziaaniem takim jest ACTION_SEARCH. Jednak dostawca propozycji posiada moliwo przesonicia tego dziaania poprzez jawne okrelenie
innego dziaania, korzystajc z metadanych wyszukiwania lub kolumny kursora propozycji.
W naszym przykadzie stosujemy dziaanie VIEW.
W trakcie omawiania prostego dostawcy propozycji wspomnielimy, e istnieje rwnie moliwo skonfigurowania trybu uruchamiania singleTop wobec aktywnoci wyszukiwania. W takim przypadku aktywno musi dodatkowo odpowiedzie na metod onNewIntent(), a take
na metod onCreate(). Omwimy przypadki odpowiadania na obydwie metody i pokaemy
podobiestwa wystpujce pomidzy nimi.
Zastosujemy zarwno metod onNewIntent(), jak i onCreate() w celu przeanalizowania
dziaa ACTION_SEARCH i ACTION_VIEW. W trakcie korzystania z dziaania wyszukiwania spowodujemy po prostu przekazanie tekstu kwerendy uytkownikowi. W przypadku dziaania
widoku przeniesiemy kontrolk do wyszukiwarki i zakoczymy biec aktywno, dziki
czemu uytkownik bdzie mia wraenie wywoania przegldarki bezporednio po klikniciu
propozycji.
Klasa SearchActivity nie musi by aktywnoci uruchamian z poziomu gwnego
menu aplikacji. Upewnijmy si, e nie skonfigurujemy niewiadomie filtrw intencji
w taki sposb jak w przypadku innych aktywnoci, wywoywanych z poziomu gwnego
menu aplikacji.
//dziaanie kwerendy
final String queryAction = queryIntent.getAction();
//dziaanie kwerendy
final String queryAction = queryIntent.getAction();
Log.d(tag,"Nowe dzialanie intencji:"+queryAction);
final String queryString =
queryIntent.getStringExtra(SearchManager.QUERY);
Log.d(tag,"Nowa kwerenda intencji:"+queryString);
if (Intent.ACTION_SEARCH.equals(queryAction))
{
this.doSearchQuery(queryIntent);
}
else if (Intent.ACTION_VIEW.equals(queryAction))
{
this.doView(queryIntent);
}
else {
Log.d(tag,"Nowa intencja utworzona NIE z poziomu wyszukiwania");
}
return;
}
private void doSearchQuery(final Intent queryIntent)
{
final String queryString =
queryIntent.getStringExtra(SearchManager.QUERY);
appendText("Szukasz obiektu:" + queryString);
}
827
Widzimy, e dla dziaania widoku wywoujemy metod doView(), a w przypadku dziaania wyszukiwania doSearchQuery().
Za pomoc funkcji doView() odczytujemy dziaanie oraz identyfikator URI danych i zapeniamy nimi now intencj, a nastpnie wywoujemy aktywno. W ten sposb zostanie przywoana przegldarka. Zakoczymy aktywno w taki sposb, aby przycisk cofania spowodowa powrt
do wywoujcego j procesu wyszukiwania.
W metodzie doSearchQuery() wywietlamy jedynie tekst kwerendy wyszukiwania w widoku.
Przyjrzyjmy si ukadowi graficznemu wykorzystanemu do obsugi funkcji doSearchQuery().
829
android:layout_height="wrap_content"
android:text="@string/search_activity_main_text"
/>
</LinearLayout>
Nadszed odpowiedni moment na zaprezentowanie zawartoci pliku strings.xml, odpowiedzialnego za wywietlanie niektrych cigw znakw w naszej aplikacji.
Plik strings.xml
Przedstawiony na listingu 23.25 plik strings.xml definiuje cigi znakw tekstowych ukadu graficznego oraz takie elementy, jak nazwa aplikacji, niektre cigi znakw konfiguracji wyszukiwania itp.
Listing 23.25. Plik strings.xml
<?xml version="1.0" encoding="utf-8"?>
doView().
Na listingu 23.26
Celem tej funkcji jest wywoanie przegldarki. Gdybymy na kocu nie wywoali funkcji
finish(), uytkownik po klikniciu przycisku cofania zobaczyby widok aktywnoci wyszukiwania, a nie z powrotem ekran wyszukiwania, jak naleaoby si spodziewa.
W idealnym przypadku, aby zapewni uytkownikowi jak najlepszy komfort pracy, kontrolka nie powinna nigdy przechodzi przez aktywno wyszukiwania. Zakoczenie tej aktywnoci rozwizuje problem. Kod z listingu 23.26 daje nam rwnie okazj zbadania, w jaki
sposb przenosimy dziaanie i dane intencji z oryginalnej intencji (ustanowionej przez dostawc propozycji), a nastpnie przekazujemy je do nowej intencji przegldarki.
Wprowadzilimy w tym podrozdziale mnstwo nowych wiadomoci. Przedstawilimy szczegowo implementacje dostawcy propozycji oraz aktywnoci wyszukiwania. W midzyczasie
pokazalimy rwnie plik metadanych wyszukiwania i plik strings.xml. Analiz plikw wymaganych do implementacji naszego projektu zamkniemy badaniem pliku manifestu aplikacji.
<!-****************************************************************
* Kod zwizany z wyszukiwaniem: aktywno wyszukiwania
****************************************************************
831
-->
<activity android:name=".SearchActivity"
android:label="Etykieta aktywnoci wyszukiwania"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
Wykorzystujemy t sam wiedz, ktr uzyskalimy na temat pola QSB, aby wywoa aplikacj
Ustawienia. Skorzystajmy z rozwizania, ktre omwilimy na pocztku rozdziau, i doczmy
nasz aplikacj do propozycji. Gdy to zrobimy, wpiszmy w polu QSB tekst widoczny na rysunku 23.27.
833
Na rysunku 23.31 przedstawiamy widok, jaki pojawi si, jeli nie dodamy przyrostka .m w polu
wyszukiwania globalnego.
835
Przyciski te s zdefiniowane w interfejsie API klasy KeyEvent, ktrej opis mona znale na
nastpujcej stronie: http://developer.android.com/reference/android/view/KeyEvent.html.
Nie wszystkie wymienione przyciski dziaania mona wykorzysta w procesie wyszukiwania,
jednak cz z nich nie sprawia pod tym wzgldem problemw, na przykad keycode_call.
Naley samodzielnie sprawdzi kady przycisk dziaania pod ktem dziaania i przydatnoci.
Po wybraniu interesujcego nas przycisku dziaania moemy powiadomi o tym system poprzez umieszczenie informacji na ten temat w metadanych w sposb przedstawiony na listingu 23.29.
Listing 23.29. Przykadowa definicja przycisku dziaania
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:includeInGlobalSearch="true"
android:searchSuggestAuthority="com.androidbook.
search.simplesp.SimpleSuggestionProvider"
android:searchSuggestSelection=" ? "
>
<actionkey
android:keycode="KEYCODE_CALL"
android:queryActionMsg="call"
android:suggestActionMsg="call"
android:suggestActionMsgColumn="call_column" />
<actionkey
android:keycode="KEYCODE_DPAD_CENTER"
android:queryActionMsg="doquery"
android:suggestActionMsg="dosuggest"
android:suggestActionMsgColumn="my_column" />
.....
</searchable>
Moemy rwnie przyporzdkowa kilka przyciskw dziaania dla tego samego kontekstu wyszukiwania. Poniej wyjaniamy, do czego suy kady element actionkey oraz w jaki sposb
jest stosowany do przetwarzania nacinicia przycisku.
keycode jest to kod przycisku zdefiniowany w interfejsie API klasy KeyEvent,
ktry powinien zosta uyty do wywoania aktywnoci wyszukiwania. Moliwo
wcinicia przycisku identyfikowanego przez taki kod pojawia si dwukrotnie. Pierwszy
837
raz wystpuje ona podczas wpisywania przez uytkownika tekstu wyszukiwania w polu
wyszukiwania, po ktrym nie zostaj wywietlone propozycje. Jeeli funkcja przycisku
dziaania nie zostaa zaimplementowana, uytkownik przewanie kliknie ikon
wyszukiwania w polu QSB. W przypadku zaimplementowania przycisku dziaania
w metadanych wyszukiwania Android pozwala uytkownikowi na wcinicie tego
przycisku zamiast ikony wyszukiwania. Druga sytuacja pojawia si, gdy uytkownik
przejdzie do okrelonej propozycji i wcinie przycisk dziaania. W obydwu przypadkach
dla aktywnoci wyszukiwania system wywouje dziaanie ACTION_SEARCH. Informacj
na ten temat przenosi dodatkowy cig znakw SearchManager.ACTION_KEY. Jeeli
znajduje si w nim jaka warto, to wiadomo, e nastpi wywoanie w odpowiedzi
na wcinicie przycisku dziaania.
queryActionMsg dowolny tekst wpisany w tym elemencie zostaje przekazany jako
warto dodatkowego cigu znakw SearchManager.ACTION_MSG do aktywnoci
wyszukiwania wywoujcej intencj. Jeeli odczytamy t informacj z intencji i jest ona
identyczna z danymi zdefiniowanymi w metadanych, wiadomo bdzie, e nastpuje
bezporednie wywoanie z poziomu pola QSB w wyniku wcinicia przycisku dziaania.
Bez takiego testu nie bdziemy mieli pewnoci, czy dziaanie ACTION_SEARCH zostao
bezporednio wywoane wobec propozycji w odpowiedzi na wcinicie przycisku dziaania.
suggestActionMsg dowolny tekst wpisany w tym elemencie zostaje przekazany
jako warto dodatkowego cigu znakw SearchManager.ACTION_MSG do aktywnoci
wyszukiwania wywoujcej intencj. Dodatkowe przyciski dla tego argumentu i pola
queryActionMsg s takie same. Jeeli przypiszemy tym polom identyczn warto,
na przykad call, nie dowiemy si, w jaki sposb uytkownik wywoa przycisk dziaania.
W wielu przypadkach jest to nieistotne, zatem mona obydwu argumentom przypisa
tak sam warto. Jednak w razie koniecznoci odrnienia obydwu sposobw
wywoania musimy wstawi tu warto inn ni obecna w argumencie queryActionMsg.
suggestActionMsgColumn wartoci argumentw queryActionMsg
i suggestActionMsg s stosowane globalnie wobec danej aktywnoci wyszukiwania
oraz dostawcy propozycji. Nie ma moliwoci zmiany znaczenia dziaania opartego
na propozycji. Mona jednak umieci w metadanych informacj o istnieniu dodatkowej
kolumny w kursorze propozycji. W ten sposb Android pobierze informacje z tej
kolumny i przele j aktywnoci jako cz intencji wywoujcej ACTION_SEARCH.
Co ciekawe, warto tej dodatkowej kolumny jest przesyana w intencji za pomoc
identycznego, dodatkowego klucza, mianowicie SearchManager.ACTION_MSG.
Spord wymienionych atrybutw obowizkowy jest kod przycisku. Ponadto musi by obecny
przynajmniej jeden z pozostaych trzech atrybutw, aby przycisk dziaania zosta uruchomiony.
Jeeli chcemy korzysta z atrybutu suggestActionMsgColumn, musimy zapeni t kolumn w klasie dostawcy propozycji. Gdybymy chcieli uywa obydwu przyciskw, musielibymy w kodzie
z listingu 23.29 umieci dwie dodatkowe kolumny typu string zdefiniowane w kursorze propozycji (listing 23.22), mianowicie kolumny call_column i my_column. W takim przypadku
nasza tablica kolumn kursora powinna przypomina tabel przedstawion na listingu 23.30.
Listing 23.30. Przykadowe kolumny przyciskw dziaania w kursorze propozycji
private static final String[] COLUMNS = {
"_id", // musi zawiera t kolumn
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
Praca ze specyficznym
dla aplikacji kontekstem wyszukiwania
Proces wyszukiwania w Androidzie umoliwia aktywnoci przekazanie dodatkowych danych
wyszukiwania do wywoanej aktywnoci wyszukiwania. Omwimy teraz szczegy tego procesu.
Pokazalimy wczeniej, e aktywno aplikacji moe przesoni metod onSearchRequested(),
dziki czemu otrzymujemy warto false i proces wyszukiwania zostaje wyczony. Co ciekawe,
w ten sam sposb moemy przekaza aktywnoci wyszukiwania dodatkowe dane, specyficzne
dla aplikacji. Na listingu 23.31 zosta zaprezentowany przykad.
Listing 23.31. Przekazywanie dodatkowego kontekstu
public boolean onSearchRequested()
{
Bundle applicationData = new Bundle();
applicationData.putString("string_key","jaka warto typu string");
applicationData.putLong("long_key",290904);
applicationData.putFloat("float_key",2.0f);
startSearch(null,
);
return true;
}
839
long l = applicationData.getLong("long_key");
float f = applicationData.getFloat("float_key");
}
Wczeniej omwilimy pobienie metod startSearch(). Jej opis znajdziemy pod nastpujcym
adresem URL, stanowicym cz dokumentacji klasy Activity: http://developer.android.com/
reference/android/app/Activity.html.
Take tutaj metoda ta przyjmuje cztery wymienione poniej argumenty:
initialQuery (argument typu string),
selectInitialQuery (argument logiczny),
applicationDataBundle (argument typu Bundle),
globalSearchOnly (argument logiczny).
Jeeli pierwszy argument zostanie zastosowany, wypeni tekst kwerendy w polu QSB.
Warto true w drugim argumencie spowoduje zaznaczenie tekstu. Uytkownik bdzie mg
zamieni cay zaznaczony tekst kwerendy na jego poprawion wersj. W przypadku wartoci
false kursor zostanie umieszczony na kocu tekstu kwerendy.
Trzeci argument stanowi oczywicie przygotowywany przez nas zestaw danych.
Jeeli w czwartym argumencie zostanie umieszczona warto true, zawsze bdzie wywoywany
proces wyszukiwania globalnego. Po wprowadzeniu wartoci false najpierw zostanie wywoane
wyszukiwanie lokalne (jeeli jest dostpne); w przeciwnym razie bdzie stosowane wyszukiwanie
globalne.
Odnoniki
Na zakoczenie tego rozdziau chcielibymy podzieli si list zasobw, ktre uznalimy za
przydatne podczas jego pisania.
www.google.com/googlephone/AndroidUsersGuide.pdf zawarto tu przydatne materiay
dla wersji 2.2.1 Androida, pomagajce zrozumie techniki korzystania z funkcji
wyszukiwania przez uytkownika.
www.google.com/help/hc/pdfs/mobile/AndroidUsersGuide-30-100.pdf instrukcja
obsugi wersji 3.0 Androida. Adres ten by do czsto zmieniany w cigu ostatnich
kilku miesicy. W przypadku kolejnej zmiany powinnimy bez trudu odnale
odpowiedni plik, wpisujc w wyszukiwarce Google haso: Android Users Guide.
http://developer.android.com/reference/android/app/SearchManager.html adres
ten zawiera gwn dokumentacj dotyczc procesu wyszukiwania w Androidzie,
utworzon przez firm Google. To samo cze stanowi rwnie rdo materiaw
na temat gwnego obiektu odpowiedzialnego za proces wyszukiwania,
mianowicie klasy SearchManager.
http://developer.android.com/reference/android/app/Activity.html#onNewIntent(andr
oid.content.Intent) w miar tworzenia wasnych aktywnoci wyszukiwania warto
czasem konfigurowa je w trybie singleTop, dziki czemu zostanie wygenerowana
metoda onNewIntent(). Tutaj znajdziemy informacje na jej temat.
http://developer.android.com/resources/samples/SearchableDictionary/index.html
pod tym adresem znajduje si przykadowa implementacja dostawcy propozycji.
cze to wskazuje przede wszystkim kod rdowy implementacji.
http://developer.android.com/reference/android/provider/SearchRecentSuggestions.html
materiay dotyczce interfejsu API przeszukiwania ostatnich propozycji.
http://developer.android.com/guide/topics/fundamentals.html zaprezentowane tutaj
dane pomagaj zrozumie aktywnoci, zadania oraz tryby uruchamiania, zwaszcza
tryb singleTop, czsto uywany jako aktywno wyszukiwania.
http://developer.android.com/reference/android/os/Bundle.html dziki temu
odnonikowi poznamy rne funkcje dostpne w obiekcie Bundle. S to informacje
przydatne zwaszcza podczas implementacji danych wyszukiwania specyficznych
dla aplikacji.
http://www.androidbook.com/notes_on_search na tej stronie znajdziemy wyniki
bada autorw dotyczcych procesu wyszukiwania w Androidzie. Nawet po
opublikowaniu tej ksiki bdziemy aktualizowa zawarto tej witryny.
ftp://ftp.helion.pl/przyklady/and2ta.zip znajdziemy tu projekty testowe przygotowane
z myl o niniejszej ksice. Obiektem naszego zainteresowania jest plik umieszczony
w katalogu ProAndroid3_R23_Wyszukiwanie.
Wyszukiwanie w tabletach
W wersji 3.0 Androida zasadnicza struktura interfejsu API wyszukiwania pozostaa niezmieniona. Jednak pole QSB oraz ustawienia wyszukiwania (dostrzegalne zwaszcza z punktu widzenia uytkownika) zostay nieco zmodyfikowane w celu umoliwienia korzystania z wikszej
powierzchni. Poza tym wszystkie omwione w tym rozdziale koncepcje znajduj zastosowanie
rwnie w przypadku tabletw.
Podsumowanie
W tym rozdziale zaprezentowalimy przede wszystkim szczegowe omwienie dziaania procesu wyszukiwania w Androidzie. Wyjanilimy, w jaki sposb aktywnoci i dostawcy propozycji
wsppracuj z procesem wyszukiwania. Zademonstrowalimy sposb wykorzystania klasy
SearchRecentSuggestionsProvider.
Zaprojektowalimy od podstaw niestandardowego dostawc propozycji, a w midzyczasie dokadnie opisalimy kursor podpowiedzi i jego kolumny. Przyjrzelimy si identyfikatorom URI
odpowiedzialnym za uzyskiwanie danych od dostawcw propozycji. Zaprezentowalimy wiele
fragmentw kodu rdowego, uatwiajcych opracowanie i zaimplementowanie wasnych
strategii wyszukiwania.
Dziki elastycznoci samego kursora propozycji wyszukiwanie w Androidzie przeksztaca si
z prostego procesu w przewodnik po informacjach dostpnych w zasigu rki.
R OZDZIA
24
Analiza interfejsu
przetwarzania tekstu na mow
Poczwszy od wersji 1.6 rodowiska Android, dostpny sta si silnik odpowiedzialny za syntez mowy, nazwany Pico. Za jego pomoc aplikacje w Androidzie
mog przeksztaca cigi znakw tekstowych na dwik mow z akcentem typowym dla wybranego jzyka. Technologia przetwarzania tekstu na mow umoliwia uytkownikowi korzystanie z urzdzenia bez koniecznoci spogldania na
ekran. W przypadku platformy mobilnej jest to niezmiernie istotna funkcja. Ilu
ludziom zdarzyo si wyj przypadkiem na rodek jezdni w trakcie czytania wiadomoci tekstowej? Czy nie wystarczyoby po prostu odsucha tej wiadomoci?
A gdyby mona byo posucha przewodnika turystycznego, zamiast czyta go
w trakcie zwiedzania? Istnieje olbrzymia liczba aplikacji, w przypadku ktrych
zaimplementowanie mowy zwikszyoby ich uyteczno. W tym rozdziale
przyjrzymy si klasie TextToSpeech i wyjanimy, co zrobi, aby wprowadzony
przez nas tekst zosta wypowiedziany przez urzdzenie. Nauczymy si take zarzdza dostpnymi ustawieniami regionalnymi, jzykami i gosami.
Moemy zmieni uywany jzyk oraz prdko mowy. Opcja Jzyk zarwno tumaczy wymawiane sowa, jak i zmienia akcent gosu, chocia cigle wymawiany bdzie tekst: Jest to przykad syntezy mowy w tumaczeniu na inne jzyki. Pamitajmy, e funkcja TTS obsuguje jedynie generowanie gosu. Tumaczenie zapewnia oddzielny skadnik, na przykad usuga tumacza
Google, omwiona w rozdziale 11. W trakcie przykadowej implementacji funkcji TTS w naszej
aplikacji bdziemy chcieli, aby syntezowany gos mwi w sposb zgodny z wybranym jzykiem,
zatem eby francuski tekst by wypowiadany przez gos mwicy w sposb waciwy dla jzyka
francuskiego. Prdko mowy jest konfigurowana w zakresie od Bardzo wolno do Bardzo szybko.
Naley zachowa ostrono przed ustawieniem opcji Zawsze uywaj moich ustawie. Wybranie jej w opcjach systemowych przez dowolnego uytkownika moe spowodowa nieprzewidywalne zachowanie aplikacji, poniewa definiowane przez ni parametry mog przesoni
ustawienia tego programu.
Wraz z pojawieniem si wersji 2.2 Androida uzyskalimy moliwo korzystania z mechanizmw TTS innych ni Pico (zatem we wczeniejszych wersjach systemu w widoku ustawie nie
byaby dostpna opcja Domylny mechanizm). Uzyskujemy w ten sposb wiksze moliwoci,
poniewa silnik Pico nie nadaje si do wszystkich zastosowa. Nawet w przypadku posiadania
kilku mechanizmw TTS w urzdzeniu znajduje si tylko jedna usuga przetwarzania mowy.
Jest ona wspdzielona przez wszystkie aktywnoci urzdzenia, zatem musimy mie wiadomo, e nie tylko okrelona aplikacja moe korzysta z tej funkcji. Oznacza to take, e nie
moemy by pewni, kiedy (jeeli w ogle) nasz tekst zostanie wypowiedziany. Jednak dziki
interfejsowi funkcji TTS posiadamy dostp do metod zwrotnych, zatem mamy pojcie, co si
dzieje z wysanym przez nas tekstem. Usuga TTS bdzie ledzia wybrany przez nas mechanizm
przetwarzania mowy oraz bdzie go wykorzystywaa do zlecanych operacji. Moe ona korzysta
z dowolnego silnika wywoywanego przez aktywno, zatem pozostae aplikacje mog korzysta
z innych silnikw przetwarzania mowy, a my nie musimy si przejmowa tym aspektem.
Sprawdmy, co si dzieje podczas konfigurowania tych ustawie funkcji TTS. Android uruchamia poza wzrokiem uytkownika usug przetwarzania tekstu na mow oraz silnik Pico,
czyli wielojzyczny mechanizm syntezy mowy. Aktywno ustawie, ktr uruchomilimy,
zainicjalizowaa silnik przetwarzajcy biecy jzyk i prdko mowy. Po klikniciu opcji Posu-
843
chaj przykadu aktywno preferencji przesya tekst do usugi, po czym z kolei silnik przetwarzania mowy wymawia ten tekst poprzez gonik. Silnik Pico rozbija tekst na fragmenty, ktre
potrafi wymwi, i skada te porcje dwikw w cakiem naturalny sposb. Algorytmy tworzce
ten silnik s o wiele bardziej zoone moemy jednak udawa, e mamy do czynienia z magi.
Na szczcie dla nas magia ta zajmuje bardzo niewiele pamici oraz pojemnoci dyskowej, zatem silnik Pico jest idealnym dodatkiem do telefonu.
W poniszym przykadzie utworzymy aplikacj, ktra bdzie odczytywaa na gos wpisywane
przez nas informacje. Nie jest ona skomplikowana; zostaa zaprojektowana w celu pokazania,
jak atwo mona zaimplementowa funkcj przetwarzania tekstu na mow. Na pocztku utwrzmy nowy projekt w Androidzie, korzystajc z artefaktw zamieszczonych na listingu 24.1.
Na kocu rozdziau zamieszczamy odnoniki do pliku zawierajcego omawiane tu projekty.
W ten sposb mona zaimportowa te projekty bezporednio do rodowiska Eclipse.
Listing 24.1. Kody XML i Java prostej wersji demonstracyjnej funkcji TTS
<?xml version="1.0" encoding="utf-8"?>
android.app.Activity;
android.content.Intent;
android.os.Bundle;
android.speech.tts.TextToSpeech;
android.speech.tts.TextToSpeech.OnInitListener;
android.util.Log;
android.view.View;
android.widget.Button;
android.widget.EditText;
}
else {
// Otrzyma co innego
}
}
public void onInit(int status) {
845
speakBtn.setEnabled(true);
}
}
@Override
public void onPause()
{
super.onPause();
W powyszym przykadzie interfejsem uytkownika jest widok EditText, umoliwiajcy wpisywanie sw do gonego odczytania, oraz przycisk inicjalizujcy syntez mowy (rysunek 24.2).
Nasz przycisk zawiera metod doSpeak(), pobierajc cig znakw tekstowych z widoku
EditText i kolejkujc ten tekst za pomoc funkcji speak() zawierajcej argument QUEUE_ADD.
Pamitajmy, e silnik TTS jest wspdzielony, wic nasz tekst moe by kolejkowany za dowolnym innym obiektem (nie zdarza si to jednak zbyt czsto). Inn opcj poza QUEUE_ADD jest
QUEUE_FLUSH, powodujca usunicie z kolejki innego tekstu i natychmiastowe odtworzenie naszego. Na kocu metody onCreate() uruchamiamy obiekt Intent, dajcy od funkcji TTS, aby powiadomia nas o poprawnoci wpisanego tekstu. Poniewa wymagamy zwrotu
odpowiedzi, stosujemy metod startActivityForResult() i przekazujemy kod dania. Odpowied otrzymujemy w metodzie onActivityResult(), w ktrej szukamy argumentu
CHECK_VOICE_DATA_PASS. Poniewa silnik TTS moe przekaza rne typy kodu resultCode
oznaczajcego poprawno wykonanych dziaa, nie moemy korzysta z argumentu RESULT_OK.
Inne uzyskiwane wartoci moemy pozna, przegldajc instrukcj przeczania.
Funkcj stop() wywoujemy, poniewa jeeli nasza aplikacja nie bdzie przetwarzana na pierwszym planie, bdzie to oznacza, e uytkownik jest zajty czym innym, zatem gone odczytywanie tekstu powinno zosta przerwane. Nie chcemy zakca adnej zwizanej z dwikiem czynnoci w innej aktywnoci, ktra pojawia si na ekranie. Za pomoc
metody shutdown() powiadamiamy system, e skoczylimy korzysta z silnika TTS, a zasoby, jeli nie s ju wykorzystywane, staj si niepotrzebne i mog zosta zwolnione.
Warto poeksperymentowa na tym przykadzie. Sprawdmy rne zdania lub wyraenia. Umiemy duy blok tekstu, aby sprawdzi, jak syntezator si sprawuje przy duszych wypowiedziach. Zastanwmy si teraz, co by si stao, gdyby w trakcie czytania takiego duego bloku
tekstu praca naszej aplikacji zostaa przerwana, na przykad z powodu innej aplikacji uzyskujcej dostp do silnika TTS, dziaajcej w trybie QUEUE_FLUSH, albo gdyby nasza aplikacja zesza
po prostu na dalszy plan. Przetestujmy takie zachowanie, wciskajc przycisk ekranu startowego
w trakcie odczytywania duej partii tekstu. Z powodu implementacji funkcji stop() w metodzie onPause() proces przetwarzania tekstu na mow zostaje zatrzymany, nawet jeli aplikacja
cay czas dziaa w tle. Skd mamy wiedzie, w ktrym miejscu aplikacja skoczya odczyt, w przypadku gdy znowu znajdzie si na pierwszym planie? Byaby to przydatna funkcjonalno,
gdyby istnia sposb zapamitywania punktu przerwania odczytywania tekstu, aby po ponownym uruchomieniu aplikacji proces gonego odczytywania tekstu rozpocz si od punktu
przerwania, a przynajmniej w jego pobliu. Istnieje rozwizanie, lecz jest nieco pracochonne.
847
W pierwszej kolejnoci trzeba si upewni, e nasza aktywno MainActivity implementuje rwnie interfejs OnUtteranceCompletedListener. Dziki temu uzyskamy metod zwrotn z silnika
TTS po zakoczeniu odczytywania wyrae. Musimy take zmodyfikowa metod doSpeak()
przycisku, aby przekazywaa dodatkowe informacje, czce identyfikator wyraenia z kadym
fragmentem wysyanego tekstu. W naszej nowej wersji aplikacji separatorami wyrae bd
przecinki i kropki. Nastpnie utworzymy ptl przekazywania wyrae za pomoc argumentu
QUEUE_ADD, a nie QUEUE_FLUSH (nie chcemy sami sobie przerywa!), oraz unikatowego identyfikatora wyraenia, ktry w istocie jest prostym licznikiem inkrementacyjnym, oczywicie
przekonwertowanym na dane typu String. Z tego powodu identyfikatorem wyraenia moe
by dowolny tekst; nie jestemy ograniczeni wycznie do cyfr. W rzeczywistoci moemy wykorzysta dowolny cig znakw, chocia zbyt dugie identyfikatory typu string mog by niekorzystne pod ktem wydajnoci. Musimy tak zmodyfikowa metod onInit(), abymy mogli
rejestrowa wywoania oznaczajce zakoczenie przetwarzania wyraenia. Powinnimy take
wprowadzi na kocu metod zwrotn onUtteranceCompleted() wywoujc silnik TTS po
zakoczeniu przetwarzania wyraenia. W naszym przykadzie kade zakoczone wyraenie
spowoduje utworzenie wpisu w dzienniku w narzdziu LogCat.
Po uruchomieniu naszej nowej aplikacji wpiszmy jaki tekst zawierajcy przecinki i kropki,
a nastpnie kliknijmy przycisk Powiedz. Obserwujmy okno narzdzia LogCat w trakcie suchania gosu odczytujcego tekst. Zauwamy, e tekst zostanie natychmiast zakolejkowany, a po
zakoczeniu kadego wyraenia nastpuje wykonanie metody zwrotnej, powodujcej wywietlenie odpowiedniego wpisu w dzienniku. Jeeli w trakcie odczytu tekstu przerwiemy dziaanie
tej aplikacji, na przykad wciskajc przycisk ekranu startowego, zostan przerwane zarwno
proces przetwarzania tekstu na mow, jak i metody zwrotne. Znamy teraz ostatnie odczytane
wyraenie i moemy rozpocz od tego miejsca dalsze odsuchiwanie.
849
<Button android:id="@+id/recordBtn"
android:text="Rejestruj"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:onClick="doButton"/>
<Button android:id="@+id/playBtn"
android:text="Odtwrz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doButton"
android:enabled="false" />
<TextView android:id="@+id/useWithLabel"
android:text="Wykorzystaj z:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<EditText android:id="@+id/realText"
android:text="Don Quixote"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button android:id="@+id/assocBtn"
android:text="Skojarz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doButton"
android:enabled="false" />
</LinearLayout>
Rysunek 24.3. Interfejs uytkownika aplikacji demonstracyjnej kojarzcej plik dwikowy z tekstem
android.app.Activity;
android.content.Intent;
android.media.MediaPlayer;
android.os.Bundle;
android.speech.tts.TextToSpeech;
android.speech.tts.TextToSpeech.OnInitListener;
android.util.Log;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
android.widget.EditText;
android.widget.Toast;
speakBtn = (Button)findViewById(R.id.speakBtn);
recordBtn = (Button)findViewById(R.id.recordBtn);
playBtn = (Button)findViewById(R.id.playBtn);
assocBtn = (Button)findViewById(R.id.assocBtn);
851
853
@Override
public void onDestroy()
{
super.onDestroy();
if(player != null) {
player.release();
}
if( mTts != null) {
mTts.shutdown();
}
}
}
Aby ten przykadowy kod zadziaa, musimy w pliku AndroidManifest.xml doda uprawnienie
android.permission.WRITE_EXTERNAL_STORAGE. Po uruchomieniu tego przykadu powinnimy ujrze interfejs UI, zilustrowany na rysunku 24.3.
Zarejestrujemy poprawne brzmienie tekstu Don Quixote, nie bdziemy wic korzysta z rzeczywistych sw. Musimy skonstruowa tekst, ktry zostanie wypowiedziany z waciwym
brzmieniem sw. Kliknijmy przycisk Powiedz, aby usysze, jak brzmi utworzona przez nas
imitacja tekstu. Nie najgorzej! Wybierzmy teraz przycisk Rejestruj, dziki czemu brzmienie tego
wyrazu zostanie zapisane w pliku WAV. Po pomylnym zarejestrowaniu dwiku stan si aktywne przyciski Odtwrz i Skojarz. Kliknijmy przycisk Odtwrz, a usyszymy nagrany plik WAV
bezporednio w odtwarzaczu MediaPlayer. Jeeli odpowiada nam brzmienie tego wyrazu,
uyjmy przycisku Skojarz. W silniku TTS zostanie wywoana metoda addSpeech(), ktra
powie nastpnie nasz nowy plik dwikowy z cigiem znakw umieszczonym w polu Wykorzystaj z. Jeeli cay proces przebiegnie pomylnie, wpiszmy w grnym widoku EditText wyraz
Don Quixote i kliknijmy przycisk Powiedz. Teraz imi to zostaje odczytane we waciwy sposb.
Zwrmy uwag, e metoda synthesizeToFile() zapisuje dwik wycznie do formatu WAV,
niezalenie od wstawionego rozszerzenia. Istnieje jednak moliwo skojarzenia innych formatw dwikowych za pomoc metody addSpeech() na przykad plikw MP3. Pliki MP3
nie mog zosta utworzone za pomoc metody synthesizeToFile() silnika TTS.
Zastosowania tej metody w przypadku mowy s bardzo ograniczone. W przypadku scenariusza z nieskojarzonymi sowami to znaczy gdy nie wiemy, jakie sowa bd wypowiadane
nie ma moliwoci przygotowania zawczasu wszystkich plikw dwikowych, ktre posuyyby do naprawienia wyrazw niepoprawnie syntezowanych przez silnik Pico. W scenariuszach zawierajcych skojarzone sowa na przykad odczyt prognozy pogody moemy
przetestowa wszystkie wyrazy w aplikacji, aby znale i odpowiednio zmodyfikowa te, ktre
nie brzmi waciwie. Moemy na przykad przygotowa wczeniej plik dwikowy gotowy do
wypowiedzenia nazwy firmy lub nawet nazwiska!
Istnieje jednak ograniczenie stosowania tej metody: tekst przekazywany metodzie speak() musi
idealnie odpowiada tekstowi wykorzystanemu do wywoania metody addSpeech(). Niestety,
nie moemy zapewni pliku dwikowego dla pojedynczego wyrazu, a nastpnie oczekiwa, e
silnik TTS zastosuje ten plik dla wspomnianego sowa, podczas gdy jest ono przekazywane
metodzie speak() jako cz duszego zdania. Aby nasz plik dwikowy zosta odtworzony,
musimy wprowadzi tekst dokadnie odpowiadajcy temu plikowi. Jeden wyraz mniej lub
wicej, a silnik Pico gubi si i stara si syntezowa dwik najlepiej, jak potrafi.
Jest bardzo prosty sposb dodania plikw dwikowych do silnika TTS. Argument text definiuje cig znakw, dla ktrego bdzie odtwarzany plik audio, packagename stanowi nazw
pakietu, w ktrym jest przechowywany ten zasb, a soundFileResourceId jest identyfikatorem
zasobu tego pliku dwikowego. Pliki dwikowe powinny by przechowywane w katalogu
/res/raw aplikacji. Podczas uruchamiania aplikacji dodajemy przygotowane pliki dwikowe do
silnika TTS poprzez odniesienie si do ich identyfikatora zasobw (np. R.raw.quixote).
Oczywicie bdzie potrzebna jaka baza danych albo predefiniowana lista, dziki ktrej bdzie
wiadomo, jakiemu tekstowi odpowiada dany plik dwikowy. Jeeli internacjonalizujemy aplikacj, moemy przechowywa alternatywne pliki dwikowe w odpowiednim podkatalogu
/res/raw; na przykad /res/raw-fr dla plikw jzyka francuskiego.
855
Nastpnie omwimy pojcia ikony akustycznej oraz odtwarzania ciszy. Rozdzia zamkniemy
tematami konfiguracji ustawie jzykowych oraz wywoa rozmaitych metod.
Opis
STREAM_ALARM
STREAM_DTMF
STREAM_MUSIC
STREAM_NOTIFICATION
STREAM_RING
STREAM_SYSTEM
STREAM_VOICE_CALL
Powodem stosowania ikon akustycznych zamiast standardowego odtwarzania plikw dwikowych w programie MediaPlayer jest mechanizm kolejkowania, zastosowany w silniku TTS.
Nie trzeba oblicza stosownego momentu, w ktrym zostanie odtworzony sygna akustyczny,
ani polega na waciwym dopasowaniu czasowym metod zwrotnych; wystarczy, e zakolejkujemy ikony akustyczne wrd tekstu wysyanego do silnika TTS. Jestemy wtedy pewni, e nasze
ikony akustyczne bd odtwarzane we waciwym momencie, wic moemy wykorzysta takie
samo rozwizanie do zaprezentowania dwikw uytkownikowi. Istnieje take moliwo
wprowadzenia wywoania metody onUtteranceCompleted() powiadamiajcej nas o aktualnej
pozycji odtwarzania.
Odtwarzanie ciszy
Silnik TTS zawiera jeszcze jedn metod, ktr moemy wykorzysta playSilence(). Podobnie jak w przypadku metod speak() i playEarcon(), take ta funkcja posiada trzy argumenty,
z ktrych drugi definiuje tryb kolejki, a w trzecim umieszczone s opcjonalne parametry
HashMap. Pierwszy parametr metody playSilence() jest typu long i okrela w milisekundach
czas trwania ciszy. Najprawdopodobniej metoda ta bdzie stosowana w trybie QUEUE_ADD,
umoliwiajcym oddzielenie w czasie dwch rnych tekstowych cigw znakw.
Innymi sowy, moemy pomidzy dwoma tekstami odtwarza cisz bez koniecznoci zarzdzania czasem oczekiwania w aplikacji. Po prostu wywoujemy metod speak(), nastpnie
playSilence() i znowu speak() w celu osignicia zamierzonego efektu. Poniej przedstawiamy przykadowy sposb wykorzystania metody playSilence() do wprowadzenia dwusekundowego opnienia:
mTts.playSilence(2000, TextToSpeech.QUEUE_ADD, params);
857
Odnoniki
Poniej prezentujemy przydatne adresy, pod ktrymi mona znale materiay rozwijajce zagadnienia omawiane w tym rozdziale:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu list projektw, ktre
moemy pobra i zaimportowa do rodowiska Eclipse. Projekty utworzone z myl
o niniejszym rozdziale zostay umieszczone w katalogu ProAndroid3_R24_TekstNaMow.
859
Podsumowanie
W tym rozdziale pokazalimy, w jaki sposb aplikacja systemu Android moe przemwi do
uytkownika. Android zosta wyposaony w bardzo przyjemny silnik TTS, pozwalajcy na atwe
wykorzystanie tej funkcji. Dla programisty nie ma tu zbyt wiele do nauki. Silnik Pico obsuy
wikszo operacji. Pokazalimy, e jeeli syntezator natrafi na problematyczne sowo, istniej
sposoby uzyskania podanego efektu jego wymawiania. Zaawansowane funkcje silnika rwnie znacznie uatwiaj ycie. Podczas pracy z technologi przetwarzania tekstu na mow musimy pamita o kilku podstawowych zasadach: o oszczdzaniu zasobw, rozsdnym wspdzieleniu silnika TTS oraz waciwym wykorzystaniu syntezatora mowy.
R OZDZIA
25
Ekrany dotykowe
Klasa MotionEvent
W tej czci rozdziau skupimy si na tym, w jaki sposb system przekazuje do
aplikacji informacje o zdarzeniach dotykowych, czyli o tym, e uytkownik
dotkn ekranu. Na razie skupimy si na dotykaniu ekranu za pomoc jednego
palca (funkcja wielodotykowoci zostanie omwiona w dalszej czci rozdziau).
Ekran dotykowy jest wykonany ze specjalnych tworzyw, ktre mog przetworzy
informacj o naciniciu na wsprzdne ekranu. Informacja o dotyku jest przetwarzana na dane, ktre s z kolei przekazywane oprogramowaniu.
Obiekt MotionEvent
Dotknicie ekranu przez uytkownika powoduje utworzenie obiektu MotionEvent. Obiekt ten
zawiera informacje o czasie i miejscu dotknicia oraz inne informacje na temat tego zdarzenia.
Zostaje on przekazany odpowiedniej metodzie danej aplikacji. Moe to by na przykad metoda
onTouchEvent() klasy View. Nie zapominajmy, e klasa View jest nadrzdna w stosunku do
do licznej grupy innych klas, w tym takich jak Layout, Button, List, Surface, Clock i tak
dalej. Oznacza to, e za pomoc zdarze dotykowych moemy oddziaywa na te wszystkie
rodzaje obiektw klasy View. Wywoana metoda moe przeanalizowa obiekt MotionView
w celu podjcia decyzji o rodzaju przeprowadzanej czynnoci. Na przykad klasa MapView moe
wykorzystywa zdarzenia dotykowe do przesuwania mapy na boki, dziki czemu uytkownik
rcznie wyszukuje interesujce go punkty. Obiekt wirtualnej klawiatury moe odbiera zdarzenia dotykowe, ktre po wciniciu klawisza dotykowego s przetwarzane na znaki wprowadzane
do innej czci interfejsu uytkownika.
Obiekt MotionEvent bierze udzia w sekwencji zdarze zwizanych ze zjawiskiem dotykania
ekranu przez uytkownika. Sekwencja taka zostaje uruchomiona w chwili dotknicia ekranu, jest
kontynuowana w trakcie przesuwania palca po ekranie, a koczy si po jego oderwaniu od wywietlacza. Zdarzenia przyoenia palca do ekranu (dziaanie ACTION_DOWN), przesuwania palca
(ACTION_MOVE) i oderwania palca (ACTION_UP) powoduj tworzenie obiektw MotionEvent.
Dziaania typu ACTION_MOVE mog jednorazowo powodowa tworzenie kilku obiektw Motion
Event, gdy palec porusza si po ekranie, zanim pojawi si ostatnie dziaanie ACTION_UP.
Kady obiekt MotionEvent zawiera informacje o rodzaju wykonywanego aktualnie dziaania, lokalizacji dotknicia, sile nacisku, obszarze dotyku, czasie wykonania dziaania oraz o momencie
zainicjalizowania dziaania ACTION_DOWN. Istnieje rwnie czwarty typ dziaania ACTION_
CANCEL. Powiadamia ono aplikacj o zakoczeniu sekwencji dotykowej bez przetwarzania jej
na czynno. Ostatnim rodzajem dziaania jest ACTION_OUTSIDE, ktre jest ustanawiane w specjalnym przypadku, gdy dotknicie odbywa si poza oknem danej aplikacji, lecz musi by
zarejestrowane.
Istnieje rwnie inny sposb odbierania zdarze dotykowych, polegajcy na zarejestrowaniu
w obiekcie klasy View procedury obsugi metod zwrotnych dla zdarze dotykowych. Klasa odbierajca zdarzenia musi zaimplementowa interfejs View.OnTouchListener i powinna zosta
wywoana metoda setOnTouchListener() klasy View w celu skonfigurowania procedury obsugi
tego obiektu. Implementacja klasy interfejsu View.OnTouchListener musi zawiera metod
onTouch(). Podczas gdy parametrem metody onTouchEvent() jest jedynie obiekt MotionEvent,
metoda onTouch() przyjmuje jako parametry obiekty klas View i MotionEvent. Wynika to
z faktu, e klasa OnTouchListener moe odbiera obiekty MotionEvent dla wielu widokw.
Stanie si to bardziej zrozumiae po przeanalizowaniu przykadowej aplikacji.
Jeeli procedura obsugi obiektu MotionEvent (poprzez metod onTouchEvent() lub onTouch())
obsuy dane zdarzenie i informacja o nim nie musi by przekazywana dalej, metoda powinna
powrci z wartoci true. W ten sposb system otrzymuje informacj, e nie trzeba przesya
zdarzenia do innych widokw. Jeeli klasa View nie potrzebuje informacji o tym zdarzeniu
ani o adnym przyszym zdarzeniu zwizanym z t sekwencj dotyku, powraca z wartoci false.
Metoda onTouchEvent() bazowej klasy View nie wykonuje adnej czynnoci i przekazuje
warto false. Jej klasy podrzdne mog, ale nie musz zachowywa si w ten sam sposb. Na
przykad kontrolka Button obsuy zdarzenie dotyku, poniewa dotknicie jest rwnowane
klikniciu, zatem powraca z wartoci true z metody onTouchEvent(). Po otrzymaniu
dziaania ACTION_DOWN kontrolka Button zmieni swj kolor, aby w ten sposb poinformowa,
863
e przetwarza zdarzenie kliknicia. Obiekt ten czeka rwnie na dziaanie ACTION_UP, ktre oznacza zakoczenie czynnoci przez uytkownika, dziki czemu zostaje uruchomiony proces kliknicia. Jeeli kontrolka Button przekazaa z metody onTouchEvent() warto false, nie otrzyma
ju adnego obiektu MotionEvent, informujcego o zdjciu przez uytkownika palca z ekranu.
Jeeli chcemy, aby zdarzenia dotyku definioway now czynno na okrelonym obiekcie View,
moemy rozszerzy klas, przesoni metod onTouchEvent() i wstawi wasny algorytm.
Moemy take zaimplementowa interfejs View.OnTouchListener i skonfigurowa procedur
obsugi metod zwrotnych wobec klasy View. Jeeli skonfigurujemy procedur obsugi wywoania
metody onTouch(), zostan do niej dostarczone wszelkie obiekty MotionView, zanim przejd
do metody onTouchEvent() klasy View. Metoda onTouchEvent() zostanie wywoana jedynie
w przypadku przekazania wartoci false przez metod onTouch(). Przejdmy do przykadowej aplikacji, ktra powinna uatwi zrozumienie omwionych zjawisk.
Na kocu rozdziau zamiecilimy adres URL strony, z ktrej mona pobra
i zaimportowa bezporednio do rodowiska Eclipse projekty utworzone z myl
o niniejszym rozdziale.
Na listingu 25.1 przedstawilimy kod XML ukadu graficznego aplikacji. Utwrzmy nowy projekt w rodowisku Eclipse i wstawmy w nim ten ukad graficzny.
Listing 25.1. Plik XML ukadu graficznego dla aplikacji TouchDemo1
<?xml version="1.0" encoding="utf-8"?>
Naley wyjani kilka spraw na temat tego ukadu graficznego. Wprowadzilimy znaczniki do
obiektw interfejsu uytkownika. Bdziemy odnosi si do tych znacznikw w kodzie rdowym w przypadku wystpowania zdarze dla tych obiektw. Wykorzystalimy take menedery
RelativeLayout do rozmieszczenia obiektw. Zwrmy uwag na zastosowanie niestandardowych kontrolek (TrueButton i FalseButton). Z kodu Java dowiadujemy si, e klasy te
wywodz si z klasy Button. Z tego powodu moemy stosowa wobec nich wszystkie atrybuty
standardowych przyciskw. Na rysunku 25.1 ukazalimy wygld tego ukadu graficznego, a na
listingu 25.2 prezentujemy kod Java naszych przyciskw.
android.content.Context;
android.util.AttributeSet;
android.util.Log;
android.view.MotionEvent;
android.widget.Button;
865
Event(),
android.app.Activity;
android.os.Bundle;
android.util.Log;
android.view.MotionEvent;
android.view.View;
android.view.View.OnTouchListener;
android.widget.Button;
android.widget.RelativeLayout;
867
return false;
}
}
protected static String describeEvent(View view, MotionEvent event) {
StringBuilder result = new StringBuilder(300);
result.append("Dziaanie: ").append(event.getAction()).append("\n");
result.append("Lokacja: ").append(event.getX()).append(" x ")
.append(event.getY()).append("\n");
if( event.getX() < 0 || event.getX() > view.getWidth() ||
event.getY() < 0 || event.getY() > view.getHeight()) {
result.append(">>> Zdarzenie dotyku opucio widok <<<\n");
}
result.append("Flagi krawdzi: ").append(event.getEdgeFlags()).append("\n");
result.append("Sia nacisku: ").append(event.getPressure()).append(" ");
result.append("Rozmiar: ").append(event.getSize()).append("\n");
result.append("Czas dotknicia: ").append(event.getDownTime()).append("ms\n");
result.append("Czas zdarzenia: ").append(event.getEventTime()).append("ms");
result.append(" Szacowany: ").append(event.getEventTime()-event.getDownTime());
result.append(" ms\n");
return result.toString();
}
}
Kod naszej aktywnoci konfiguruje metody zwrotne dla przyciskw i ukadw graficznych
w taki sposb, aby zdarzenia dotyku (na przykad obiekty MotionEvent) byy przetwarzane dla
kadego elementu interfejsu uytkownika. Utworzylimy rozbudowany system zapisywania
informacji o zdarzeniach w dzienniku, zatem bdziemy doskonale wiedzie, co si wydarzyo
podczas dotknicia ekranu. Po skompilowaniu i uruchomieniu aplikacji powinnimy ujrze taki
sam ekran jak na rysunku 25.1.
Aby w peni wykorzysta aplikacj, musimy otworzy w rodowisku Eclipse narzdzie LogCat,
aby na bieco obserwowa komunikaty wywietlane podczas dotykania ekranu. Aplikacja dziaa
rwnie dobrze na emulatorze, jak w rzeczywistym urzdzeniu. Zalecamy take zmaksymalizowanie okna narzdzia LogCat, aby mc atwiej przewija ekran i przeglda wszystkie komunikaty wygenerowane przez aplikacj. Okno to zostanie zmaksymalizowane po dwukrotnym
klikniciu zakadki LogCat. Przejdmy teraz do interfejsu UI aplikacji i dotknijmy przycisku
przekazuje warto true, znajdujcego si u gry ekranu (w przypadku korzystania z emulatora kliknijmy krtko ten przycisk). W oknie LogCat powinny pojawi si przynajmniej dwa
komunikaty o zdarzeniach. Zostan one oznaczone jako przychodzce z obiektu trueBtnTop
i zapisane w dzienniku za pomoc metody onTouch() klasy MainActivity. Moemy spojrze na
kod metody onTouch() w pliku MainActivity.java. Po przeanalizowaniu wynikw w oknie
LogCat, mona stwierdzi, wywoania ktrych metod generuj wartoci. Na przykad warto
wywietlana po etykiecie Dziaanie: pochodzi z metody getAction(). Na listingu 25.4 zosta
przedstawiony przykadowy wpis z dziennika podczas korzystania z emulatora, a listing 25.5
ukazuje to samo, ale podczas uytkowania rzeczywistego urzdzenia.
Listing 25.4. Przykadowe komunikaty narzdzia LogCat, pochodzce z aplikacji TouchDemo1
zainstalowanej na emulatorze
trueBtnTop
trueBtnTop
trueBtnTop
Pierwsze zdarzenie posiada dziaanie oznaczone jako 0, ktrego odpowiednikiem jest ACTION_
Dziaanie ostatniego zdarzenia posiada etykiet 1, oznaczajc ACTION_UP. W przypadku uywania rzeczywistego urzdzenia moe si pojawi wicej zdarze. Wszystkie zdarzenia porednie midzy dziaaniami ACTION_DOWN a ACTION_UP bd prawdopodobnie
okrelane przez dziaanie o wartoci 2, reprezentujce ACTION_MOVE. Pozostaymi moliwociami s wartoci 3 dla dziaania ACTION_CANCEL i 4 dla dziaania ACTION_OUTSIDE. Podczas
DOWN.
869
uywania palcw na prawdziwym wywietlaczu czasami nie ma moliwoci dotknicia i oderwania palca bez nieznacznego przesunicia nim po ekranie, zatem pewne niespodziewane zdarzenia ACTION_MOVE nie powinny budzi zdziwienia.
Istniej rwnie inne rnice pomidzy emulatorem a rzeczywistym urzdzeniem. Zauwamy, e dokadno wskazywania pooenia na emulatorze jest rzdu liczb naturalnych
(52 na 20), podczas gdy rzeczywiste urzdzenie generuje wartoci w formie uamkw (42.8374
na 25.293747). Pooenie zdarzenia MotionEvent posiada skadowe X i Y, gdzie warto X
oznacza odlego od lewej krawdzi widoku View do dotknitego punktu, natomiast warto Y
reprezentuje dystans od grnej czci widoku View do punktu dotknicia.
Zauwamy rwnie, e sia nacisku, jak rwnie jego rozmiar w emulatorze posiadaj warto 0.
W przypadku rzeczywistego urzdzenia sia nacisku wskazuje na to, jak mocno zosta przycinity palec do ekranu dotykowego, a rozmiar oznacza obszar dotknitego wywietlacza. Jeeli
dotkniemy leciutko ekran czubkiem maego palca, wartoci siy nacisku oraz rozmiaru bd
nieznaczne. Jeli natomiast mocno przyciniemy ekran kciukiem, wartoci obydwu parametrw osign wiksze wartoci. Zgodnie z dokumentacj wartoci siy nacisku i rozmiaru
mieszcz si w przedziale od 0 do 1. Jednak ze wzgldu na rnice sprztowe okrelenie bezwzgldnych wartoci tych dwch parametrw moe by sporym problemem. Dobrze byoby porwna wartoci tych dwch parametrw pomidzy poszczeglnymi zdarzeniami MotionEvent, jednak uznanie wartoci wikszej od 0.8 za mocne wcinicie moe sprawi nam nie
lada kopot. Na danym urzdzeniu mona nigdy nie przekroczy wartoci 0.8. Moe nawet nie
uda si przekroczy wartoci 0.2.
Wartoci czasu dotyku i czasu zdarzenia s wyliczane tak samo na emulatorze i w rzeczywistym
urzdzeniu, jedyna rnica polega na tym, e w rzeczywistym urzdzeniu pojawiaj si o wiele
wiksze wartoci. Rwnie obliczenia szacowanego czasu przebiegaj w identyczny sposb.
Flagi krawdzi su do wykrycia momentu, w ktrym dotyk przekroczy fizyczn granic wywietlacza. W dokumentacji zestawu Android SDK wystpuje stwierdzenie, e flagi te su do
wskazywania momentu zetknicia palca z krawdzi ekranu (grn, doln, lew lub praw).
Jednak metoda getEdgeFlags() moe zawsze przekazywa warto 0, w zalenoci od rodzaju
uywanego urzdzenia lub emulatora. W przypadku niektrych ukadw elektronicznych
trudno wykry krawd ekranu, zatem spodziewamy si, e Android przypnie lokacj do brzegu
i skonfiguruje odpowiedni flag brzegu. Nie zawsze tak si dzieje, zatem nie powinnimy zbytnio
polega na konfiguracji flag brzegowych. Klasa MotionEvent zawiera metod setEdgeFlags(),
dziki ktrej moemy samodzielnie wyznaczy takie flagi.
Naley jeszcze wspomnie, e nasza metoda onTouch() powraca z wartoci true, poniewa
kontrolka TrueButton zostaa zakodowana w taki sposb, aby przekazywana bya wanie ta
warto. Otrzymanie tej wartoci oznacza, e klasa MotionEvent zostaa przetworzona i nie ma
potrzeby przekazywa jej dalej. W ten sposb system zostaje rwnie poinformowany, eby
w dalszym cigu przesya zdarzenia tej sekwencji dotknicia do tej metody. To dlatego otrzymujemy komunikat o zdarzeniach ACTION_UP oraz ACTION_MOVE w przypadku rzeczywistego
urzdzenia.
Dotknijmy teraz przycisku przekazuje warto false, znajdujcego si w grnej czci ekranu.
Zademonstrujemy dla pozostaej czci podrozdziau jedynie przykadowy wynik z okna LogCat,
wygenerowany po uyciu rzeczywistego urzdzenia. Wyjanilimy wszystkie rnice, zatem
osoby pracujce na emulatorze powinny umie poprawnie zinterpretowa wyniki pojawiajce
si na ekranie. Listing 25.6 prezentuje wynikowe dane narzdzia LogCat dla dotknicia przycisku
przekazuje warto false.
Widzimy tutaj zupenie odmienne zachowanie, wyjanijmy zatem, co tu zaszo. Android odbiera
zdarzenie ACTION_DOWN w obiekcie MotionEvent i przekazuje je naszej metodzie onTouch() klasy
MainActivity. Metoda ta rejestruje informacj w oknie LogCat i powraca z wartoci false.
Android dowiaduje si w ten sposb, e zdarzenie nie zostao przetworzone, zatem wyszukuje kolejn metod do wywoania, ktr w naszym wypadku okazuje si przesonita metoda
onTouchEvent() klasy FalseButton. Poniewa klasa ta jest rozszerzeniem klasy BooleanButton,
871
Nawet jeeli zdejmiemy palec z przycisku, bdziemy otrzymywa zwizane z nim powiadomienia o zdarzeniach dotyku. Pierwszy rejestr na listingu 25.7 prezentuje wpis zdarzenia zachodzcego poza obrbem przycisku. W naszym przypadku wsprzdna X zdarzenia znajduje si z prawej strony od krawdzi przycisku. Cigle jednak wystpuj wywoania obiektu
MotionEvent, a do momentu wystpienia zdarzenia ACTION_UP, poniewa cigle otrzymujemy
warto true z metody onTouch(). Nawet jeli w kocu oderwiemy palec od ekranu dotykowego
i nie bdzie si on znajdowa na przycisku, metoda onTouch() bdzie cigle wywoywana w celu
przekazywania zdarzenia ACTION_UP, poniewa nieprzerwanie jest przekazywana warto true.
Naley o tym pamita podczas korzystania z obiektw MotionEvent. Po przesuniciu palca poza
widok moemy zadecydowa o anulowaniu wykonywanej czynnoci i otrzyma warto false
z metody onTouch(), dziki czemu nie bdzie powiadamiana o nastpnych zdarzeniach.
Ewentualnie moemy pozwoli na dalsze otrzymywanie zdarze (poprzez przekazanie wartoci
true z metody onTouch()) i wykonywanie operacji jedynie po powrocie palca do widoku.
873
Sekwencja zdarze dotyku zostaa powizana z grnym przyciskiem przekazuje warto true po
otrzymaniu wartoci true z metody onTouch(). W ten sposb Android zosta poinformowany,
e moe przesta szuka elementu odbierajcego obiekty MotionEvent i wysya nam wszystkie
nastpne obiekty MotionEvent zwizane z t sekwencj dotyku. Nawet jeli natrafimy palcem na
inny widok, sekwencja dotyku cigle bdzie dotyczya pierwotnego widoku.
Zobaczmy, co si dzieje z doln poow aplikacji. Dotknijmy umieszczonego tam przycisku
przekazuje warto true. Widzimy, e nastpuje takie same zjawisko jak w przypadku dotknicia
grnego przycisku przekazuje warto true. Poniewa metoda onTouch() powraca z wartoci
true, Android bdzie przesya reszt zdarze w sekwencji dotyku, dopki palec styka si
z wywietlaczem. Teraz dotknijmy dolnego przycisku przekazuje warto false. I znowu metody
onTouch() oraz onTouchEvent() powracaj z wartociami false (obydwie s zwizane z kontrolk falseBtnBottom). Jednak tym razem nastpnym widokiem, ktry otrzyma obiekt Motion
Event, jest ukad graficzny falseLayoutBottom, rwnie przekazujcy warto false. I to tyle.
Poniewa metoda onTouchEvent() wywoaa metod onTouchEvent() klasy bazowej, przycisk zmieni kolor, wskazujc tym samym, e zosta czciowo wcinity. Pozostanie on jednak
w tym stanie, poniewa nigdy nie otrzymamy zdarzenia ACTION_UP w tej sekwencji, gdy nasze
metody cay czas zwracaj warto false. W przeciwiestwie do poprzedniego przykadu, nawet
ukad graficzny nie reaguje na takie zdarzenie. Gdybymy dotknli przycisku przekazuje warto false i przytrzymali go, a nastpnie poprzesuwali palec po ekranie, nie zobaczylibymy adnego wpisu w oknie LogCat, poniewa obiekty MotionEvent nie s wysyane. Zawsze przekazywana jest warto false, system nie bdzie wic informowa o zdarzeniach z tej sekwencji
dotyku. Jeeli rozpoczniemy now sekwencj dotyku, ujrzymy rejestry pojawiajce si w narzdziu LogCat. Jeeli zainicjalizujemy tak sekwencj w dolnym ukadzie graficznym, lecz nie na
obszarze przycisku, zostanie zarejestrowane pojedyncze zdarzenie w obiekcie falseLayoutBottom,
przekazujce warto false, i nic poza tym (dopki nie rozpoczniemy nowej sekwencji dotyku).
Dotychczas wykorzystywalimy przyciski do ukazania efektw zdarze MotionEvent zwizanych z ekranem dotykowym. Warto zauway, e w normalnej sytuacji zaimplementowalibymy operacje na przyciskach za pomoc metody onClick(). Wybralimy do tego przykadu
przyciski, poniewa nie s trudne do utworzenia oraz s klasami podrzdnymi w stosunku do
klasy View, mog wic odbiera zdarzenia dotyku tam samo jak inne widoki. Pamitajmy, e
omwione techniki odnosz si do dowolnego widoku View w aplikacji, bez wzgldu na to, czy
mamy do czynienia ze standardow, czy niestandardow klas widoku.
android.app.Activity;
android.os.Bundle;
android.util.Log;
android.view.MotionEvent;
android.view.VelocityTracker;
875
import
import
import
import
import
import
import
android.content.Context;
android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.util.AttributeSet;
android.view.MotionEvent;
android.view.View;
877
Po uruchomieniu tej aplikacji ujrzymy bia kropk na niebieskim tle. Moemy j dotkn
i przesuwa po obszarze ekranu. Po oderwaniu palca od ekranu kropka nie wraca na pozycj
pocztkow i jest gotowa do dalszego przesuwania. Znacznie uprocilimy cay proces, aby
zaprezentowa jedynie podstawy przesuwania obiektu po ekranie. Metoda draw() umieszcza
kropk w biecej pozycji X i Y. Dziki obiektom MotionEvent przekazywanym w metodzie
onTouchEvent() moemy w miar przesuwania palca po ekranie modyfikowa wsprzdne
X i Y. Rejestrujemy pooenie pocztkowe kropki oraz pocztkowy punkt dotknicia ekranu
w momencie otrzymania zdarzenia ACTION_DOWN. Poniewa uytkownik nie zawsze dotyka
rodka obiektu, wsprzdne dotyku nie s tosame z wsprzdnymi pooenia obiektu.
Musimy rwnie wzi pod uwag przypadek, gdy punktem odniesienia dla naszego obiektu
jest nie rodek, a lewy grny rg ekranu.
879
Wielodotykowo
Po omwieniu obsugi ekranu za pomoc jednego palca moemy przej do kwestii wielodotykowoci. Technologia wielodotykowoci zyskaa olbrzymie zainteresowanie od czasu konferencji
TED w 2006 roku, w czasie ktrej Jeff Han zademonstrowa wielodotykow powierzchni dla
komputerowego interfejsu UI. Uywanie wielu palcw na ekranie otwiera mnstwo moliwoci
manipulowania jego zawartoci. Na przykad rozsunicie dwch palcw na pliku graficznym
moe skutkowa powikszeniem obrazu. Przyoenie kilku palcw na takim obrazie i ich obrt
zgodnie z ruchem wskazwek zegara moe obrci ten rysunek. Obsuga wielodotykowoci zostaa wprowadzona w wersji 2.0 zestawu Android SDK. Pojawia si moliwo wykorzystywania maksymalnie trzech palcw jednoczenie (z technicznego punktu widzenia) do wykonywania
takich czynnoci, jak przyblianie, obracanie oraz wszelkie inne operacje, podczas ktrych korzysta si z technologii wielodotykowoci. Piszemy: z technicznego punktu widzenia, poniewa pierwsze urzdzenia wykorzystujce funkcj wielodotykowoci pozwalay wycznie na obsug dwch palcw. Jednak po duszym zastanowieniu mona stwierdzi, e nie ma w tym
adnej magii. Jeeli elektronika urzdzenia pozwala na wykrywanie wielodotykowoci oraz informuje aplikacj o przesuwaniu palcw po ekranie i o ich zdjciu z wywietlacza, aplikacja ta
moe odczyta, co uytkownik chcia przekaza tym gestem. Mimo e nie ma tu adnej magii,
technologia ta nie naley do najprostszych wynalazkw. W tym podrozdziale sprbujemy pomc
w zrozumieniu idei wielodotykowoci.
W wersji 2.2 Androida wprowadzono w klasie MotionEvent pewne zmiany,
jeszcze bardziej komplikujce ju i tak zoony mechanizm wielodotykowoci,
w tym rwnie przedawnienie dwch omawianych w tym podrozdziale staych
(ACTION_POINTER_ID_MASK oraz ACTION_POINTER_ID_SHIFT). Oznacza to,
e w przypadku urzdze starszych generacji bdziemy stosowa mechanizmy
omwione poniej, natomiast sposb wprowadzania funkcji wielodotykowoci
w urzdzeniach posiadajcych wersj 2.2 Androida lub nowsze zostanie wyjaniony
w dalszej czci podrozdziau.
881
android.app.Activity;
android.os.Bundle;
android.util.Log;
android.view.MotionEvent;
android.view.View;
android.view.View.OnTouchListener;
android.widget.RelativeLayout;
883
Aplikacja dziaa na emulatorze, nie ma jednak moliwoci symulowania dotknicia ekranu kilkoma palcami. W takim przypadku zostan wywietlone wyniki podobne do przedstawionych
w poprzednim przykadzie. Na listingu 25.11 przedstawiamy przykadowe komunikaty narzdzia LogCat dla opisanej kilka stron wczeniej sekwencji dotyku, to znaczy: najpierw palec 1
dotyka ekranu, nastpnie ekranu dotyka palec 2, potem palec 1 odrywa si od ekranu, wreszcie
to samo dzieje si z palcem 2.
Listing 25.11. Przykadowe wyniki narzdzia LogCat dla aplikacji wielodotykowej
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
Action
Action
Action
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
Action
Action
Action
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
Action
Action
Action
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
trueLayout
Zastanwmy si, co si dzieje w tej aplikacji. Pierwszym zdarzeniem jest dziaanie ACTION_DOWN
(warto dziaania rwna 0) wywoane przyoeniem pierwszego palca. Mwi nam o tym metoda
getAction(). Aby dowiedzie si, jakie wyniki s generowane przez poszczeglne metody, wystarczy spojrze na kod metody describeEvent() umieszczony w pliku MainActivity.java.
Otrzymujemy jeden wskanik, ktrego indeks i identyfikator maj przypisan warto 0.
Nastpnie zostanie prawdopodobnie wygenerowanych kilka zdarze ACTION_MOVE (warto dziaania rwna 2) dla tego palca, chocia na listingu 25.11 widzimy tylko jedno z nich. Cigle posiadamy jeden wskanik zawierajcy wymienione wczeniej wartoci.
885
Chwil pniej dotykamy ekranu drugim palcem. Dziaanie otrzymuje teraz warto dziesitn
rwn 261. Co to oznacza? Warto dziaania skada si z dwch czci: wskanika, dla ktrego
jest wykonywane dziaanie, oraz rodzaju przeprowadzanej czynnoci. Po przeksztaceniu wartoci
dziesitnej 261 na warto szesnastkow otrzymujemy 0x00000105. Dziaanie jest oznaczone
przez najmniejszy bajt (w naszym wypadku 5), natomiast identyfikator wskanika jest definiowany przez nastpny dostpny bajt (u nas jest to 1). Zwrmy uwag, e jestemy informowani o identyfikatorze wskanika, a nie o jego indeksie. Po dotkniciu ekranu trzecim
palcem dziaanie uzyska warto 0x00000205 (517 w systemie dziesitnym). Dotknicie czwartym palcem zmienioby warto na 0x00000305 (dziesitne 773) i tak dalej. Nie widzielimy
jeszcze dziaania o wartoci rwnej 5, jest ono jednak znane jako ACTION_POINTER_DOWN.
Rni si od dziaania ACTION_DOWN jedynie tym, e jest wykorzystywane w mechanizmie
wielodotykowoci.
Przyjrzyjmy si nastpnej parze rekordw okna LogCat z listingu 25.11. Pierwszy rejestr odpowiada za zdarzenie ACTION_MOVE (warto dziaania rwna 2). Pamitajmy, e trudno
utrzyma palce w bezruchu na prawdziwym ekranie. Demonstrujemy tylko jedno zdarzenie
ACTION_MOVE, moe ich jednak wystpi wicej. Po podniesieniu pierwszego palca otrzymujemy warto dziaania rwn 0x00000006 (w systemie dziesitnym jest to warto 6).
Podobnie jak we wczeniejszym przypadku, indeks wskanika wynosi 0, a dziaanie nosi nazw ACTION_POINTER_UP (analogicznym dziaaniem w mechanizmie wykorzystujcym jeden palec jest ACTION_UP). Jeeli podniesiemy drugi palec, otrzymamy dziaanie 0x00000106
(262 w kodzie decymalnym). Zauwamy, e nadal otrzymujemy informacje o dwch palcach,
gdy jeden z nich generuje zdarzenie ACTION_UP.
Ostatnia para rekordw na listingu 25.11 przedstawia jeszcze jedno zdarzenie ACTION_MOVE
dla drugiego palca, po ktrym nastpuje dziaanie ACTION_UP. Tym razem widzimy warto
dziaania rwn 1 (ACTION_UP). Nie otrzymalimy wartoci 262, ale za chwil wyjanimy dlaczego. Odnotujmy rwnie fakt, e po oderwaniu pierwszego palca od ekranu indeks wskanika
uleg zmianie z wartoci 1 na 0, ale identyfikator wskanika cigle wynosi 0.
Zdarzenia ACTION_MOVE nie pozwalaj okreli, ktry palec zosta przesunity. Dla kadego
ruchu zawsze bdzie definiowana warto 2, bez wzgldu na liczb wykrywanych palcw czy to,
z ktrym palcem mamy do czynienia. Wszystkie pozycje przyoonych palcw s przechowywane w obiekcie MotionEvent, wic musimy odczytywa wsprzdne i analizowa sytuacj.
Jeeli do ekranu zostanie przyoony tylko jeden palec, identyfikator wskanika pozwoli nam
okreli, ktry palec si porusza, gdy bdzie to jedyny dostpny wskanik. Na listingu 25.11
w miejscu, gdzie pozosta przyoony tylko drugi palec, zdarzenie ACTION_MOVE posiadao indeks
o wartoci 0 oraz identyfikator wskanika rwny 1, nie byo wic problemu z okreleniem palca.
Wracajc do pocztku listingu 25.11, indeks wskanika o wartoci 0 dla pierwszego przyoonego palca posiada warto 0, dlaczego wic nie otrzymujemy wartoci 0x00000005 (dziesitnie 5) dla dziaania, skoro palec ten dotkn ekranu przed innymi palcami? Jest to, niestety,
pytanie, na ktre nie ma dobrej odpowiedzi. Moemy otrzyma warto dziaania rwn 5
w nastpujcym przypadku: najpierw dotykamy ekranu pierwszym palcem, nastpnie drugim
palcem, dziki czemu otrzymujemy wartoci dziaania, odpowiednio, 0 i 261 (pomijamy na razie zdarzenia ACTION_MOVE). Teraz podnosimy pierwszy palec (warto dziaania 6) i ponownie
przykadamy go do ekranu. Identyfikator wskanika dla drugiego palca pozosta niezmieniony
i posiada warto 1. Przez czas oderwania pierwszego palca od ekranu aplikacja miaa jedynie
informacj o identyfikatorze wskanika posiadajcym warto 1. Po ponownym dotkniciu wywietlacza pierwszy palec ponownie otrzyma identyfikator 0. Skoro uywamy wielu palcw,
887
Funkcja wielodotykowoci
w systemach poprzedzajcych wersj 2.2
W wersji 2.2 Androida wprowadzono kilka istotnych zmian do mechanizmu dziaania wielodotykowoci. Wspomnielimy w poprzednim punkcie o uznaniu pewnych staych za przestarzae oraz o wprowadzeniu nowych. W tej wersji systemu pojawiy si rwnie dwie nowe
metody getActionMasked() oraz getActionIndex() uatwiajce okrelenie wskanika
oraz indeksu wykorzystywanych w danym dziaaniu. Za pomoc tych metod moemy podmieni kod z listingu 25.12 fragmentem z listingu 25.13.
Listing 25.13. Przykadowy kod pozwalajcy na okrelenie wartoci dziaania
int action = event.getActionMasked();
int ptrIndex = event.getActionIndex();
int ptrId = event.getPointerId(ptrIndex);
Kod ten jest o wiele prostszy od zaprezentowanego na listingu 25.12. Zwrmy jednak uwag,
e w dziaaniu trzeba uwzgldni nastpujce zmienne: ACTION_DOWN, ACTION_UP, ACTION_MOVE,
ACTION_CANCEL, ACTION_OUTSIDE, ACTION_POINTER_DOWN lub ACTION_POINTER_UP (wartoci
kolejno od 0 do 6). Jeeli chcemy wykorzysta tego typu rozwizanie, moemy po prostu odj 5,
w przypadku gdy metoda getActionMasked() przekae warto wiksz od 4. Moemy rwnie pomczy si z dwiema dodatkowymi wartociami.
Jak ju wczeniej wspomnielimy, zanim zaczniemy tworzy wasn mask, widoczn na przykad na listingu 25.12, musimy pamita, e poczwszy od wersji 2.2 Androida, stae ACTION_
POINTER_ID_MASK oraz ACTION_POINTER_ID_SHIFT zostay uznane za przestarzae, a na ich miejsce wprowadzono stae ACTION_POINTER_INDEX_MASK oraz ACTION_POINTER_INDEX_SHIFT,
definiujce dokadnie te same wartoci. Poniewa te zaktualizowane stae nie s rozpoznawane
Moemy przesoni metod onTouchEvent(), jeli chcemy wprowadzi inne formy obsugi
map. Czciej spotykamy si z przesanianiem metod w klasie Overlay ni w klasie MapView,
zatem w tym punkcie zajmiemy si tym zagadnieniem. Podobnie jak we wczeniej omwionych przypadkach, metoda onTouchEvent() klas Overlay obsuguje obiekty MotionEvent.
Nawet w przypadku map zdarzenie MotionEvent przechowuje wsprzdne X i Y obszaru dotknitego przez uytkownika. Jest to tutaj niemal nieprzydatne, poniewa przewanie bdziemy
chcieli zna wsprzdne geograficzne dotknitego punktu, a nie wsprzdne ekranu. Na
szczcie istniej rozwizania tego problemu.
Klasa MapView zostaa wyposaona w interfejs Projection, ktry zawiera metody przetwarzajce piksele na obiekty GeoPoint i odwrotnie. Dostp do tego interfejsu uzyskujemy poprzez wywoanie metody MapView.getProjection(). Po wprowadzeniu interfejsu Projection do
konwersji moemy wykorzysta metody fromPixels() i toPixels(). Pamitajmy, e klasa
Projection jest przydatna jedynie wtedy, gdy mapa nie ulega zmianie w widoku. Wewntrz
metody onTouchEvent() moemy za pomoc metody fromPixels() przekonwertowa wartoci X
i Y pooenia na obiekt GeoPoint.
Przydatn i jednoczenie interesujc metod klasy Overlay jest metoda onTap(), bardzo podobna do omwionej wczeniej metody onTouch(), rnicej si jednak pewnym kluczowym
aspektem. Klasy Overlay nie posiadaj metody onTouch(). Sygnatura metody onTap() zostaa
pokazana poniej:
public boolean onTap(GeoPoint p, MapView mapView)
889
Oznacza to, e jeli uytkownik dotknie mapy z nakadk Overlay, zostanie wywoana metoda
onTap() wraz z obiektem GeoPoint, ktry okreli wsprzdne wskazanego miejsca. W ten sposb
zaoszczdzimy mnstwo czasu, gdy nie bdziemy musieli podejmowa prb okrelenia dotknitego miejsca na mapie. Nie musimy si ju martwi o konwersj wsprzdnych X i Y
lokacji na wsprzdne geograficzne. Zajmuje si tym system.
Przyjrzymy si teraz ponownie przykadowi z rozdziau 17., w ktrym wywietlalimy map
wraz z przyciskami pozwalajcymi na jej przegldanie w rnych trybach (satelitarny, uliczny,
widok ruchu ulicznego oraz tryb standardowy). Do tego projektu dodamy moliwo uruchamiania trybu widoku ulicznego lokacji wskazanej na mapie. W tym celu musimy umieci nakadk Overlay w widoku MapView, a po dotkniciu nakadki Overlay zdarzenie to zostanie
przeksztacone na wskazan lokalizacj na mapie. Po przeksztaceniu w taki sposb lokalizacji uruchomimy intencj wywoujc tryb widoku ulicznego. Rozpoczniemy od utworzenia
w rodowisku Eclipse kopii aplikacji MapViewDemo z rozdziau 17. (listingi 17.2 i 17.3). Nastpnie
wykorzystamy informacje z listingu 25.14 do zmodyfikowania metody onCreate() gwnej
klasy Activity oraz dodamy now klas, rwnie umieszczon na listingu 25.14, w pliku
ClickReceiver.java. Zmiany w metodzie onCreate() zostay zaznaczone pogrubion czcionk.
Interfejs, widoczny na rysunku 17.3, nie ulegnie zmianie.
Listing 25.14. Dodawanie funkcji dotyku do aplikacji MapViewDemo
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
mapView = (MapView)findViewById(R.id.mapview);
ClickReceiver clickRecvr = new ClickReceiver(this);
mapView.getOverlays().add(clickRecvr);
mapView.invalidate();
}
android.content.Context;
android.content.Intent;
android.net.Uri;
android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
public class ClickReceiver extends Overlay{
private static final String TAG = "ClickReceiver";
private Context mContext;
public ClickReceiver(Context context) {
context = context;
}
@Override
public boolean onTap(GeoPoint p, MapView mapView) {
Log.v(TAG, "Otrzymano klikniecie w tym punkcie: " + p);
if(mapView.isStreetView()) {
To wystarczy, aby uruchomi zmodyfikowan wersj aplikacji chyba e nie posiadamy aplikacji widoku ulicznego1 (StreetView) na emulatorze lub w urzdzeniu. Aplikacja StreetView
zostaa umieszczona w emulatorach CupCake (1.5) i Donut (1.6), a zostaa usunita z emulatora
clair (2.0). Jeeli brakuje nam programu StreetView na emulatorze, moemy wykorzysta
rzeczywiste urzdzenie wyposaone w t funkcjonalno i przetestowa na nim utworzon
aplikacj. Osoby posiadajce wycznie emulator mog wykona nastpujce czynnoci:
1. Skonfiguruj urzdzenie AVD oparte na interfejsie Google API w wersji 1.6 lub 1.5.
2. Uruchom emulator za pomoc urzdzenia AVD zdefiniowanego w punkcie 1.
3. Wykonaj polecenie adb pull /system/app/StreetView.apk, aby skopiowa
aplikacj StreetView.apk z emulatora na dysk twardy stacji roboczej.
4. Skonfiguruj urzdzenie AVD dla wersji interfejsu Google API, z ktrego zamierzasz
korzysta.
5. Zatrzymaj emulator z punktu 2. i uruchom emulator z punktu 4.
6. Wykonaj polecenie adb install StreetView.apk na pliku .apk skopiowanym
w punkcie 3.
Aplikacja StreetView powinna zosta zainstalowana na emulatorze, dziki czemu nasza przykadowa aplikacja zadziaa. W nowszych wersjach Androida nazwa pakietu tej aplikacji brzmi
Street.apk, zatem by moe uda nam si znale wersj nowsz od umieszczonej w Androidzie 1.6.
Po uruchomieniu wieo zmodyfikowanej aplikacji MapViewDemo wykonajmy zblienie pozwalajce na zobaczenie ulic miasta. Kliknijmy przycisk Ulica, aby ulice obsugiwane przez
aplikacj StreetView (zdjcia tych ulic s zamieszczone na przykad w bazie danych Google)
zostay zaznaczone na niebiesko. Dotknijmy teraz jednej z ulic, a zostanie wywoana metoda
onTap() klasy ClickReceiver, ktra z kolei skontaktuje za pomoc intencji aktywno aplikacji
StreetView z dotknit lokalizacj. Jeeli dotkniemy obszar mapy nieobsugiwany przez aplikacj StreetView, pojawi si pusty ekran tej aplikacji wraz z komunikatem typu nieprawidowa
panorama. Oznacza to, e serwer Google nie moe odnale zdj znajdujcych si w pobliu
wybranego miejsca. Kliknijmy przycisk cofania, aby wrci do aplikacji obsugujcej mapy,
i sprawdmy inn lokalizacj. Jeeli zajrzymy do okna LogCat, zauwaymy, e zostay w nim
zapisane wsprzdne geograficzne dotknitej lokacji. Zwrmy uwag, e obiekt GeoPoint
definiuje szeroko i dugo geograficzn danymi typu int, natomiast identyfikator URI aplikacji StreetView wymaga typu float.
1
Tryb widoku ulicznego jeszcze nie jest dostpny w Polsce przyp. red.
891
W naszej przykadowej aplikacji zdecydowalimy si na wysyanie intencji zawierajcej wsprzdne geograficzne dotknitej lokacji do aktywnoci aplikacji StreetView. Moemy sobie jednak
wyobrazi rwnie inne moliwoci. Jeeli znamy szeroko i dugo geograficzn lokalizacji,
moemy wykorzysta obiekt Geocoder do identyfikacji jej okolicy. Nic nie stoi na przeszkodzie,
aby uy informacji o lokalizacji do nawigacji typu zakrt po zakrcie. Istnieje moliwo zmierzenia odlegoci wskazanej lokalizacji od naszego biecego pooenia. Moemy nawet zachowa dane lokalizacji do pniejszego uytku.
Gesty
Gesty s specjalnym przypadkiem zdarzenia dotyku. Pojcie gest jest stosowane wobec rnorodnych czynnoci obsugiwanych przez system Android, poczwszy od prostej sekwencji dotykowej, na przykad szarpnicia lub cinicia, a do formalnych gestw klasy Gesture, ktre zostan omwione w dalszej czci rozdziau. Szarpnicia, cinicia, dugie przycinicia oraz gesty
przewijania posiadaj zdefiniowane zachowanie, aktywowane cile okrelonymi bodcami.
Wikszo osb wie, e szarpnicie oznacza gest, w ktrym palec zostaje przyoony do ekranu,
do szybko przesunity w okrelonym kierunku i na zakoczenie oderwany od wywietlacza.
Na przykad wykonanie tego gestu w aplikacji Galeria (ukazujcej obrazy sekwencyjnie, od lewej
do prawej strony) spowoduje, e kolejne obrazy przemkn przed naszymi oczami.
W niniejszym podrozdziale wykorzystamy wiedz zdobyt na temat obiektw MotionEvent
i rozszerzymy j o gesty, korzystajc z przykadowego gestu ciskania. To nie jest wcale takie
trudne, jak mogoby si wydawa. Gest ciskania nie jest jawnie obsugiwany w wersjach Androida starszych od 2.2, jeli wic chcemy zaimplementowa w nich ten ruch, musimy wasnorcznie napisa kod odczytujcy obiekty zdarze oraz wykonujcy odpowiednie dziaanie. Tym
si wanie zajmiemy. Poczwszy od wersji 2.2 systemu, uzyskujemy kilka pomocnych funkcji,
pozwalajcych na korzystanie z takich gestw jak ciskanie; poznamy je w dalszej czci rozdziau.
Zaprezentujemy nastpnie kilka klas, pomocnych w definiowaniu innych gestw, na przykad szarpni i dugich przycini. Std pozostanie tylko krok do wprowadzenia niestandardowych gestw, tj. gestw, ktre moemy sami zarejestrowa, a ktrych odtworzenie
przez uytkownika w naszej aplikacji uruchomi okrelon czynno. Najpierw jednak pobawmy
si gestem ciskania!
Gest ciskania
Jednym z ciekawszych zastosowa wielodotykowoci jest gest ciskania, wykorzystywany do
zmiany skali obrazu. Mechanizm ten opiera si na koncepcji rozsuwania i ciskania palcw:
jeeli dotkniemy wywietlacz dwoma palcami i je rozsuniemy, aplikacja powinna zareagowa
powikszeniem obrazu, jeeli natomiast je ciniemy, dany element zostanie zmniejszony.
Gest ten jest najczciej wykorzystywany w aplikacjach obsugujcych obrazy, na przykad pokazujcych mapy.
W celu zademonstrowania procesu implementacji gestu ciskania zmodyfikujemy poprzedni
aplikacj. Na listingu 25.15 widzimy now wersj klasy ClickReceiver; reszta kodu pozostaje
bez zmian. Zwrmy uwag, e ta aplikacja bdzie dziaaa na urzdzeniach wyposaonych
przynajmniej w wersj 2.2 Androida, co zostanie wyjanione po zaprezentowaniu listingu.
android.content.Context;
android.content.Intent;
android.net.Uri;
android.util.FloatMath;
android.util.Log;
android.view.MotionEvent;
com.google.android.maps.GeoPoint;
com.google.android.maps.MapView;
com.google.android.maps.Overlay;
893
Do nakadki ClickReceiver dodalimy metod zwrotn onTouchEvent(). Wewntrz tej metody uzyskujemy kady obiekt MotionEvent, ktry jest kierowany z ekranu dotykowego do
widoku MapView. W wikszoci przypadkw po prostu przekazujemy je dalej. W ten sposb
895
W czasie testowania tej aplikacji bdziemy mogli w dalszym cigu przesuwa map w rne
strony oraz za pomoc stuknicia uruchamia tryb StreetView, jednak teraz gesty ciskania
i rozcigania pozwol nam na zwikszanie lub zmniejszanie skali mapy.
Zignorowalimy na moment moliwo dotykania ekranu trzema palcami, a nastpnie oderwania jednego z nich, dziki czemu pozostayby dwa aktywne palce. Wiele urzdze rozpoznaje maksymalnie tylko dwa jednoczenie przyoone palce do ekranu, powinnimy si jednak
spodziewa, e coraz wicej urzdze bdzie rozpoznawao wiksz liczb palcw. Jeeli chcemy
wykorzystywa gest ciskania w aplikacji, ktra nie obsuguje map, bdziemy musieli wasnorcznie zdefiniowa tryb powikszania danych obiektw. Jeeli na przykad na ekranie wywietlamy obraz, ktry chcemy powiksza i zmniejsza w reakcji na gesty rozcigania i ciskania, bdziemy musieli odpowiednio nim manipulowa po wystpieniu dziaania ACTION_MOVE,
tak jak to zostao wczeniej zaprezentowane. Niedugo zajmiemy si omwieniem podobnego
przykadu.
Wspomnielimy wczeniej, e gest ciskania nie by jawnie obsugiwany a do pojawienia si
wersji 2.2 Androida, a chocia omawiany kod dziaa rwnie w tej wersji, warto wykorzysta
nowe funkcje, pozwalajce na atwiejsze wprowadzenie tego gestu do aplikacji. Warto zauway,
e klasa MapView, poczwszy od wersji 2.2 Androida, posiada standardowo wbudowan obsug gestu ciskania; dziaa to bez adnej dodatkowej obsugi, wic nie musimy odnosi si do
adnego kodu odpowiedzialnego za ten gest w nowszych wersjach systemu. Zanim przyjrzymy
si natywnej obsudze gestu ciskania, musimy najpierw omwi klas, ktra jest dostpna
od samego pocztku GestureDetector.
android.app.Activity;
android.graphics.Matrix;
android.os.Bundle;
android.util.Log;
android.view.MotionEvent;
android.view.ScaleGestureDetector;
android.widget.ImageView;
897
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.v(TAG, "w metodzie onTouchEvent");
Ukad graficzny jest bardzo prosty. Zawarta w nim zostaa kontrolka TextView wywietlajca
komunikat o wykorzystaniu gestu ciskania oraz widok ImageView ze standardow ikon Androida. Wykonujc gest ciskania, bdziemy zmienia rozmiar tej ikony. Oczywicie, w jej miejsce moemy wstawi dowolny obraz. Wystarczy umieci odpowiedni plik obrazu w folderze
drawable oraz zmieni atrybut android:src w pliku ukadu graficznego. Zwrmy uwag na
atrybut android:scaleType naszego obrazu. Za jego pomoc informujemy system, e do przeprowadzania operacji skalowania obrazu bdziemy uywa macierzy grafiki. Chocia suy
ona rwnie do przesuwania obrazu, skupimy si teraz wycznie na procesie skalowania.
Zauwamy take, e zdefiniowalimy najwikszy dopuszczalny widok ImageView. Nie
chcemy, aby w trakcie skalowania obrazu by on obcinany przez granice widoku ImageView.
Rwnie sam kod nie jest skomplikowany. Wewntrz metody onCreate() uzyskujemy odniesienie do obrazu i tworzymy klas ScaleGestureDetector. Jedynymi czynnociami w metodzie
zwrotnej onTouchEvent() s odbieranie wszelkich obiektw zdarze oraz przekazywanie wartoci true w celu cigego otrzymywania nowych zdarze. W ten sposb klasa ScaleGesture
Detector obserwuje wszystkie zdarzenia i moe zadecydowa, kiedy poinformowa system
o wykryciu gestu.
W obiekcie ScaleListener jest przeprowadzany proces skalowania. W klasie obiektu nasuchujcego znajdziemy trzy metody zwrotne: onScaleBegin(), onScale() oraz onScaleEnd().
Pierwsza i trzecia metoda nie s nam potrzebne, wic ich nie implementujemy.
Niestandardowe gesty
W ostatniej czci tego rozdziau przyjrzymy si formalnym klasom typu Gesture. Zgodnie
z definicj gestem nazywamy uprzednio zarejestrowany ruch po ekranie dotykowym, ktrego
aplikacja oczekuje od uytkownika. Jeeli uytkownik wykona taki gest w aplikacji, zacznie ona
wykonywa operacje zdefiniowane dla tego ruchu. Potrzebne s nakadki wykrywajce dany ruch,
ktre przekazuj informacje o wykryciu ruchu do gwnej aktywnoci. Stosowanie gestw pozwala uproci interfejs uytkownika, gdy przyciski oraz inne kontrolki staj si niepotrzebne
i s wypierane przez szybkie ruchy palcami lub inne gesty. Mog one stanowi rwnie interesujce interfejsy gier. W tym punkcie pokaemy, w jaki sposb mona rejestrowa wasne gesty
i programowa ich obsug w aplikacji. Zwrmy uwag, e wszelkie klasy zwizane z gestami
nie s w ogle wykorzystywane w tym przykadzie; prezentujemy tutaj zupenie inny zestaw klas.
899
Zwrmy uwag, e moemy narysowa rne rodzaje gestw zaznaczania i wszystkie nazwa
checkmark. Zarejestrujmy przynajmniej jeszcze jeden taki gest i nazwijmy go rwnie
checkmark; powinien w jaki sposb rni si od pierwotnego gestu, chociaby rozmiarem.
Dodajmy rwnie za pomoc przycisku Add gesture inne gesty, nadajc im odmienne nazwy.
Kadorazowe wcinicie przycisku Done powoduje dodanie kolejnego gestu do biblioteki.
Chocia do utworzenia gestu nie moemy uywa funkcji wielodotykowoci, istnieje sposb
uwzgldnienia wielu gestw waciwych w obrbie jednego gestu. Aby na przykad zdefiniowa
gest oznaczajcy liter E, potrzebujemy co najmniej dwch gestw waciwych; jeden gest
waciwy moe definiowa grny, boczny i dolny odcinek litery, a drugi gest waciwy posuy do narysowania jej rodkowego odcinka. Moemy take najpierw narysowa za pomoc jednego gestu waciwego pionow lini w literze E, a nastpne trzy gesty waciwe
wykorzysta do narysowania trzech linii poziomych. Istniej rne metody narysowania litery E i na szczcie mamy moliwo zapisania ich wszystkich w bibliotece gestw. Zarejestrujmy gest odpowiedzialny za liter E na kilka rnych sposobw, poniewa uytkownicy
mog korzysta z odmiennych technik rysowania tej litery, a chcemy, eby aplikacja rozpoznaa ten gest bez wzgldu na sposb jego wykonania. Rysunek 25.7 przedstawia rne sposoby rejestrowania litery E.
Utworzenie gestu skadajcego si z wielu gestw waciwych moe stanowi nie lada wyzwanie
na emulatorze. Jak ju wspomnielimy, moemy ponownie narysowa nowy gest na wczeniejszym gecie, ktry zostanie usunity. Skd wic Android wie, kiedy rysujemy gest od nowa,
a kiedy dodajemy jedynie nowy gest waciwy do istniejcego gestu? Android stosuje w tym celu
901
warto atrybutu FadeOffset, ktra jest podawana w milisekundach. Jeli po jej przekroczeniu zaczniemy rysowa gest, Android uzna, e cay proces naley przeprowadzi od pocztku. Domylna warto tego atrybutu wynosi 420 milisekund. Oznacza to, e jeli podczas rysowania gestu uniesiemy palec na ponad 420 milisekund, system uzna, e skoczylimy
rysowanie tego gestu, i zostanie on zapamitany w takim stanie. W rzeczywistym urzdzeniu taki czas moe wystarczy do rozpoczcia rysowania kolejnego gestu waciwego. W przypadku emulatora moe nie by tak dobrze. Wszystko zaley od szybkoci stacji roboczej.
Jeeli aplikacja Gestures Builder stwarza problemy z akceptacj gestu skadajcego si z wielu
gestw waciwych, moemy utworzy wasn wersj tej aplikacji i zmodyfikowa domyln
warto atrybutu fadeOffset. Opisalimy wczeniej sposb utworzenia projektu Gestures Builder w rodowisku Eclipse. Postpujmy zgodnie z instrukcjami, a nastpnie przejdmy do pliku
/res/layout/create_gesture.xml i dodajmy atrybut android:fadeOffset="1000" do elementu
GestureOverlayView. Warto atrybutu fadeOffset zostanie powikszona do 1 sekundy
(1000 milisekund). Moemy jednak wstawi tu dowoln warto.
Poszukajmy miejsca, w ktrym s przechowywane gesty. Wiadomo typu Toast w aplikacji
Gestures Builder informuje nas, e gesty s zapisywane w katalogu /sdcard/gestures (lub
/mnt/sdcard/gestures od wersji 2.2 Androida). Skorzystajmy z perspektywy File Explorer w rodowisku Eclipse lub z powoki adb, aby odnale folder /sdcard na emulatorze. Znajdziemy
w nim plik gestures. Zwrmy uwag na jego niewielki rozmiar. Jest to plik binarny, zatem
nie ma moliwoci, aby go rcznie edytowa. Jeli chcemy modyfikowa jego zawarto, musimy otworzy aplikacj Gestures Builder. W trakcie tworzenia aplikacji obsugujcej gesty
musimy skopiowa plik gestures do jej katalogu /res/raw. Dokonujemy tego za pomoc
funkcji File Copy, znajdujcej si w perspektywie File Explorer, lub za pomoc polecenia adb
pull, co pozwala na skopiowanie pliku na dysk twardy, a stamtd do projektu.
Mamy nie tylko moliwo dodawania nowych gestw za pomoc aplikacji Gestures Builder
poprzez dugie kliknicie moemy wywoa menu istniejcego gestu. Dziki znajdujcym
si tu opcjom moemy zmieni nazw gestu lub go usun. Nie mona ponownie rejestrowa
zachowanego gestu, wic jeli nam si nie podoba, musimy go usun i utworzy nowy. Jak ju
import
import
import
import
import
import
import
import
import
import
import
java.util.ArrayList;
android.app.Activity;
android.gesture.Gesture;
android.gesture.GestureLibraries;
android.gesture.GestureLibrary;
android.gesture.GestureOverlayView;
android.gesture.Prediction;
android.gesture.GestureOverlayView.OnGesturePerformedListener;
android.os.Bundle;
android.util.Log;
android.widget.Toast;
//
903
W tym przykadzie bdziemy korzysta z tego samego pliku, ktry zosta zapisany przez aplikacj Gestures Builder. Stosujemy w tym celu metod GestureLibraries.fromFile() wewntrz
metody onCreate(). Pokazujemy jednak rwnie w komentarzach, w jaki sposb mona
uzyska dostp do pliku gestw stanowicego cz naszej aplikacji. Gdybymy wprowadzili
metod fromRawResource(), uylibymy argumentu przyjmujcego posta zwykego identyfikatora zasobw, natomiast plik gestw wstawilibymy do katalogu /res/raw.
Nasza aplikacja nie jest bardzo rozbudowana, lecz jej uruchomienie da nam wiksze pojcie
na temat zjawisk zachodzcych w systemie podczas przetwarzania gestw. W momencie uruchamiania aplikacja wczytuje plik gestw i zapisuje informacje o znalezionych gestach.
Wywietla take wyniki prb dopasowania wzorcowego gestu do gestu narysowanego na ekranie. Wczmy aplikacj wykrywajc gesty, przy oczywistym zaoeniu, e ju uruchomilimy
905
Odnoniki
Poniej prezentujemy odnoniki do materiaw, ktre pomog Czytelnikowi zrozumie zagadnienia omawiane w niniejszym rozdziale.
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu zbir projektw bezporednio
zwizanych z ksik. Prezentowane w tym rozdziale projekty zostay umieszczone
w katalogu ProAndroid3_R25_EkranyDotykowe. W katalogu znajduje si rwnie plik
Czytaj.TXT, zawierajcy instrukcj importowania projektw do rodowiska Eclipse.
http://www.ted.com/talks/jeff_han_demos_his_breakthrough_touchscreen.html Jeff
Han demonstruje swj interfejs uytkownika obsugujcy wielodotykowo na konferencji
TED w 2006 roku wietny materia.
http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
wpis dotyczcy wielodotykowoci, w ktrym ukazany jest inny sposb implementacji
klasy GestureDetector wewntrz rozszerzenia widoku.
Podsumowanie
W tym rozdziale zademonstrowalimy sposb obsugi ekranw dotykowych, poczwszy od
aplikacji rozpoznajcej dotykanie jednym palcem, nastpnie za zajlimy si kwesti wielodotykowoci. Wyjanilimy zasad dziaania funkcji obsugi dotyku w pracy z mapami, a take
omwilimy niektre pomocnicze klasy i metody uatwiajce przetwarzanie zdarzenia dotyku w aplikacji obsugujcej mapy. W ostatnim podrozdziale przeanalizowalimy mechanizm
gestw, umoliwiajcy wprowadzanie danych w nowy i prawdopodobnie prostszy sposb ni
za pomoc klawiatury i innych kontrolek interfejsu uytkownika.
R OZDZIA
26
Czujniki
Wykrywanie czujnikw
Nie powinnimy jednak zakada, e kade urzdzenie bdzie wyposaone we wszystkie wymienione rodzaje czujnikw. W rzeczywistoci wiele urzdze zostao wyposaonych tylko
w cz tych ukadw. Na przykad emulator Androida posiada wycznie akcelerometr. W jaki
sposb moemy wic dowiedzie si, ktre czujniki s dostpne w urzdzeniu? Istniej dwa
sposoby jeden bezporedni, drugi poredni.
Pierwszy sposb polega na zadaniu od klasy SensorManager listy dostpnych czujnikw.
W efekcie otrzymamy list obiektw, dla ktrych moemy ustanowi obiekty nasuchujce,
a nastpnie pobra z nich dane. Rozwizanie to zostanie omwione w dalszej czci rozdziau.
Zakadamy tutaj, e aplikacja zostaa ju zainstalowana w urzdzeniu; co jednak naley zrobi,
w przypadku gdy urzdzenie nie ma czujnika wymaganego przez nasz program?
W tym miejscu moemy wykorzysta drugie rozwizanie. Moemy zdefiniowa w pliku
AndroidManifest.xml funkcje urzdzenia niezbdne do dziaania aplikacji. Jeeli nasz program
wymaga obecnoci czujnika zblieniowego, moemy to okreli w pliku manifecie za pomoc
nastpujcego wiersza:
<uses-feature android:name="android.hardware.sensor.proximity" />
Rozdzia 26 Czujniki
909
java.util.HashMap;
java.util.List;
android.app.Activity;
android.hardware.Sensor;
android.hardware.SensorManager;
android.os.Bundle;
android.widget.TextView;
Rozdzia 26 Czujniki
911
android.app.Activity;
android.hardware.Sensor;
android.hardware.SensorEvent;
android.hardware.SensorEventListener;
android.hardware.SensorManager;
android.os.Bundle;
android.widget.TextView;
W tej przykadowej aplikacji ponownie uzyskujemy odniesienie do klasy SensorManager, jednak tym razem otrzymujemy informacje wycznie na temat czujnika owietlenia. Nastpnie
w metodzie onResume() naszej aktywnoci konfigurujemy obiekt nasuchujcy, a w metodzie
onPause() nastpuje jego wyrejestrowanie. Nie chcemy zajmowa si poziomami owietlenia,
gdy aplikacja nie znajduje si na pierwszym planie.
Rozdzia 26 Czujniki
913
Problemy pojawiajce si
podczas uzyskiwania danych z czujnikw
Architektura czujnikw w Androidzie wie si z pewnymi problemami, ktrych powinnimy
by wiadomi. Ten aspekt obsugi czujnikw wcale nie jest prosty. W pewnych przypadkach
problemy s bardzo atwe do rozwizania, w innych jest to niemoliwe lub bardzo trudne do
osignicia.
Technologia JNI (ang. Java Native Interface) stanowi mechanizm pozwalajcy na uruchamianie
kodu napisanego w jzyku natywnym (np. C/C++) wewntrz rodowiska Java przyp. tum.
Rozdzia 26 Czujniki
915
tego problemu. Przede wszystkim moemy skonfigurowa czas wyganicia wywietlacza, dziki
czemu nie zostanie on wyczony podczas otrzymywania aktualizacji odczytw. Zasadnicz
wad tego rozwizania jest wysoki koszt energetyczny, co wie si z szybszym rozadowywaniem baterii. Aby ustanowi czas wygaszania wywietlacza, musimy wprowadzi fragment
podobny do ukazanego poniej, gdzie myDelay oznacza czas wyraony w milisekundach:
Settings.System.putInt(getContentResolver(),
Settings.System.SCREEN_OFF_TIMEOUT, myDelay);
Wprowadzenie wartoci -1 sprawi, e ekran nigdy nie zostanie wyczony. Aplikacja bdzie wymagaa rwnie przydzielenia odpowiedniego uprawnienia (android.permission.WRITE_SETTINGS)
w pliku AndroidManifest.xml. Drug wad tego rozwizania jest to, e ustawienie czasu wygasania wywietlacza ma charakter globalny. Jeli jaka aplikacja zmieni t warto, Android
zmodyfikuje j wszdzie. W rzeczywistoci wic aplikacja powinna zapamita poprzedni warto tego ustawienia i przywrci j w momencie koczenia pracy. Jednak nawet po wdroeniu
takiego rozwizania moemy natrafi na problemy, poniewa uytkownik po uruchomieniu
aplikacji moe si zastanawia, dlaczego wywietlacz nie zostaje wyczony po pewnym czasie,
przej do panelu ustawie i wprowadzi zupenie inn warto wygaszania ekranu, nastpnie
powrci do aplikacji i dopiero wtedy j zamkn. Nie musimy wspomina, e po takich modyfikacjach ustawie ekran moe zosta wyczony w czasie pracy aplikacji, w efekcie czego
przestanie ona otrzymywa odczyty czujnika.
Technika wyrejestrowania i zarejestrowania
dotyczca cigych aktualizacji odczytw czujnika
Jedna z metod zapewnienia aplikacji otrzymywania nieprzerwanego strumienia odczytw polega
na zarejestrowaniu odbiorcy powiadomie zwizanych z wyczaniem ekranu, nastpnie wyrejestrowaniu obiektu nasuchujcego zdarzenia czujnika i jego ponownym zarejestrowaniu
w metodzie onReceive() klasy BroadcastReceiver. Rozwizanie to okazywao si skuteczne
w niektrych urzdzeniach pracujcych pod kontrol Androida w wersji 2.1, ale nie we wszystkich.
Poniewa nasza aplikacja w normalnych warunkach zostaje zatrzymana w momencie wyczenia
wywietlacza, najpierw musimy ustanowi czciow blokad przechodzenia urzdzenia w stan
wstrzymania (ang. wake lock), aby program pozostawa aktywny po wygaszeniu ekranu. W naszym przykadzie wykorzystujemy aktywno, jednak w rzeczywistej aplikacji najprawdopodobniej umiecilibymy kod obiektu nasuchujcego wewntrz usugi. Na listingu 26.3 pokazalimy przykadow implementacj tego pomysu w postaci aktywnoci.
Listing 26.3. Rozwizywanie problemw za pomoc wyczania obiektw SensorListener
package com.androidbook.sensor.accel;
java.io.BufferedWriter;
java.io.FileWriter;
java.io.IOException;
java.text.SimpleDateFormat;
java.util.Date;
android.app.Activity;
android.content.BroadcastReceiver;
android.content.Context;
android.content.Intent;
android.content.IntentFilter;
android.hardware.Sensor;
android.hardware.SensorEvent;
android.hardware.SensorEventListener;
android.hardware.SensorManager;
android.os.Bundle;
android.os.Environment;
android.os.PowerManager;
android.os.PowerManager.WakeLock;
android.provider.Settings;
android.util.Log;
Rozdzia 26 Czujniki
}
catch(Exception e) {
mSavedTimeout = 120000;
}
Settings.System.putInt(getContentResolver(),
Settings.System.SCREEN_OFF_TIMEOUT, 5000);
// 5 sekund
}
public BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
writeLog("Ekran zostal wylaczony");
917
// Ignoruje
}
public void onSensorChanged(SensorEvent event) {
writeLog("Uzyskano zdarzenie czujnika: " + event.values[0] + ", " +
event.values[1] + ", " + event.values[2]);
}
private void writeLog(String str) {
try {
Date now = new Date();
mLog.write(mTimeFormat.format(now));
mLog.write(str);
mLog.write("\n");
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
Nie musimy si przejmowa ukadem graficznym, gdy ta przykadowa aplikacja bdzie wywietlaa jedynie swoj nazw. Nie interesuj nas rwnie uprawnienia, zatem na listingu
26.4 prezentujemy plik AndroidManifest.xml.
Listing 26.4. Plik AndroidManifest.xml aplikacji Monitor akcelerometru
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0" package="com.androidbook.sensor.accel">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Rozdzia 26 Czujniki
919
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>
Gwnym zadaniem tego przykadowego kodu jest zapisywanie zdarze akcelerometru w pliku
dziennika. Musimy umieci czciow blokad przechodzenia urzdzenia w stan upienia
wewntrz metody onCreate(), dziki czemu aplikacja nie bdzie wstrzymywana w momencie
wyczenia ekranu (blokady przechodzenia urzdzenia w stan upienia zostay omwione w rozdziale 14.). Ustanawiamy rwnie czas wyganicia ekranu na 5 sekund, czyli wywietlacz zostanie wyczony do szybko, jednak jego poprzednia warto zostaje zapamitana i bdzie
przywrcona w momencie wywoania metody onDestroy().
Modyfikujemy czas wyganicia ekranu jedynie w celu sprawdzenia, co si stanie
z obiektem nasuchujcym zdarzenia czujnika. W rzeczywistej aplikacji uytkowej
nie stosowalibymy tego rozwizania.
Konfigurujemy rwnie odbiorc BroadcastReceiver, ktry zostaje powiadomiony o wyczeniu wywietlacza. Zapisujemy t informacj w metodzie onReceive(), a nastpnie wprowadzamy omawiane obejcie dla wersji 2.1 Androida, pozwalajce na dalsze otrzymywanie aktualizacji odczytw z czujnika. W metodzie onStart() zostaje zarejestrowany obiekt nasuchujcy
zdarzenia czujnika, a take odbiorca komunikatw. Obydwa te obiekty zostaj wyrejestrowane w metodzie onStop(). Korzystamy z metod onStart() i onStop() zamiast onResume()
i onPause(), poniewa chcemy nasuchiwa zdarzenia czujnika nawet po przejciu uytkownika do innej aktywnoci w trakcie dziaania programu.
Metoda onDestroy() zapewni oczyszczenie, oprnienie i zamknicie pliku dziennika. W przeciwiestwie do poprzedniego przykadu, metoda onAccuracyChanged() nie wykonuje adnej
czynnoci. Dane zdarze s zapisywane do pliku dziennika w metodzie onSensorChanged().
Taki wzr pracy z obiektem nasuchujcym zdarzenia czujnika powinnimy stosowa rwnie
w zwykej, uytkowej aplikacji. Inaczej ni w przypadku poprzedniego przykadu, gdzie nie przejmowalimy si stanem wstrzymania urzdzenia, najprawdopodobniej bdziemy musieli wprowadzi blokad przechodzenia urzdzenia w stan wstrzymania, gwarantujc pobieranie zdarze przez aplikacj nawet po wyczeniu ekranu. Pamitajmy, e jeeli naszym systemem
docelowym bdzie Android 2.2 lub nowszy, nie powinnimy si przejmowa procesami rejestrowania i wyrejestrowywania obiektu nasuchujcego w odbiorcy BroadcastReceiver. Rwnie Android w wersji 2.0 i starszych nie powinien sprawia problemw.
Aplikacja, ktr pokazalimy, jest bardzo ciekawa. Proponujemy, aby przetestowa j w nastpujcy sposb:
1. Zainstaluj aplikacj, nastpnie odcz urzdzenie od stacji roboczej, aby nie byo ono
poczone z kablem USB (czasami z tego powodu ekran pozostaje cay czas wczony
pomimo wprowadzonych ustawie).
Kiedy aplikacja si uruchomi, moesz porusza w przestrzeni urzdzeniem, a po piciu
sekundach wywietlacz zostanie wyczony.
Rozdzia 26 Czujniki
921
mTurnBackOn.acquire();
}});
}
}
};
Jeeli teraz po uruchomieniu aplikacji wciniemy przycisk zasilania urzdzenia w celu wyczenia ekranu, wykryje ona to zdarzenie i za pomoc blokady przechodzenia w stan upienia ustawi
wywietlacz w trybie oszczdzania energii. W trakcie przeprowadzania tej czynnoci moe si
pojawi krtka przerwa pomidzy kolejnymi odczytami czujnika, jednak jest to lepsze rozwizanie od braku aktualizacji odczytw w czasie, gdy wywietlacz pozostaje wyczony. Zwrmy
uwag, e wykorzystalimy procedur obsugi do wystawienia obiektu Runnable z poziomu odbiorcy komunikatw. Dziki temu nasz kod bdzie przetwarzany w gwnym wtku, co staje si
istotne w momencie zwalniania blokady przechodzenia urzdzenia w stan upienia w metodzie
onDestroy(). Zakadanie i zwalnianie blokady musz by przeprowadzane w tym samym wtku.
Zauwamy rwnie, e w metodzie onReceive() odbiorcy komunikatw zwalniamy blokad
przechodzenia urzdzenia w stan upienia, zanim pojawi si kolejna. Dokonujemy tego na wypadek kilkukrotnego wcinicia przycisku zasilania w trakcie rejestrowania odczytw czujnika.
Liczba zwalnianych blokad musi odpowiada liczbie zakadanych blokad, wic zwalniamy t
jedn jeszcze przed wystpieniem nastpnej.
Skoro ju wyjanilimy, w jaki sposb naley pobiera dane z czujnikw, naley powiedzie, co
moemy z nimi zrobi. Jak ju wczeniej stwierdzilimy, w zalenoci od rodzaju czujnika wartoci
przekazywane w tablicy posiadaj rnorodne znaczenie. W nastpnym podrozdziale zajmiemy si
poszczeglnymi typami czujnikw oraz znaczeniem generowanych przez nie odczytw.
Czujniki owietlenia
Czujnik owietlenia stanowi jeden z najprostszych rodzajw tego typu ukadw elektronicznych pokazalimy, jak go wykorzysta, w pierwszej przykadowej aplikacji omwionej w tym
rozdziale. Czujnik generuje odczyty zwizane z wykrywanym poziomem natenia wiata.
Wraz ze zmian natenia poziomu wiata czujnik aktualizuje rwnie swoje odczyty. Dane s
wyraane w luksach luks jest jednostk natenia wiata w ukadzie SI. Aby pozna dokadn
Czujniki zblieniowe
Czujnik zblieniowy mierzy odlego dzielc dany obiekt od urzdzenia (w centymetrach) lub
definiuje flag okrelajc, czy dany obiekt jest blisko, czy daleko. Niektre czujniki zblieniowe
posiadaj zakres wartoci od 0 do maksimum wraz z wartociami porednimi, inne natomiast
definiuj wycznie wartoci minimaln i maksymaln. Jeeli maksymalny zakres czujnika jest
rwny jego rozdzielczoci, to znaczy, e odczytuje on wycznie kracowe wartoci. Maksymalna
warto pewnych czujnikw wynosi 1.0, a innych 6.0. Niestety, dopki nie zainstalujemy
i nie uruchomimy aplikacji, nie dowiemy si, jaki rodzaj czujnika zosta umieszczony w urzdzeniu. Nawet jeli umiecimy znacznik <uses-feature> w pliku AndroidManifest.xml, niewiele
nam to pomoe. Nasza aplikacja powinna w elegancki sposb obsugiwa obydwa typy czujnikw zblieniowych, chyba e dokadniejsza wiedza o typie czujnika okae si niezbdna.
Warto pozna interesujcy szczeg dotyczcy czujnikw zblieniowych: czasami czujnik ten
jest czci ukadu elektronicznego wsplnego z czujnikiem owietlenia. Android jednak traktuje je jako logicznie oddzielne czujniki, jeli wic bdziemy potrzebowa danych z obydwu rodzajw czujnikw, musimy utworzy dla nich osobne obiekty nasuchujce. Istnieje jeszcze jeden
ciekawy fakt: w aplikacjach telefonicznych czujnik zblieniowy czsto jest stosowany do okrelania odlegoci gowy uytkownika od urzdzenia. Jeeli gowa znajdzie si wystarczajco blisko
ekranu dotykowego, zostaje on zablokowany, aby aden przycisk nie zosta przypadkowo wcinity przez ucho lub policzek podczas rozmowy.
Wrd kodw rdowych utworzonych na potrzeby tego rozdziau znajdziemy prost aplikacj
monitorujc czujnik zblieniowy, ktra w rzeczywistoci jest zmodyfikowan wersj monitora
czujnika owietlenia. W samej ksice nie umiecimy tego kodu, jednak nic nie stoi na przeszkodzie, eby samodzielnie go pobra i z nim poeksperymentowa.
Termometry
Termometr przekazuje wyniki odczytu temperatury i rwnie s to pojedyncze elementy w tablicy
values[0]. Wartoci s reprezentowane w stopniach Celsjusza. Wartoci w skali Fahrenheita
uzyskamy, mnoc warto wyraon w stopniach Celsjusza przez 9/5 i dodajc do wyniku 32.
Rozdzia 26 Czujniki
923
Na przykad 0 stopni Celsjusza (punkt zamarzania wody) w skali Fahrenheita przyjmuje warto
32, natomiast 100 stopni Celsjusza (punkt wrzenia wody) to 212 stopni Fahrenheita.
W zalenoci od urzdzenia termometr moe zosta umieszczony w rnych miejscach, istnieje
wic moliwo, e wynik mierzonej temperatury zakca ciepo generowane przez telefon.
Przykadowo odczyty temperatury w pewnych urzdzeniach s zakcane przez ciepo powstajce podczas pracy baterii. Powinnimy o tym pamita podczas pisania aplikacji wykorzystujcych termometry. Nie naley oczekiwa, e termometr wbudowany w telefon bdzie mierzy
wycznie temperatur powietrza otaczajcego aparat.
Wrd projektw utworzonych na potrzeby tego rozdziau Czytelnik znajdzie jeden ukazujcy
sposb korzystania z termometru, zatytuowany TemperatureSensor.
Czujniki cinienia
Co ciekawe, w czasie, gdy pisalimy niniejsz ksik, ten rodzaj czujnikw nie zosta jeszcze
umieszczony w adnym urzdzeniu. Uwaamy jednak, e kolejne generacje urzdze mog zosta wyposaone w barometryczne czujniki cinienia, pozwalajce na przykad na pomiar wysokoci. Nie naley myli tego czujnika z funkcj ekranu dotykowego, ktry reaguje na nacisk
palca, moe okreli jego si i generuje obiekt MotionEvent. Wykrywanie tego rodzaju oddziaywa mechanicznych zostao omwione w rozdziale 25., a suca do tego architektura nie
jest czci omawianej w tym rozdziale struktury czujnikw.
Chocia utworzenie aplikacji obsugujcych czujniki cinienia przez proste skopiowanie i zmodyfikowanie zaprezentowanych do tej pory aplikacji monitorujcych nie byoby trudnym zadaniem, to jednak bez wiedzy, jakie jednostki bd stosowane w przypadku tych czujnikw, napisanie takiej aplikacji nie zdaoby si na wiele. Najwidoczniej programici z firmy Google planuj
z wyprzedzeniem.
yroskopy
yroskopy stanowi bardzo ciekaw kategori urzdze, mierzc skrt urzdzenia w paszczynie odniesienia. Inaczej mwic, za ich pomoc mierzymy prdko obrotu telefonu w danej osi. Jeli urzdzenie nie bdzie obracane, wartoci odczytywane przez czujnik bd wynosi 0.
W momencie obrotu smartfonu w dowolnym kierunku pojawi si niezerowe odczyty. Sam yroskop nie poda nam wszystkich wymaganych danych. Niestety, podczas pracy z yroskopami
zawsze wkradn si jakie bdy. Jednak w sprzeniu z akcelerometrami moemy okreli
ciek ruchu urzdzenia. Do powizania odczytw pochodzcych z obydwu czujnikw mog
suy filtry Kalmana. Akcelerometry nie s zbyt dokadne w krtszych odcinkach czasowych,
z kolei yroskopy trac j wraz z upywem czasu, wic ich powizanie ze sob moe nam zagwarantowa cakiem niez dokadno przez cay czas. Filtry Kalmana s bardzo skomplikowane, ale istnieje alternatywa zwana filtrami komplementarnymi, ktre s atwiejsze do implementacji i generuj cakiem poprawne wyniki. Wspomniane tu koncepcje wykraczaj poza
zakres ksiki.
yroskop przekazuje trzy wartoci w tablicy wartoci, opisujce kolejno punkty na osiach x,
y i z. Jednostk przekazywanych wartoci s radiany na sekund, reprezentuj one szybko
obrotu urzdzenia wok danej osi. Jednym ze sposobw ich wykorzystania jest ich cakowanie
po czasie w celu obliczenia zmiany kta. W podobny sposb jest cakowana warto prdkoci
liniowej po czasie w celu obliczenia odlegoci.
Akcelerometry
Akcelerometry stanowi chyba najciekawsze z obecnie dostpnych rodzajw czujnikw. Za
ich pomoc aplikacja moe okreli fizyczne uoenie urzdzenia w zalenoci od siy cienia,
a dodatkowo wykrywa siy przesuwajce to urzdzenie. Dziki takim informacjom programici zyskuj niespotykane dotd moliwoci, poczwszy od nowej jakoci sterowania w grach,
a skoczywszy na pracy w rzeczywistoci rozszerzonej. Oczywicie, podstawowym zadaniem
akcelerometru jest przekazanie do urzdzenia informacji o zmianie jego uoenia z orientacji
pionowej na poziom, i odwrotnie.
System wsprzdnych w akcelerometrze dziaa nastpujco: o x czujnika ma swj pocztek
w lewym dolnym rogu urzdzenia i jest skierowana w praw stron (patrzc od przodu urzdzenia). O y rwnie ma pocztek w lewym dolnym rogu urzdzenia i jest skierowana w gr
telefonu. Take punkt 0 osi z znajduje si w lewym dolnym rogu urzdzenia i jest skierowany
na zewntrz, w taki sposb, e oddala si od urzdzenia. Zostao to zobrazowane na rysunku 26.2.
System wsprzdnych rni si od wykorzystywanego w ukadach graficznych i grafice dwuwymiarowej. W przypadku tamtych ukadw wsprzdnych ich pocztek (0, 0) znajduje si
w lewym grnym rogu ekranu, a wartoci dodatnie osi y rosn w kierunku dolnym. atwo si
pomyli podczas pracy z systemami wsprzdnych w rnych ukadach odniesienia, naley
wic zachowa ostrono.
Jeszcze nic nie wspomnielimy o znaczeniu wartoci przekazywanych przez akcelerometr, a wic
co one oznaczaj? Przypieszenie jest mierzone w metrach na sekund do kwadratu (m/s2).
Przypieszenie powodowane ziemsk grawitacj wynosi 9,81 m/s2 i jest skierowane w d,
w stron rodka planety. Z punktu widzenia akcelerometru warto siy cienia wynosi 9,81.
Jeeli urzdzenie znajduje si w stanie spoczynku (nie porusza si) i jest uoone na doskonale
paskiej, poziomej powierzchni, odczyty na osiach x i y przyjm wartoci 0, natomiast w osi
z +9,81. W rzeczywistoci, zalenie od czuoci i dokadnoci akcelerometru, wartoci te nie
bd doskonale odwzorowywa faktycznego stanu rzeczy, jednak bd stanowi wystarczajco
dobre przyblienie. W stanie spoczynku jedynie grawitacja bdzie wpywa na urzdzenie, a poniewa jej wektor jest skierowany w d (nasze urzdzenie za ley pasko), nie bdzie miaa
wpywu na osie x i y. W przypadku osi z bdzie mierzona warto siy dziaajcej na urzdzenie
Rozdzia 26 Czujniki
925
oraz zostanie odjta warto siy cienia, wic 0 minus 9,81 daje nam ostatecznie warto +9,81
i tyle wanie wynosi warto siy przyoonej do tej osi (element values[2] w obiekcie
SensorEvent).
Wartoci przesyane do aplikacji przez akcelerometr zawsze stanowi sum si dziaajcych na
urzdzenie minus warto przypieszenia ziemskiego. Gdybymy unieli w gr nasze uoone
doskonale pasko urzdzenie, pocztkowo warto mierzona dla osi z wzrosaby, poniewa zwikszylibymy oddziaywanie wbrew sile grawitacji. Gdy tylko przestaniemy unosi urzdzenie,
warto sumaryczna dziaajcych si powrci do wartoci grawitacji. Gdyby urzdzenie zostao
upuszczone (czysto hipotetycznie nie sprawdzajmy tego), zaczoby uzyskiwa przypieszenie
w kierunku ziemi, a tym samym warto odczytu w nastpnych momentach zmalaaby do zera.
Wyobramy sobie, e urzdzenie z rysunku 26.2 obrcimy w taki sposb, aby byo uoone
w trybie portretowym, pionowo. O x pozostaje bez zmian i wskazuje z lewej strony na praw.
Z kolei o y zostaje ustawiona prostopadle do ziemi, a o z zostaje skierowana w nasz stron.
Warto osi y wynosi teraz +9,81, a osi x i z po 0.
Co si stanie, jeli obrcimy urzdzenie do uoenia poziomego, w trybie krajobrazowym,
i w dalszym cigu bdziemy trzyma je pionowo, tj. ekran bdzie si znajdowa na wprost twarzy?
Nietrudno zgadn, e osie y i z przybior wartoci 0, a w osi x bdzie dziaa sia rwna +9,81.
Taka sytuacja zostaa zilustrowana na rysunku 26.3.
Gdy urzdzenie znajduje si w stanie spoczynku lub porusza si z jednostajn prdkoci, akcelerometry mierz wycznie warto grawitacji. Dla kadej osi odczyty akcelerometru stanowi skadowe grawitacje w tej osi. Zatem za pomoc oblicze trygonometrycznych moemy
okreli kty oraz uoenie urzdzenia wzgldem kierunku dziaania siy cienia. Oznacza
to, e moemy si dowiedzie, czy urzdzenie jest uoone w trybie portretowym, krajobrazowym, czy jakim porednim. W rzeczywistoci system korzysta wanie z tego rozwizania
w czasie rozpoznawania orientacji uoenia (tryb krajobrazowy lub portretowy). Zwrmy jednak uwag, e akcelerometry nie okrelaj uoenia urzdzenia w odniesieniu do pnocy
magnetycznej. Do tego wanie suy magnetometr, ktry zostanie omwiony w dalszej czci rozdziau.
Rotation()
Akcelerometry i grawitacja
Do tej pory zajmowalimy si bardzo pobienie kwesti zachowania odczytw akcelerometru w trakcie przemieszczania si urzdzenia. Zastanwmy si teraz nad tym dokadniej.
Rozdzia 26 Czujniki
927
Wszystkie siy dziaajce na urzdzenie zostan wykryte przez akcelerometry. Jeeli uniesiemy
telefon, sia dziaajca w osi z bdzie pocztkowo dodatnia, a jej warto bdzie wiksza ni +9,81.
Jeeli przesuniemy urzdzenie w lewo, pocztkowa warto wektora siy w osi x bdzie ujemna.
Zaleaoby nam teraz na oddzieleniu wartoci wynikajcych z oddziaywania grawitacyjnego
od pozostaych si dziaajcych na urzdzenie. Rozwizanie jest cakiem proste i nosi nazw filtru
dolnoprzepustowego. Siy niebdce oddziaywaniem grawitacyjnym zazwyczaj niestopniowo
wpywaj na urzdzenie. Inaczej mwic, jeeli uytkownik potrzsa urzdzeniem, pojawiajce
si siy s bardzo szybko rejestrowane przez akcelerometry. W zwizku z tym filtr dolnoprzepustowy usunie skadow odpowiedzialn za same wstrzsy i pozostawi wycznie niezmienn
skadow, w tym przypadku przypieszenie ziemskie. Zilustrujmy t koncepcj na przykadzie.
Interesujcy nas projekt nosi nazw GravityDemo. Na listingu 26.6 zosta umieszczony ukad
graficzny oraz kod Java.
Listing 26.6. Pomiar grawitacji za pomoc akcelerometrw
<?xml version="1.0" encoding="utf-8"?>
android.app.Activity;
android.hardware.Sensor;
android.hardware.SensorEvent;
android.hardware.SensorEventListener;
android.hardware.SensorManager;
android.os.Bundle;
android.widget.TextView;
// Ignorujemy.
}
public void onSensorChanged(SensorEvent event) {
// Zmienn ratio jest stosunek grawitacji przyoonej w osi Y do standardowej wartoci grawitacji.
// Warto ta powinna mieci si w zakresie pomidzy 1 i 1.
ratio = gravity[1]/SensorManager.GRAVITY_EARTH;
if(ratio > 1.0) ratio = 1.0;
if(ratio < -1.0) ratio = -1.0;
Rozdzia 26 Czujniki
929
text.invalidate();
counter=1;
}
}
}
Aplikacja ta w wikszoci przypomina wczeniejszy przykadowy program Monitor akcelerometru. Rnice pojawiaj si w metodzie onSensorChanged(). Zamiast standardowego wywietlania otrzymywanych wartoci z tablicy prbujemy pozna skadowe grawitacji i ruchu.
Skadow grawitacji uzyskujemy poprzez zsumowanie aktualnej i poprzedzajcej wartoci
z tablicy grawitacji, przemnoonych przez wspczynniki. Wspczynniki te musz po zsumowaniu da warto 1.0, a dobiera si je tak, e aktualn warto z tablicy wartoci grawitacji
mnoy si przez mniejszy wspczynnik, a poprzedni przez wikszy (byle suma tych wspczynnikw bya rwna wartoci 1.0). W naszym przykadzie wykorzystalimy wspczynniki
0,9 i 0,1. Moemy rwnie wyprbowywa inne wartoci wspczynnikw, na przykad 0,8 i 0,2.
Tablica wartoci grawitacji prawdopodobnie nie bdzie zmieniaa si tak szybko jak rzeczywiste
odczyty czujnika. W ten sposb jednak zbliamy si do rzeczywistych wartoci. Do tego wanie suy filtr dolnoprzepustowy. Wartoci tabeli zdarze ulegaj zmianom jedynie w przypadku si poruszajcych urzdzeniem, a my nie chcemy, aby byy one mierzone jako cz oddziaywania grawitacyjnego. W tabeli wartoci grawitacji chcemy jedynie zarejestrowa faktyczne
odczyty siy cienia. Zastosowane tutaj obliczenia matematyczne nie sprawiaj, e w magiczny
sposb jest rejestrowane wycznie przypieszenie grawitacyjne, ale uzyskiwane wartoci bd
znacznie blisze rzeczywistoci ni nieprzetworzone dane.
Magnetometry
Magnetometr mierzy indukcj pola magnetycznego w otoczeniu i okrela jej rozkad w osiach
x, y i z. Ukad wsprzdnych jest taki sam jak w przypadku akcelerometrw, wic moemy
zastosowa tu taki, jaki wida na rysunku 26.2. Jednostkami stosowanymi w magnetometrach
s mikrotesle (T). Czujnik ten wykrywa ziemskie pole magnetyczne, dlatego te pozwala nam
Rozdzia 26 Czujniki
931
okrela kierunek pnocny. Magnetometr jest czsto nazywany rwnie kompasem, nawet
w znaczniku <uses-feature> jest stosowana nazwa android.hardware.sensor.compass.
Magnetometr jest bardzo niewielkim i czuym urzdzeniem, dlatego na jego odczyty wpywa
pole magnetyczne generowane przez inne urzdzenia znajdujce si w pobliu, a w pewnym
stopniu nawet ukady znajdujce si w samym telefonie. Zatem wskazania magnetometru
mog by czasami niewiarygodne.
Wrd projektw przygotowanych specjalnie na potrzeby tego rozdziau umiecilimy prost
aplikacj CompassSensor, warto wic zaimportowa j i troch z ni poeksperymentowa. Jeeli
zbliymy metalowe przedmioty do urzdzenia w trakcie dziaania aplikacji, moemy zauway
zmian odczytw. Oczywicie, jeeli blisko czujnika ustawimy magnes, odczyty na pewno
ulegn zmianie, nie radzimy jednak tego robi, gdy magnetometr moe zosta rozkalibrowany.
Moemy si zastanawia, czy istnieje moliwo wykorzystania magnetometru jako kompasu
do wskazywania kierunku pnocnego. Odpowied brzmi: niebezporednio. Chocia magnetometr wykrywa strumienie magnetyczne otaczajce urzdzenie, jeeli urzdzenie nie bdzie
uoone doskonale poziomo, jego odczyty jako kompasu bd niemiarodajne. Mamy jednak do
dyspozycji akcelerometry, ktre informuj nas o uoeniu urzdzenia wzgldem osi stanowicej
kierunek dziaania siy grawitacji! Moemy wic wykorzystywa magnetometr jako kompas,
bdzie nam jednak do tego potrzebna pomoc akcelerometrw. Zobaczmy, jak si to robi.
Niedawno stwierdzilimy, e stosowanie preferowanego mechanizmu obliczania orientacji urzdzenia w przestrzeni jest trudnym zadaniem. W nastpnym przykadzie porwnamy wartoci
orientacji uzyskiwane z preferowanego rozwizania z odczytami czujnika orientacji i przyjrzymy
si rnicom.
Urozmaicimy nieco przykadow aplikacj. Moglibymy po prostu wywietli wartoci przekazywane przez czujniki, ale moemy je jeszcze wykorzysta w interesujcy sposb. Wyobramy
sobie, e stoimy na ulicy w Jacksonville na Florydzie. Nasza aplikacja bdzie nam pokazywaa
zdjcia w trybie StreetView tego miasta, tak jakbymy tam byli, a czujnik orientacji posuy
nam do okrelenia kierunku, w jakim spogldamy. Wraz ze zmian kierunku orientacji telefonu bd si odpowiednio zmieniay widoki w trybie StreetView. Na listingu 26.7 widzimy
ukad graficzny i kod Java naszej przykadowej aplikacji, nazwanej VirtualJax.
Listing 26.7. Uzyskiwanie informacji o pooeniu za pomoc czujnikw
<?xml version="1.0" encoding="utf-8"?>
Rozdzia 26 Czujniki
import
import
import
import
import
import
import
import
import
android.hardware.SensorEvent;
android.hardware.SensorEventListener;
android.hardware.SensorManager;
android.net.Uri;
android.os.Build;
android.os.Bundle;
android.view.View;
android.view.WindowManager;
android.widget.TextView;
933
// Ignorujemy
}
public void onSensorChanged(SensorEvent event) {
Rozdzia 26 Czujniki
935
// google.streetview:cbll=30.32454,81.6584&cbp=1,yaw,,pitch,1.0
// yaw = warto w stopniach, zgodnie ze wskazwkami zegara od bieguna pnocnego
// W przypadku odchylenia (ang. yaw) moemy wykorzysta wartoci mAzimuth
// lub orientationValues[0].
//
// pitch = warto w stopniach, przechy w gr lub d. 90 oznacza spogldanie w gr,
// +90 to spogldanie w d,
// nie biorc pod uwag faktu, e przechy wzduny (ang. pitch) nie jest poprawnie obliczany.
Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse(
"google.streetview:cbll=30.32454,-81.6584&cbp=1," +
Math.round(orientationValues[0]) + ",,0,1.0"
));
startActivity(intent);
return;
}
}
Zanim przyjrzymy si wynikom, wyjanijmy, jakie jest zadanie tej aplikacji. W metodzie
onCreate() przeprowadzamy takie same czynnoci jak w poprzednich przykadach: tworzymy
odniesienia do widokw tekstowych, klasy SensorManager oraz trzech typw czujnikw, jakie
chcemy wykorzysta: akcelerometru, kompasu i czujnika orientacji. Definiujemy rwnie
zmienn przechowujc warto obrotu. Za chwil dowiemy si po co.
W metodzie onResume() uruchamiamy czujniki, a w metodzie onPause() wyczamy je.
Podczas otrzymywania aktualizacji odczytw czujnika okrelamy, do ktrej kategorii one nale,
i rejestrujemy te wartoci w lokalnych czonkach: accelValues, compassValues lub orientation
Values. Zauwamy, e moglibymy skopiowa tablic zdarze w celu zachowania lokalnych
kopii odczytw; oznaczaoby to jednak cige tworzenie obiektw, a to nie jest dobry pomys.
Tworzenie nowych obiektw i pniejsze ich usuwanie moe by naprawd kosztowne, jeli chodzi
o zuycie zasobw, dlatego ograniczamy si wycznie do aktualizowania ju istniejcych tablic.
Zwrmy uwag, e zanim zajmiemy si przetwarzaniem dalszej czci kodu, sprawdzamy za pomoc operacji logicznej, czy posiadamy wartoci zarwno w tablicy accelValues, jak i compass
Values. Widzimy nastpnie wywoanie metody getRotationMatrix(), po ktrej nastpuje
wywoanie metody getOrientation(). Wprowadzilimy rwnie metod getInclination().
Nie bdziemy z niej korzysta, warto jednak wiedzie, e reprezentuje ona kt pomidzy strumieniem magnetycznym a powierzchni Ziemi. Im bliej znajdujemy si ktrego z biegunw,
tym wiksza jest przekazywana warto kta. Nastpnie tworzymy licznik, za pomoc ktrego
bdzie wywietlana co dziesita aktualizacja wartoci. Podobnie jak we wczeniejszych przykadach, mechanizm ten suy do zminimalizowania obcienia interfejsu uytkownika, dziki
czemu aplikacja zyskuje na wydajnoci.
Rozdzia 26 Czujniki
937
Przekazujemy w niej macierz obrotw oraz wartoci okrelajce sposb zamiany osi x i y, w wyniku czego otrzymujemy now tablic obrotw (outR), a take warto logiczn, ktra wskazuje,
czy proces przeksztacania zosta zakoczony powodzeniem. Wartoci x i y s staymi klasy
SensorManager, takimi jak AXIS_Z lub AXIS_MINUS_Y.
Omawiane tu rozwizanie zademonstrowalimy w przykadowej aplikacji VirtualJaxWithRemap,
ktr moemy pobra wraz z innymi projektami.
Rozdzia 26 Czujniki
939
Czujniki grawitacji
Wraz z wersj 2.3 Androida wprowadzono czujniki grawitacji. W rzeczywistoci nie jest to
osobny ukad elektroniczny. Mamy tu do czynienia z wirtualnym czujnikiem opartym na odczytach akcelerometrw. Tak naprawd wykorzystywany jest tutaj opisany wczeniej mechanizm okrelania skadowej grawitacji przez akcelerometry, w ktrym jest ona oddzielana od
pozostaych si dziaajcych na urzdzenie. Nie moemy jednak modyfikowa tego mechanizmu i musimy zaakceptowa wszelkie wspczynniki i przeksztacenia dostpne w klasie tego
czujnika. By moe w przyszoci ten wirtualny czujnik bdzie wykorzystywa rwnie inne
sensory, na przykad yroskop, do dokadniejszego pomiaru wartoci grawitacji. Macierz wartoci w przypadku tego czujnika przekazuje odczyty grawitacji w taki sam sposb, jak miao to
miejsce w przypadku akcelerometrw.
Rozdzia 26 Czujniki
941
Otrzymamy w ten sposb singletonowy obiekt klasy NfcAdapter. Aby sprawdzi, czy klasa
NfcAdapter jest aktywna, wprowadzamy metod isEnabled(), ktra powraca z wartoci
logiczn okrelajc, czy technologia NFC zostaa wczona w panelu Ustawienia. Nigdzie nie
znalelimy udokumentowanego sposobu na programowe wczanie i wyczanie adaptera NFC.
Jeeli jest wyczony, a dana aplikacja wymaga jego uruchomienia, musimy powiadomi uytkownika o koniecznoci rcznego wczenia czujnika. Aby wywietli uytkownikowi waciwy
widok ustawie, moemy skorzysta z nastpujcego fragmentu kodu:
startActivityForResult(new Intent(
android.provider.Settings.ACTION_WIRELESS_SETTINGS), 0);
Po przetworzeniu tego kodu Android otworzy odpowiedni widok ustawie i uytkownik bdzie
mg wczy adapter NFC. Metoda zwrotna onActivityResult() zostanie wywoana w chwili
zamknicia okna ustawie przez uytkownika. Pamitajmy, e uytkownik moe nie wczy
adaptera pomimo powiadomienia. Nasza aplikacja powinna by przygotowana rwnie na ten
scenariusz.
Rozdzia 26 Czujniki
943
dzeniu istnieje inna aplikacja, dla ktrej tego typu terminale s przeznaczone, jednak ta waciwa
aplikacja nie otrzymaa terminalu. Taka sytuacja moe nastpi, w przypadku gdy filtr intencji
znalaz wicej ni jedn aplikacj pasujc do terminalu. Wtedy system wywietla monit, aby
uytkownik wybra waciw aplikacj. Moe si zdarzy, e uytkownik wybierze aplikacj, dla
ktrej dany terminal nie jest przeznaczony. Istnieje wic kolejny powd, dla ktrego naley
ostronie definiowa filtry intencji dla terminali NFC: jeeli uytkownik otrzyma monit o wybr
aplikacji, z duym prawdopodobiestwem przed podjciem decyzji wyjdzie z zasigu terminalu.
Jeeli moemy okreli, jakiego typu dane terminali bd przetwarzane przez nasz aplikacj,
oznacza to, e moemy je bardzo dokadnie sprecyzowa, na przykad za pomoc niestandardowego schematu identyfikatora URI lub wasnego typu MIME.
Wybr filtru intencji zaley od rodzaju dziaania umieszczonego wewntrz intencji terminalu
NFC (zostao to omwione powyej). Na listingu 26.8 zosta umieszczony przykadowy filtr
intencji dla terminalu NDEF, ktry moemy umieci w pliku AndroidManifest.xml.
Listing 26.8. Filtr intencji dla terminalu NDEF zawierajcego typ MIME
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<data android:mimeType="type/subtype" />
</intent-filter>
W tym przykadzie definiujemy schemat geo, dziki czemu po wykryciu terminalu zawierajcego
identyfikator rozpoczynajcy si od czonu geo: zostanie uruchomiona nasza aktywno. Moemy
stosowa wszystkie atrybuty wza <data> do okrelania, ktre dane terminalu NFC s oczekiwane przez nasz aktywno.
Jeeli nasza aktywno wymaga terminali NFC utworzonych w okrelonej technologii, moemy
skorzysta z filtru intencji zaprezentowanego na listingu 26.10. Moe rwnie zaistnie sytuacja, w ktrej zostanie wykryty terminal NDEF, lecz adna aktywno nie bdzie dopasowana
do przetwarzania intencji NDEF_DISCOVERED. Rwnie w takim przypadku nasza aktywno
moe otrzyma t intencj, dopki jest ona zgodna z filtrem intencji. Inaczej mwic, jeeli
intencja terminalu zawierajca dziaanie NDEF_DISCOVERED nie zostanie dostarczona do aktywnoci wyszukujcej tego typu dziaania, zostanie wysana do aktywnoci oczekujcej terminalu utworzonego w okrelonej technologii.
Zwrmy uwag, e wstawilimy teraz dziaanie definiujce poszukiwan technologi, a zamiast wza <data> wprowadzilimy znacznik <meta-data>, ktry znajduje si poza znacznikiem <intent-filter>. Mamy rwnie do czynienia z innymi znacznikami w tym wle, ktre
znajduj si w oddzielnym pliku, umieszczonym w katalogu /res/xml. Na listingu 26.11 demonstrujemy przykadowy plik nfc_tech_filter.xml.
Listing 26.11. Przykadowy plik XML zawierajcy filtr technologii NFC
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
</resources>
Filtr ten definiuje dwa rodzaje terminali oczekiwanych przez nasz aplikacj. Terminal NFC
zazwyczaj zawiera list obsugiwanych przez niego technologii. Jeeli ktrykolwiek z elementw
tej listy zosta wymieniony w filtrze z listingu 26.11, nasza aktywno uzyska dostp do intencji
tego terminalu.
Na listingu 26.11 pierwszy rodzaj terminalu okrela technologie NfcA oraz MifareUltralight,
w drugim za zdefiniowano: NfcB i Ndef. Moemy dodawa kolejne wzy <resources> do
tego pliku w celu definiowania nastpnych terminali akceptowanych przez nasz aktywno. Uwzgldniane tutaj technologie bior swoje nazwy od nazw klas dostpnych w pakiecie
android.nfc.tech, ale powinnimy wpisywa jedynie te mechanizmy, ktre bd przydatne
aktywnoci. Wzy potomne znacznika <tech-list> zawieraj wszystkie technologie, ktre
powinien posiada terminal NFC, aby jego intencja pasowaa do aktywnoci. Wszystkie technologie z danej listy musz si znajdowa na licie technologii obsugiwanych przez terminal
NFC. Zatem lista technologii w filtrze moe zawiera mniej elementw ni analogiczna lista
w terminalu NFC, nie moe jednak wystpi odwrotna sytuacja. Kontynuujc powyszy przykad, jeeli w terminalu NFC znajdzie si wycznie wskazanie technologii Ndef, terminal ten nie
zostanie przepuszczony przez aden filtr i aktywno nie otrzyma jego intencji. adna z wymienionych list filtru intencji nie stanowi podzbioru na licie terminalu. Gdyby ten terminal
zawiera technologie NfcA, NfcB oraz Ndef, okazaby si zgodny z drug specyfikacj i zostaby
przesany do aktywnoci. Ta druga specyfikacja stanowi podzbir listy technologicznej terminalu
NFC. Terminal ten byby dopasowany, nawet gdyby zawiera dodatkowe technologie, niewymienione w filtrze intencji.
Rozdzia 26 Czujniki
945
Ostatni filtr intencji, ktry moe si przyda Czytelnikowi, zosta zaprezentowany na listingu
26.12. Charakteryzuje go uniwersalno. Oznacza to, e jeli po wykryciu terminalu NFC nie
znaleziono adnej aktywnoci odbierajcej terminale NDEF bd zgodnej z okrelonymi w nim
technologiami lub jeli mamy do czynienia z nieznanym typem terminalu, zostanie utworzona
intencja zawierajca dziaanie ACTION_TAG_DISCOVERED.
Listing 26.12. Filtr intencji dla nieznanego lub nieprzetwarzanego terminalu NFC
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
Zwrmy uwag, e dla tego filtru intencji nie zdefiniowano adnego wza <data> ani
<meta-data>, poniewa nie s przenoszone adne dane w intencji oznaczonej dziaaniem
ACTION_TAG_DISCOVERED. W normalnej sytuacji oznaczaoby to konieczno wprowadzenia
znacznika <category>. Sprawa ma si jednak inaczej z intencjami terminali NFC. Stanowi
one specjalny przypadek, zatem w filtrach intencji nie s wymagane tego typu terminale w celu
dopasowania intencji. Jeeli intencja otrzymuje dziaania ACTION_TAG_DISCOVERED, oznacza to,
e system nie zdoa odnale aktywnoci dla terminali NFC. W tym momencie kada aktywno przyjmujca to dziaanie otrzyma intencj tego terminalu. W wikszoci standardowych
operacji nigdy nie natrafimy na intencj znacznika ACTION_TAG_DISCOVERED, poniewa wikszo terminali NFC bdzie dopasowanych do kryteriw NDEF lub TECH.
Istnieje jeszcze jeden sposb, w jaki aktywno moe otrzyma intencj terminalu NFC zastosowanie systemu dyspozycji pierwszoplanowej. Jeeli nasza aktywno znajduje si na pierwszym planie (co oznacza, e zostaa uruchomiona metoda onResume() i uytkownik korzysta
z tej aktywnoci), moemy utworzy intencj oczekujc, tablic filtrw intencji, tablic list
technologii, a nastpnie wprowadzi nastpujce wywoanie:
mAdapter.enableForegroundDispatch(this, pendingIntent,
intentFiltersArray, techListsArray);
gdzie mAdapter jest adapterem NFC, a this stanowi odniesienie do naszej aktywnoci. Za pomoc tego wywoania skutecznie wystawiamy nasz aktywno przed wszystkie pozostae aktywnoci i jeeli ktrykolwiek z jej filtrw jest dopasowany do wykrytego terminalu NFC, to
aktywno ta przetworzy terminal. Jeeli aktywno nie otrzyma intencji terminalu z powodu
niedopasowania, jej dziaanie bdzie sprawdzane wobec pozostaych aktywnoci. Musimy wywoa t metod z poziomu wtku interfejsu uytkownika, a najlepiej tego dokona w metodzie
onResume() naszej aktywnoci. Wymagane byoby rwnie wprowadzenie nastpujcego
wywoania:
mAdapter.disableForegroundDispatch(this);
z poziomu metody zwrotnej onPause(), dziki czemu nasza aktywno nie otrzyma intencji,
ktrej nie bdzie moga przetworzy. Gdy aktywno w taki sposb otrzyma intencj, przekae
j za pomoc metody zwrotnej onNewIntent().
Mamy tu do czynienia ze standardow intencj oczekujc. Tablica intentFiltersArray moe
stanowi zbir potrzebnych nam obiektw IntentFilter, z ktrych kady definiuje okrelone
dziaanie, a take, w razie potrzeby, dowolne dane lub typy MIME. Na listingu 26.13 widzimy
przykadowy kod generujcy filtr intencji dla obiektu Ndef, ktry nastpnie zostaje wstawiony
do tablicy.
Nie zapominajmy, e w tablicy filtrw intencji moe si znale wiele wystpie obiektw
IntentFilter, zawierajcych te same lub rne dziaania, a take posiadajcych dane lub
ich pozbawione. To samo dotyczy wartoci pl typw.
Obiekt techListsArray jest tablic, ktrej wartociami s inne tablice listy zawierajce
nazwy klas obsugiwanych przez znacznik NFC. Moemy okreli wiele list technologii. Zostao
to zaprezentowane na listingu 26.14, ktry jest odpowiednikiem pliku zasobw ukazanego na
listingu 26.11.
Listing 26.14. Kod tabeli zawierajcej listy technologii
techListsArray = new String[][] {
new String[] { NfcA.class.getName(),
MifareUltralight.class.getName() },
new String[] { NfcB.class.getName(),
Ndef.class.getName() }
};
Rozdzia 26 Czujniki
947
Jedn z pierwszych kwestii rozwaanych w procesie projektowania aplikacji otrzymujcej i przetwarzajcej intencje NFC jest konieczno obsugi fizycznego terminalu znajdujcego si w otoczeniu urzdzenia za pomoc interfejsu sprztowego. Interfejs API generuje wywoania blokujce, co oznacza, e nie bd one przekazywane tak szybko, jak bymy sobie tego yczyli,
w zwizku z czym bdziemy musieli uruchamia metody terminalu w osobnym wtku.
Informacje terminalu NFC s umieszczone w pakiecie dodatkowych danych otrzymywanej intencji. Po odebraniu intencji moemy uzyska dostp do tych informacji za pomoc nastpujcego fragmentu kodu:
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String[] techlists = tag.getTechLists();
Jeeli zdefiniowany przez nas filtr intencji jest bardzo dokadny, bdziemy ju doskonale wiedzie, jaki rodzaj terminalu otrzymalimy. Jeeli jednak zdefiniowalimy wiksz liczb akceptowanych technologii terminalu, moemy teraz sprawdzi listy tych technologii, aby pozna
mechanizmy, z jakimi bdziemy mieli do czynienia w terminalu. Kady cig znakw na tej
licie stanowi nazw klasy przechowujcej technologi obsugiwan przez wykryty terminal.
Jeeli nasz terminal obsuguje klas android.nfc.tech.Ndef, moemy wykorzysta poniszy kod do uzyskania bardziej bezporedniego dostpu do danych NDEF:
NdefMessage[] ndefMsgs = intent.getParcelableArrayExtra
(NfcAdapter.EXTRA_NDEF_MESSAGES);
Teoretycznie moemy otrzyma warto null, w przypadku gdy intencja nie bdzie zawieraa
informacji NDEF. W przeciwnym wypadku nie powinno by problemu z analiz skadni przesyanych danych. Moemy odczytywa elementy klasy NdefMessage z poziomu intencji, zlicza je i w przypadku kadej z nich odbiera zawarte w nich obiekty NdefRecord.
Obiekty klasy NdefRecord s do interesujce. Nie zaszkodzi, jeeli Czytelnik zajrzy do specyfikacji technologii NFC, dostpnej pod adresem http://www.nfc-forum.org/specs/. Aby uzyska do
niej dostp, trzeba zaakceptowa postanowienia licencyjne NFC Forum. Jest to bezpatny proces,
naley jednak poda imi i nazwisko, adres, numer telefonu i adres e-mail. Alternatywnym rozwizaniem jest aplikacja NfcDemo dostpna w pakiecie SDK Androida 2.3.3, w folderze z przykadowymi programami. Kod rdowy tego przykadu zosta umieszczony na stronie http://developer.
android.com/resources/samples/NFCDemo/index.html. Aplikacja ta odbiera intencje NFC i wywietla zawarto klasy NdefRecord w widoku ListView. Sytuacja komplikuje si z powodu obecnoci kilku odmian klasy NdefRecord, ktre mog zosta przesane wraz z obiektami NdefMessage. Kada odmiana tej klasy ma inne zastosowanie. Na przykad odmiana Text przechowuje
tekst w okrelonym jzyku. W odmianie Uri znajdziemy identyfikator URI. Ze wszystkich
znanych rodzajw rekordw NDEF aplikacja NfcDemo obsuguje tylko trzy, z czego dwa przed
chwil omwilimy, a trzecim jest SmartPoster, ktremu wkrtce przyjrzymy si uwaniej.
Format klasy NdefRecord skada si z trzybitowego pola TNF (ang. Type Name Format format
nazwy typu), pola typu o zmiennej dugoci, pola identyfikatora o zmiennej dugoci oraz
pola adunku o zmiennej dugoci. A zatem mamy do czynienia z dwoma kategoriami pl.
Pole TNF stanowi podstawowy typ tego obiektu i definiuje zawarto caego rekordu. Moe
by to na przykad bezwzgldny rekord URI (TNF_ABSOLUTE_URI) lub oficjalny rekord RTD
(TNF_WELL_KNOWN). Nastpne pole typu przechowuje dokadniejsze informacje na temat rodzaju rekordu w oparciu o warto pola TNF. Jeeli w polu TNF zostaa zdefiniowana staa
TNF_WELL_KNOWN, pole to bdzie si skadao ze staych RTD_* klasy NdefRecord, takich jak
RTD_SMART_POSTER. Jeeli wartoci pola TNF jest TNF_ABSOLUTE_URI, kolejnym polem typu
bdzie konstrukt BNF niezalenego identyfikatora URI, zdefiniowany w specyfikacji RFC 3986.
Nastpnym polem obiektu NdefRecord jest jego identyfikator. Odczytywany rekord moe
posiada identyfikator, chocia nie jest to wymagane.
Na kocu mamy do czynienia z adunkiem. Moe to by do dua tablica bajtowa, ktra posiada jednak wewntrzn struktur, zalen od rodzaju obiektu NdefRecord. Trzeba zwrci
uwag na t struktur. W przypadku typu RTD URI pierwszy bajt tej tablicy reprezentuje pocztek identyfikatora URI. Na przykad warto 1 oznacza http://www. i od tego czonu
bdzie rozpoczyna si kady identyfikator URI znajdujcy si we wntrzu adunku. W przypadku typu rekordu Text pierwszy bajt tabeli adunku stanowi warto kodowania tekstu
(UTF-8 lub UTF-16), a take dugo tablicy jzyka, wystpujcej tu po polu stanu kodowania.
Za polem jzyka znajduje si waciwy tekst. W przypadku obiektw SmartPoster sprawa jest
bardziej skomplikowana, gdy kady obiekt NdefRecord przechowuje obiekty NdefMessage,
przechowujce z kolei wicej obiektw NdefRecord. Te ostatnie mog zawiera rekord Title
(zbudowany tak samo jak rekordy Text), rekord URI (niernicy si od omawianego wczeniej), rekord zalecanego dziaania, rekord rozmiaru, rekord ikony oraz rekord typu. Warto
zalecanego dziaania wskazuje na czynnoci, jakie aplikacja moe wykonywa w przypadku danych obiektu SmartPoster. Zwrmy uwag, e wartoci zalecanego dziaania nie s wymieniane w dokumentacji klasy NdefRecord. S one nastpujce:
-1
0
1
2
UNKNOWN
DO_ACTION
SAVE_FOR_LATER
OPEN_FOR_EDITING
Tylko od nas zaley, co z nimi zrobimy, chocia oczywicie prawdopodobnie bdziemy chcieli
sprbowa przeprowadzi na odczytywanym terminalu najwaciwsz operacj. Jeli na przykad
mamy do czynienia z formatem rekordu TNF_WELL_KNOWN, jego typem jest RTD_SMART_POSTER,
a zalecane dziaanie przybiera warto 0 (DO_ACTION) i jest poczone z adresem URL strony
WWW, najprawdopodobniej bdziemy chcieli uruchomi przegldark internetow i otworzy
stron dostpn pod tym adresem. Rekord rozmiaru pozwala zdefiniowa wielko obiektu
czekajcego po drugiej stronie cza. Jeeli terminal odnosi si do pobieralnego pliku, w rekordzie rozmiaru moe zosta okrelony jego rozmiar. W rekordzie ikony przechowywany jest obraz
ikony, wykorzystywany przez urzdzenie do jej wywietlania wraz z tytuem oraz adresem URL.
Rekord typu nie jest tym samym co warto formatu TNF czy typ klasy NdefRecord. Jest on
wykorzystywany w terminalach SmartPoster, a w tym przypadku reprezentuje on typ MIME
obiektu znajdujcego si po drugiej stronie adresu URL. Urzdzenie moe nie obsugiwa
danego typu MIME i w ten sposb zostanie uniemoliwione pobieranie tego obiektu.
Jedynym niezbdnym podrekordem terminalu SmartPoster jest rekord URI i tylko jedno
jego wystpienie moe si znajdowa w kadym terminalu. Moemy wypisa wiele rekordw
Title, kady przechowujcy tekst w innym jzyku. Moliwe jest rwnie zamieszczanie wielu
rekordw ikony, pod warunkiem e kady z nich posiada osobny typ MIME dla swojego formatu.
Rozdzia 26 Czujniki
949
W przypadku wszystkich rodzajw terminali NFC, w tym rwnie terminali NDEF, moemy
zastosowa poniszy fragment kodu, aby uzyska wystpienie danego typu terminali:
NfcA nfca = NfcA.get(tag);
Za pomoc tak utworzonego obiektu moemy uzyska dostp do okrelonych metod, najbardziej nadajcych si dla danego typu terminalu. W przypadku terminali Ndef i NdefFormatable
klasy NdefMessage oraz NdefRecord okazuj si bardzo przydatne do przetwarzania ich danych.
Klasy pozostaych rodzajw terminali posiadaj odpowiednie metody umoliwiajce obsug
tych terminali i zawartych w nich informacji. Mamy do dyspozycji odpowiednie metody do
odczytu i zapisu danych na terminalu. Zwrmy uwag, e zapisywanie danych w terminalu nie
jest tym samym co emulacja karty. Zapisywanie terminalu oznacza, e w pobliu urzdzenia
znajduje si jaki terminal, ktry mona zmodyfikowa (jeli mamy odpowiednie uprawnienia).
Emulacja karty jest oddzielnym procesem.
Odnoniki
Znajdziemy tu cza do materiaw pomocnych w zrozumieniu koncepcji zawartych w tym
rozdziale:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu list projektw utworzonych
specjalnie na potrzeby niniejszej ksiki. Projekty przeznaczone dla tego rozdziau
zostay umieszczone w katalogu ProAndroid3_R26_Czujniki. Kady z zawartych
w nim projektw znajduje si w osobnym katalogu. W katalogu umiecilimy rwnie
plik Czytaj.TXT, stanowicy dokadn instrukcj importowania projektw
do rodowiska Eclipse.
Rozdzia 26 Czujniki
951
Podsumowanie
W niniejszym rozdziale przyjrzelimy si gwnej architekturze czujnikw w Androidzie, a take
funkcji komunikacji bliskiego pola NFC. Zademonstrowalimy mechanizm odczytywania
danych generowanych przez czujniki oraz sposoby ich przetwarzania. Teraz Czytelnik powinien
by w stanie tworzy wietne aplikacje wsppracujce z nowoczesnymi urzdzeniami w otaczajcym wiecie.
R OZDZIA
27
Analiza interfejsu kontaktw
W rozdziale 4., dotyczcym dostawcw treci, wymienilimy zalety wynikajce z eksponowania danych za pomoc abstrakcji dostawcy treci. Udowodnilimy rwnie,
e takie wyodrbnione dane s dostpne w postaci zbioru adresw URL, ktre inne
obiekty mog odczytywa, wysya do nich zapytania, a take aktualizowa oraz wstawia lub usuwa w ich wntrzu wasne informacje. Wspomniane adresy URL oraz
ich kursory stanowi podstaw interfejsu API dostawcy treci.
Jednym z takich interfejsw API dostawcw treci jest interfejs kontaktw sucy
do pracy z danymi kontaktowymi. W Androidzie kontakty s przechowywane w bazie
danych i eksponujemy je za pomoc dostawcy treci, ktrego uprawnienie rozpoczyna si od segmentu:
content://com.android.contacts
Dokumentacja zestawu Android SDK wymienia wiele rnych kontraktw zawieranych przez tego dostawc kontaktw za pomoc rnorodnych interfejsw
i klas Java, ktre s przechowywane w pakiecie:
android.provider.ContactsContract
W czasie tworzenia aplikacji natrafimy na wiele klas, pomocnych w wysyaniu zapyta, odczytywaniu, aktualizowaniu oraz wstawianiu kontaktw do lub z bazy
kontaktw, ktrych nadrzdnym kontekstem jest ContactsContract. Podstawowa dokumentacja omawiajca zastosowanie interfejsu API kontaktw jest dostpna na stronie systemu Android:
http://developer.android.com/resources/articles/contacts.html
Gwny punkt wejciowy interfejsu, czyli ContactsContract, nosi waciw nazw,
poniewa klasa ta definiuje kontrakt pomidzy klientami kontaktw a dostawc
i zabezpieczeniem bazy kontaktw.
W tym rozdziale omwimy pojcie kontraktu do szczegowo, nie wymienimy
jednak kadego najdrobniejszego detalu. Interfejs kontaktw jest bardzo rozbudowany, a jego korzenie sigaj naprawd daleko. Gdy jednak powicimy mu uwag,
po kilku tygodniach zauwaymy, e jego zasadnicza struktura wcale nie jest taka
skomplikowana. Wanie na tej strukturze chcielibymy si skupi najbardziej
i wyjani jej podstawowe mechanizmy, co umoliwi Czytelnikowi jej zrozumienie w czasie potrzebnym na przeczytanie rozdziau.
Koncepcja konta
Wszystkie kontakty w systemie Android dziaaj w kontekcie konta. Czym jest konto? Jeli na
przykad korzystamy ze skrzynki pocztowej umieszczonej na serwerze Google, to znaczy, e
posiadamy konto Google. Jeeli umiecilimy swoje dane w serwisie Facebook i jestemy jego
uytkownikami, stalimy si posiadaczami konta Facebook.
Nawet jeli korzystamy tylko z poczty e-mail na serwerze Google, te same nazwa uytkownika
i haso daj nam dostp do pozostaych usug tej firmy, zatem nasze konto pocztowe nie jest
ograniczone wycznie do skrzynki pocztowej. Jednak niektre rodzaje kont s ograniczone do
jednego rodzaju usugi, czego przykadem jest konto pocztowe typu POP (ang. Post Office Protocol
protok wzw pocztowych). W przypadku urzdzenia mobilnego moemy zarejestrowa
wiele rnych usug opartych na korzystaniu z konta.
Cz z takich kont, na przykad konta Google, Facebook lub firmowe konto Microsoft Exchange,
moemy ustanowi z poziomu widoku Konta i synchronizacja, dostpnego w aplikacji Ustawienia. Wicej informacji dotyczcych kont znajdziemy w instrukcji uytkownika systemu
Android. W podrozdziale Odnoniki zamiecilimy do niej adres URL.
955
Na rysunku 27.2 interesuje nas gwnie lista dostpnych kont. W celach wiczeniowych kliknijmy przycisk Dodaj konto, dziki czemu ujrzymy list dostpnych kont, ktre moemy skonfigurowa lub doda (rysunek 27.3).
Lista ta bdzie miaa rn zawarto w zalenoci od rodzaju urzdzenia oraz dostpnych elementw. Na rysunku 27.3 zaprezentowalimy list dostpnych kont dla wersji 2.3 Androida
zainstalowanej na emulatorze, gdzie docelowy jest 9. poziom interfejsw Google API. Jeeli pobrano sam podstawow wersj zestawu SDK, nie bdzie mona wybra interfejsw Google API
Jeli klikniemy przycisk Utwrz, pojawi si pola wymagane do zaoenia nowego konta Google,
co zostao ukazane na rysunku 27.5.
957
Na rysunku 27.5 widzimy pola wymagane do utworzenia nowego konta Google, jeli uytkownik jeszcze go nie posiada. Jak ju stwierdzilimy, liczba i tre pl mog si rni w zalenoci
od rodzaju konta. Teraz pokaemy, w jaki sposb wprowadzi ustawienia dla ju istniejcego
konta Google. W tym przypadku cay proces konfiguracji ogranicza si do zalogowania si na
swoje konto, tak jak wida na rysunku 27.6.
Skoro zademonstrowalimy ju podstawy dotyczce kont oraz sposb ich umieszczania w urzdzeniu, wyjanijmy, dlaczego konta peni tak wan rol dla kontaktw.
Wyliczanie kont
Zasadniczo interfejs kontaktw obsuguje kontakty przechowywane wewntrz rnych kont.
Samo tworzenie kont zachodzi poza interfejsem kontaktw, zatem opis moliwoci zwizanych
z pisaniem wasnych dostawcw kont oraz synchronizowania z nimi kontaktw wykracza poza
zakres tego rozdziau. Sam proces konfiguracji kont jest nieistotny dla niniejszego rozdziau. Jeeli
jednak chcemy doda kontakt lub list kontaktw, musimy wiedzie, jakie konta s dostpne
Oczywicie, aby uruchomi kod z listingu 27.1, w pliku manifecie musi zosta umieszczone
odpowiednie uprawnienie, widoczne na listingu 27.2.
Listing 27.2. Uprawnienie pozwalajce na odczytywanie zawartoci kont
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
W tym przypadku przyjlimy, e posiadamy skonfigurowane tylko jedno konto (Google). Jeeli
jest ich wicej, wszystkie zostan wywietlone w podobny sposb.
Zanim zajmiemy si bardziej szczegowo kontaktami, zastanwmy si, w jaki sposb uytkownicy tworz kontakty za pomoc aplikacji fabrycznie umieszczonej w systemie Android.
Aplikacja Kontakty
Jeeli producent urzdzenia, na przykad Motorola, lub operator (przykadowo Verizon1) nie
przewidzieli wasnej aplikacji zarzdzajcej kontaktami, z pomoc przychodzi system Android
i jego domylna aplikacja. Znajdziemy j bez trudu na licie aplikacji dostpnych w urzdzeniu,
jej dokumentacja rwnie zostaa zamieszczona w instrukcji obsugi systemu Android.
Wywietlanie kontaktw
Po uruchomieniu aplikacji Kontakty pierwszym z wywietlonych ekranw bdzie lista kontaktw
(rysunek 27.7). Zasadniczo kontaktem s dane dotyczce osoby, ktr znamy w kontekcie
konta, np. Gmail. Jeeli posiadamy wiele kont, lista z rysunku 27.7 zostanie wypeniona pochodzcymi z nich kontaktami. Spogldajc na ten ekran, nie dowiemy si, z ktrym kontem jest
1
959
powizany dany kontakt. System postara si nie powiela takich samych kontaktw pochodzcych z rnych kont, chyba e zostanie to jawnie dozwolone. W nastpnym podrozdziale zajmiemy si t heurystyk podobnych kontaktw.
Odnoszc si do sytuacji z rysunku 27.7, zaoylimy, e s dostpne dwa kontakty, ktre s alfabetycznie uszeregowane.
W grnej czci zaprezentowanego na rysunku 27.9 ekranu Edytuj kontakt ujrzymy nazw
konta, w ramach ktrego dany kontakt jest modyfikowany lub tworzony. W przypadku omawianego kontaktu jedynym kontem jest telefon, co oznacza, e nie ma dla niego skonfigurowanego adnego konta serwerowego (np. Google) oraz e mamy do czynienia z kontem lokalnym.
Rzeczywicie, w bazie kontaktw nazwa i typ konta przyjmuj w tym przypadku wartoci null.
Firma Google usilnie zaleca utworzenie przynajmniej jednego konta na jej serwerze, zanim
urzdzenie pracujce pod kontrol systemu Android zostanie uaktywnione, bez wzgldu na to,
czy korzystamy z telefonu, czy tabletu.
Jak jednak wida, moemy tworzy kontakt bez koniecznoci czenia go z okrelonym kontem,
w takich za przypadkach ekran widoczny w momencie tworzenia takiego kontaktu wyglda tak
jak na rysunku 27.9.
Patrzc na rysunek 27.9, zauwaymy, e tu za wskanikiem typu konta (Tylko telefon itd.) znajduje si miejsce na zdjcie powizane z kontaktem, a nastpnie zestaw pl. Rysunek 27.10
prezentuje nastpne pola, widoczne po przewiniciu ekranu.
961
Jak wida na rysunku 27.10, istnieje moliwo przypisania do kontaktu rnych rodzajw numerw telefonw i adresw e-mail. Czytelnik zastanawia si pewnie rwnie, czy w kontaktach
moemy umieszcza wasne pola zawierajce niestandardowe dane (widoczne na rysunku
27.10 pola Telefon oraz E-mail stanowi znane, predefiniowane typy pl. By moe kto chciaby
wstawi mniej oczekiwane formaty danych. To wanie mamy na myli, piszc niestandardowe).
Interfejs API kontaktw pozwala na wprowadzenie tego typu pl, co zostao zaprezentowane
na rysunku 27.11, gdzie dodalimy dane adresowe do kontaktu.
Eksportowanie kontaktw
Zakoczmy przegld aplikacji zarzdzajcej kontaktami zapoznaniem si z mechanizmem eksportowania kontaktw na kart SD. Funkcja ta pozwala nam midzy innymi na przegldanie
rodzajw danych przechowywanych w kontakcie oraz sprawdzenie, w jaki sposb s prezentowane w formie tekstowej (rysunek 27.13).
963
Moemy skopiowa widoczny na rysunku 27.14 plik .vcf z urzdzenia do stacji roboczej za pomoc ikon widocznych w prawym grnym rogu zakadki File Explorer. Zawarto pliku .vcf dla
dwch kontaktw widocznych na rysunku 27.8 bdzie wygldaa tak jak zaprezentowano na
listingu 27.3.
Listing 27.3. Eksportowane kontakty w formacie VCF
BEGIN:VCARD
VERSION:2.1
N:C1-Nazwisko;C1-Imi;;;
FN:C1-Imi C1-Nazwisko
TEL;TLX:55555
TEL;WORK:66666
EMAIL;HOME:test@dom.com
EMAIL;WORK:test@praca.com
ORG:PracaKomp
TITLE:Prezes
ORG:Inna praca
TITLE:Prezes
URL:www.com
NOTE:Uwaga1
X-AIM:aim
X-MSN:wlive
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:C2-Nazwisko;C2-Imi;;;
FN:C2-Imi C2-Nazwisko
END:VCARD
Kady rodzaj danych, na przykad email czy structuredpostal (przechowujcy kod pocztowy),
posiada wasny zestaw pl. Skd moemy wiedzie, jaki ksztat przybieraj te pola? S one
zdefiniowane w pomocniczych klasach, znajdujcych si w pakiecie:
android.provider.ContactsContract.CommonDataKinds
Analiza kontaktw
Jak ju stwierdzilimy, kontakt jest przypisany do konta. Kade konto zawiera wasny zbir
kontaktw. Z kolei na kady kontakt przypada zestaw elementw danych (na przykad adres
e-mail, numer telefonu, imi i nazwisko czy kod pocztowy). Co wicej, Android przedstawia
965
zbiorczy widok nieprzetworzonych kontaktw, ktre umieszcza na licie kontaktw po sprawdzeniu, czy speniaj okrelone kryteria. Uytkownik widzi tak zbiorcz list kontaktw w momencie uruchomienia aplikacji Kontakty (rysunek 27.8).
Sprawdzimy teraz, w jaki sposb dane powizane z kontaktami s przechowywane w rnych
rodzajach tabel. Wyjanienie regu rzdzcych tymi tabelami oraz powizanymi z nimi widokami stanowi klucz do zrozumienia dziaania interfejsu kontaktw.
Nieprzetworzone kontakty
Przypominamy, e kontakty widoczne po uruchomieniu aplikacji Kontakty s nazywane
kontaktami zbiorczymi. Na kady kontakt zbiorczy skada si zbir tak zwanych nieprzetworzonych kontaktw. Kontakt zbiorczy stanowi jedynie widok zestawu podobnych do siebie
Najwaniejsze pola zostay wyrnione pogrubion czcionk. Podobnie jak w przypadku pozostaych tabel systemu Android, znajdziemy tu kolumn _ID, niepowtarzalnie definiujc nieprzetworzony kontakt. Pola account_name oraz account_type wsplnie definiuj konto tego
kontaktu (dokadniej nieprzetworzonego kontaktu). Pole sourceid wskazuje sposb okrelania
nieprzetworzonego kontaktu w nadrzdnym koncie. Zamy na przykad, e chcemy dowiedzie si, w jaki sposb jest zdefiniowany identyfikator nieprzetworzonego kontaktu wewntrz
konta pocztowego Google. W tym przypadku zazwyczaj pole to przechowuje identyfikator poczty
e-mail uytkownika.
967
Kolumna contact_id odnosi si do kontaktu zbiorczego, ktrego czci jest dany nieprzetworzony kontakt. Kontakt zbiorczy wskazuje przynajmniej jeden kontakt (lub wicej),
ktry w istocie prezentuje t sam osob wykryt na rnych kontach.
Pole display_name przechowuje wywietlan nazw kontaktu. Jest to przede wszystkim pole
tylko do odczytu. Jego warto jest ustanawiana przez wyzwalacze na podstawie informacji
umieszczanych w tabeli danych (ktra zostanie omwiona w nastpnym punkcie).
Pola zawierajce czon sync s wykorzystywane przez konto do synchronizowania kontaktw
pomidzy urzdzeniem a kontem serwerowym, na przykad poczt Gmail.
Chocia do analizy tych pl wykorzystywalimy narzdzia obsugujce silnik SQLite, istnieje
wicej sposobw na ich przegldanie. Zalecanym rozwizaniem jest wykorzystanie definicji klas
zadeklarowanych w interfejsie ContactsContract. Aby przebada kolumny nalece do tabeli
nieprzetworzonych kontaktw, powinnimy przejrze dokumentacj klasy ContactsContract.
RawContact.
Rozwizanie to posiada swoje zalety i wady. Istotn zalet jest moliwo zapoznania si z polami, ktre s opublikowane i akceptowane przez zestaw Android SDK. Kolumny bazodanowe
mog by dodawane lub usuwane bez koniecznoci modyfikowania interfejsu publicznego. Zatem
gdybymy chcieli bezporednio manipulowa kolumnami bazodanowymi, moemy, ale nie musimy na nie natrafi. Z kolei korzystajc z publicznych definicji tych kolumn, jestemy zawsze
zabezpieczeni.
Naley jednak wspomnie, e dokumentacja omawianej klasy zawiera mnstwo innych staych
pomieszanych z nazwami kolumn; nawet my w trakcie opracowywania ksiki pogubilimy si
w ich ogromie. Ta olbrzymia liczba definicji klasy daje wraenie zoonoci interfejsu API, podczas gdy w rzeczywistoci 80 procent jego dokumentacji omawia stae tych kolumn oraz umoliwiajce do nich dostp identyfikatory URI.
Gdy w nastpnych podrozdziaach bdziemy wiczy stosowanie interfejsu kontaktw, zaczniemy wykorzystywa stae zdefiniowane w dokumentacji klasy zamiast bezporednich nazw
kolumny. Mimo to uwaamy, e bezporednia eksploracja tabel jest najszybszym sposobem pozwalajcym na zrozumienie interfejsu kontaktw.
Zastanwmy si teraz, w jaki sposb s przechowywane dane zwizane z kontaktem, na przykad adres e-mail lub numer telefonu.
Tabela danych
Jak ju wspomnielimy podczas omawiania definicji tabeli nieprzetworzonych kontaktw, taki
kontakt (w tym rozczarowujcym znaczeniu) stanowi wycznie identyfikator wskazujcy, z jakim kontem jest zwizany. Wikszo informacji stanowicych zawarto kontaktu jest przechowywana nie w tabeli nieprzetworzonych kontaktw, lecz w tabeli danych. Kady element
danych, taki jak adres e-mail lub numer telefonu, posiada wasne pole w tabeli danych. Wszystkie te wiersze zawierajce dane s powizane z nieprzetworzonym kontaktem za pomoc jego
identyfikatora, ktry stanowi jedn z kolumn tabeli danych oraz jest gwnym identyfikatorem
w tabeli nieprzetworzonych kontaktw.
Tabela danych skada si z szesnastu standardowych kolumn, w ktrych moe by przechowywanych szesnacie dowolnych punktw danych danego elementu, na przykad adresu e-mail.
Na listingu 27.7 widzimy struktur tabeli danych.
Najwaniejsze kolumny tabeli kontaktw zostay wyrnione pogrubion czcionk. Tak jak
moglimy si spodziewa, pole raw_contact_id stanowi odniesienie do nieprzetworzonego
kontaktu, z ktrym jest zwizana dana informacja.
Pole mimetype_id okrela typ MIME danych wejciowych i wskazuje jeden z typw zdefiniowanych na listingu 27.4. Kolumny od data1 do data15 stanowi standardowe tablice przechowujce cigi znakw, w ktrych moemy umieszcza wszelkie niezbdne informacje, zgodne
z danym typem MIME. Take tutaj pola typu sync obsuguj synchronizacj kontaktw. Tabela
zawierajca informacje o typie MIME identyfikatorw zostaa zaprezentowana na listingu 27.8.
Listing 27.8. Definicja tabeli wyszukiwania typw MIME
CREATE TABLE mimetypes
(_id INTEGER PRIMARY KEY AUTOINCREMENT,
mimetype TEXT NOT NULL)
Podobnie jak ma to miejsce w tabeli nieprzetworzonych danych, analiza kolumn tabeli danych
jest moliwa dziki dokumentacji pomocniczej klasy ContactsContract.Data.
Chocia moemy rozpoznawa kolumny za pomoc definicji tej klasy, nie zapoznamy si w ten
sposb z zawartoci pl od data1 do data15. W tym celu trzeba pozna definicje rnorodnych klas umieszczonych w przestrzeni nazw ContactsContract.CommonDataKinds.
Poniej prezentujemy przykady niektrych zawartych w niej klas:
ContactsContract.CommonDataKinds.Email,
ContactsContract.CommonDataKinds.Phone.
W istocie dla kadego typu danych wymienionego na listingu 27.4 istnieje jedna klasa ze wspomnianej przestrzeni nazw. W ostatecznym rozrachunku wszystkie elementy podrzdne klasy
CommonDataKinds wskazuj, ktre ze standardowych pl danych (data1 data15) s wykorzystywane oraz w jakim celu.
Kontakty zbiorcze
Ostatecznie dochodzimy do wniosku, e kontakt i zwizane z nim dane w sposb jednoznaczny
s przechowywane w tabeli nieprzetworzonych kontaktw i tabeli danych. Z drugiej strony
kontakt zbiorczy jest nieco bardziej heurystyczny i ju nie tak bardzo jednoznaczny.
969
Gdy natrafimy na kontakt, ktry jest taki sam dla rnych kont, chcielibymy, eby jego nazwa
pojawiaa si na licie tylko raz. W tym celu Android umieszcza wszystkie podobne kontakty w
widoku przeznaczonym tylko do odczytu. Takie zbiorcze kontakty s przechowywane w tabeli
zwanej kontaktami. Aby zapeni lub modyfikowa tak tabel kontaktw zbiorczych, Android
wykorzystuje wiele wyzwalaczy wobec tabeli nieprzetworzonych kontaktw i tabeli danych.
Zanim przejdziemy do omwienia mechanizmw zbierania kontaktw, spjrzmy najpierw na
definicj tabeli kontaktw (listing 27.9).
Listing 27.9. Definicja tabeli kontaktw zbiorczych
CREATE TABLE contacts
(_id INTEGER PRIMARY KEY AUTOINCREMENT,
name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),
photo_id INTEGER REFERENCES data(_id),
custom_ringtone TEXT,
send_to_voicemail INTEGER NOT NULL DEFAULT 0,
times_contacted INTEGER NOT NULL DEFAULT 0,
last_time_contacted INTEGER,
starred INTEGER NOT NULL DEFAULT 0,
in_visible_group INTEGER NOT NULL DEFAULT 1,
has_phone_number INTEGER NOT NULL DEFAULT 0,
lookup TEXT,
status_update_id INTEGER REFERENCES data(_id),
single_is_restricted INTEGER NOT NULL DEFAULT 0)
Najwaniejsze pola zostay oznaczone pogrubion czcionk. aden klient nie aktualizuje bezporednio tej tabeli. Po dodaniu nieprzetworzonego kontaktu wraz z jego wspistniejcymi
danymi Android sprawdza pozostae nieprzetworzone kontakty pod ktem podobiestwa. Po
wykryciu powtarzajcego si wpisu jego identyfikator stanie si rwnie identyfikatorem nowego
nieprzetworzonego kontaktu. Nie zostanie wprowadzony aden nowy wpis do tabeli kontaktw
zbiorczych. W przypadku braku obecnoci duplikatu zostanie utworzony nowy kontakt zbiorczy,
a jego identyfikator bdzie przypisany rwnie do nowego nieprzetworzonego kontaktu.
Do okrelenia, czy dwa nieprzetworzone kontakty s podobne, Android stosuje nastpujcy
algorytm:
1. Dwa nieprzetworzone kontakty posiadaj takie same nazwy.
2. Wyrazy tworzce nazwy kontaktw s takie same, uoone s jednak w innej
kolejnoci: pierwszy ostatni, pierwszy, ostatni lub ostatni, pierwszy.
3. Rozpoznawane s zdrobnienia imion, na przykad Robercik jest zdrobnieniem
imienia Robert.
4. Jeli jeden z nieprzetworzonych kontaktw zawiera tylko dane o imieniu lub nazwisku,
zostanie wczony mechanizm poszukiwania innych atrybutw, na przykad numeru
telefonu lub adresu e-mail, i jeeli bd one takie same, kontakty zostan ze sob
powizane.
5. Jeli jeden z nieprzetworzonych kontaktw nie bdzie zawiera imienia ani nazwiska,
podobnie jak w punkcie 4., spowoduje to rwnie wczenie procesu wyszukiwania
innych atrybutw.
971
view_contacts
Pierwszym ze wspomnianych widokw jest view_contacts. Chocia mamy do dyspozycji tabel przechowujc zbiorcze kontakty (tabela kontaktw), interfejs API nie pozwala na uzyskanie bezporedniego dostpu do tej tabeli. Zamiast tego do przegldania kontaktw zbiorczych
suy wanie widok view_contacts. Gdy wysyamy zapytanie oparte na identyfikatorze URI
ContactsContract.Contacts.CONTENT_URI, otrzymamy kolumny, ktre bazuj na widoku
view_contacts. Definicja tego widoku zostaa umieszczona na listingu 27.11.
Listing 27.11. Widok umoliwiajcy odczytywanie kontaktw zbiorczych
CREATE VIEW view_contacts AS
SELECT contacts._id AS _id,
contacts.custom_ringtone AS custom_ringtone,
name_raw_contact.display_name_source AS display_name_source,
name_raw_contact.display_name AS display_name,
name_raw_contact.display_name_alt AS display_name_alt,
name_raw_contact.phonetic_name AS phonetic_name,
name_raw_contact.phonetic_name_style AS phonetic_name_style,
name_raw_contact.sort_key AS sort_key,
name_raw_contact.sort_key_alt AS sort_key_alt,
name_raw_contact.contact_in_visible_group AS in_visible_group,
has_phone_number,
lookup,
photo_id,
contacts.last_time_contacted AS last_time_contacted,
contacts.send_to_voicemail AS send_to_voicemail,
contacts.starred AS starred,
contacts.times_contacted AS times_contacted, status_update_id
FROM contacts JOIN raw_contacts AS name_raw_contact
ON(name_raw_contact_id=name_raw_contact._id)
Zwrmy uwag, e widok ten czy tabel kontaktw z tabel nieprzetworzonych kontaktw,
a wsplnym mianownikiem jest tu identyfikator zbiorczego kontaktu.
contact_entities_view
Kolejny przydatny widok czy tabel nieprzetworzonych kontaktw z tabel danych. Dziki
niemu moemy odczytywa jednoczenie wszystkie elementy danych okrelonego, nieprzetworzonego kontaktu, a nawet dane bdce czci wielu nieprzetworzonych kontaktw skadajcych si na jeden kontakt zbiorczy. Na listingu 27.12 widzimy definicj widoku tych encji.
Listing 27.12. Widok encji kontaktw
CREATE VIEW contact_entities_view AS
SELECT raw_contacts.account_name AS account_name,
raw_contacts.account_type AS account_type,
raw_contacts.sourceid AS sourceid,
raw_contacts.version AS version,
raw_contacts.dirty AS dirty,
Eksploracja kont
Rozpoczniemy wiczenia od napisania programu wywietlajcego list dostpnych kont.
Do tego zadania bd nam potrzebne nastpujce pliki:
TestContactsDriverActivity.java gwna aktywno sterujca, o ktrej bdziemy
wspomina kilkakrotnie w dalszej czci rozdziau. Aktywno ta zawiera zestaw
elementw menu przywoujcych poszczeglne przykady.
DebugActivity.java podstawowa klasa aktywnoci sterujcej, ukrywajca kilka
szczegw implementacji, ktrych znajomo nie jest wymagana do zrozumienia
koncepcji interfejsu kontaktw.
973
Plik menu
Plik menu z listingu 27.13 musi nosi nazw main_menu.xml oraz zosta umieszczony w podkatalogu res/menu naszego projektu.
Listing 27.13. Plik gwnego menu naszego projektu
<menu xmlns:android="http://schemas.android.com/apk/res/android">
Na tym etapie umieszczamy w pliku tylko dwa elementy menu. W trakcie tworzenia dalszych
przykadw bdziemy wstawia do niego kolejne obiekty. Pierwszy element menu pozwala na
wywietlanie listy dostpnych kont, drug opcj jest natomiast przydatna funkcjonalno oglnego przeznaczenia, suca do usuwania komunikatw debugowania lub informacji pochodzcych z testowej aktywnoci sterujcej.
Interfejs ten jest kontraktem dla dziedziczonych klientw, umoliwiajcym im wysyanie komunikatw informacyjnych oraz debugowania. Miejsce i sposb wywietlania tych komunikatw
nie nale do zada klientw.
BaseTester.java
Wszystkie funkcje testowe bd posiaday dostp do interfejsu IReportBack.java, dziki czemu
bd mogy generowa komunikaty po ich wywoaniu za pomoc elementu menu. Gwarantuje
nam to bazowa klasa dla wszystkich prezentowanych funkcji, zwana BaseTester. Jej kod
rdowy zosta zademonstrowany na listingu 27.15.
Listing 27.15. Kod rdowy klasy BaseTester
public class BaseTester
{
protected IReportBack mReportTo;
protected Context mContext;
public BaseTester(Context ctx, IReportBack target)
{
mReportTo = target;
mContext = ctx;
}
}
Klasa BaseTester przechowuje interfejs IReportBack oraz odniesienie do kontekstu (zazwyczaj jest nim nadrzdna klasa sterujca). Te dwie zmienne s wykorzystywane przez pochodne
funkcje testowe.
AccountsFunctionTester.java
Zaprezentujemy teraz pierwsz z omawianych funkcji testowych,
ktrej kod umieszczono na listingu 27.16.
AccountsFunctionTester,
975
AccountManager am = AccountManager.get(this.mContext);
Account[] accounts = am.getAccounts();
for(Account ac: accounts)
{
String acname=ac.name;
String actype = ac.type;
this.mReportTo.reportBack(tag,acname + ":" + actype);
}
}
}
Kod widoczny na listingu 27.16 jest raczej nieskomplikowany. Na pocztku rozdziau omwilimy zagadnienie kont oraz mechanizm wywietlania ich w postaci listy. Kod z listingu 27.16
pobiera jedynie nazw oraz typ kadego konta, a nastpnie wywouje interfejs raportujcy,
dziki ktremu zostan wywietlone wyniki. Dopki istnieje aktywno sterujca, ktra moe
wywoa metod testAccounts(), powyszy kod moe przekazywa nazw i typ konta. Zastanwmy si teraz nad klasami zwizanymi z aktywnoci sterujc.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.debug_activity_layout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(menuId, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
appendMenuItemText(item);
if (item.getItemId() == R.id.menu_da_clear){
this.emptyText();
return true;
}
return onMenuItemSelected(item);
}
private TextView getTextView(){
return (TextView)this.findViewById(R.id.text1);
}
protected void appendMenuItemText(MenuItem menuItem){
String title = menuItem.getTitle().toString();
TextView tv = getTextView();
tv.setText(tv.getText() + "\n" + title);
}
protected void emptyText(){
TextView tv = getTextView();
tv.setText("");
}
private void appendText(String s){
TextView tv = getTextView();
tv.setText(tv.getText() + "\n" + s);
Log.d(tag,s);
}
public void reportBack(String tag, String message)
{
this.appendText(tag + ":" + message);
Log.d(tag,message);
}
public void reportTransient(String tag, String message)
{
String s = tag + ":" + message;
Toast mToast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
mToast.show();
reportBack(tag,message);
Log.d(tag,message);
}
977
debug_layout_activity.xml
Widoczny na listingu 27.18 plik debug_layout_activity.xml musi zosta umieszczony w podkatalogu /res/layout.
Listing 27.18. Plik ukadu graficznego debuggera debug_layout_activity.xml
<?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"
>
<TextView
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Tutaj pojawia si tekst debugowania"
/>
</LinearLayout>
TestContactsDriverActivity.java
Listing 27.19 prezentuje gwn aktywno sterujc, ktra koordynuje dziaanie poszczeglnych elementw menu z wywoywaniem odpowiednich metod, umieszczonych w funkcjach
testowych.
Listing 27.19. Gwna aktywno sterujca
public class TestContactsDriverActivity
extends DebugActivity
implements IReportBack
{
public static final String tag="Test kontaktw";
AccountsFunctionTester accountsFunctionTester = null;
public TestContactsDriverActivity()
{
super(R.menu.main_menu,tag);
accountsFunctionTester = new AccountsFunctionTester(this,this);
}
protected boolean onMenuItemSelected(MenuItem item)
{
Log.d(tag,item.getTitle().toString());
if (item.getItemId() == R.id.menu_show_accounts)
{
accountsFunctionTester.testAccounts();
return true;
}
return true;
}
}
Uruchomienie programu
Listing 27.21 zawiera spis plikw wymaganych do skompilowania i uruchomienia naszego prostego przykadu.
Listing 27.21. Kompletny spis plikw tworzcych pierwsz aplikacj testow
IReportBack.java
BaseTester.java
AccountsFunctionTester.java
DebugActivity.java
TestContactsDriverActivity.java
/res/menu/main_menu.xml
/res/layout/debug_layout_activity.xml
AndroidManifest.xml
979
Menu pokazane na rysunku 27.15 zawiera dwie opcje. Opcja wyczy stanowi standardowy
element menu, zdefiniowany w bazowej klasie aktywnoci debugowania, za pomoc ktrego
wyzerujemy zawarto widoku tekstowego. Opcja Konta spowoduje wywietlenie listy kont
dostpnych w urzdzeniu. Przekonajmy si, co si stanie po jego klikniciu. Pojawi si ekran
widoczny na rysunku 27.16.
981
Pierwsza funkcja, getColumnValue(), powraca z wartoci kolumny, pobierajc jej nazw z biecego wiersza kursora. Bez wzgldu na podstawowy typ danych kolumny funkcja ta przekazuje
t warto w postaci cigu znakw.
Druga funkcja jest bardzo przydatna. Pobiera ona dowolny kursor i przekazuje osobn list
wszystkich jego kolumn. Jest to uyteczne zwaszcza w przypadku badania nowych identyfikatorw URI pod ktem rodzajw pl, ktre okrelaj. Chocia mona udokumentowa te
kolumny w kodzie Java, wspomniana metoda ich odkrywania w czasie dziaania aplikacji moe
si przyda w pewnych sytuacjach.
Poniewa ten i nastpne przykady wykorzystuj koncepcj wysyania identyfikatora URI i odbierania kursora za pomoc aktywnoci, umiecilimy funkcje realizujce te zadania w bazowej
klasie URIFunctionTester. Na listingu 27.23 zamiecilimy kod rdowy tej klasy, po czym
opisalimy kad dostpn w niej metod.
Listing 27.23. Klasa bazowa, umoliwiajca analizowanie funkcji zwizanych z identyfikatorami URI
public class URIFunctionTester extends BaseTester
{
protected static String tag = "tc>";
public URIFunctionTester(Context ctx, IReportBack target)
{
super(ctx, target);
}
protected Cursor getACursor(String uri,String clause)
{
// Uruchamia kwerend
Activity a = (Activity)this.mContext;
return a.managedQuery(Uri.parse(uri), null, clause, null, null);
}
protected Cursor getACursor(Uri uri,String clause)
{
// Uruchamia kwerend
Activity a = (Activity)this.mContext;
return a.managedQuery(uri, null, clause, null, null);
}
protected void printCursorColumnNames(Cursor c)
{
this.mReportTo.reportBack(tag,Utils.getCursorColumnNames(c));
}
}
Kod z listingu 27.24 wcale nie jest skomplikowany. Wykorzystalimy tu kursor do wczytania
interesujcych nas pl. Zaprezentujemy teraz na listingu 27.25 klas AggregatedContactFunction
Tester, ktra pomoe nam wypeni zadania ustalone na pocztku tego podrozdziau.
Listing 27.25. Kod umoliwiajcy testowanie kontaktw zbiorczych
public class AggregatedContactFunctionTester extends URIFunctionTester
{
public AggregatedContactFunctionTester(Context ctx, IReportBack target)
{
super(ctx, target);
}
/*
* Pobiera kursor ze wszystkich kontaktw
* Bez klauzuli where
* Nie stosujmy tego w przypadku duego zbioru
*/
private Cursor getContacts()
{
// Uruchamia kwerend
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
Activity a = (Activity)this.mContext;
return a.managedQuery(uri, null, null, null, sortOrder);
/*
* Wykorzystuje powysz metod getContacts
* do utworzenia spisu kolumn zawartych w kursorze
*/
public void listContactCursorFields()
{
Cursor c = null;
try
{
c = getContacts();
int i = c.getColumnCount();
this.mReportTo.reportBack(tag, "Liczba kolumn:" + i);
this.printCursorColumnNames(c);
}
finally
{
if (c!= null) c.close();
}
}
/*
* Przy uyciu kursora wypenionego kontaktami
* zostaj wywietlone nazwy kontaktw
* wraz z ich kluczami wyszukiwania
*/
private void printLookupKeys(Cursor c)
{
for(c.moveToFirst();!c.isAfterLast();c.moveToNext())
{
String name=this.getContactName(c);
String lookupKey = this.getLookupKey(c);
String luri = this.getLookupUri(lookupKey);
this.mReportTo.reportBack(tag, name + ":" + lookupKey);
this.mReportTo.reportBack(tag, name + ":" + luri);
}
}
/*
* Wykorzystuje funkcj getContacts()
* do uzyskania kursora i wywietlenia wszystkich
* nazw kontaktw wraz z kluczami ich wyszukiwania
* Stosuje funkcj printLookupKeys()
*/
public void listContacts()
{
Cursor c = null;
try
{
c = getContacts();
int i = c.getColumnCount();
this.mReportTo.reportBack(tag, "Liczba kolumn:" + i);
this.printLookupKeys(c);
983
/*
* Funkcja uytkowa odczytujca
* klucz wyszukiwania z kursora kontaktu
*/
private String getLookupKey(Cursor cc)
{
int lookupkeyIndex = cc.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
return cc.getString(lookupkeyIndex);
}
/*
* Funkcja uytkowa odczytujca
* wywietlan nazw z kursora kontaktu
*/
private String getContactName(Cursor cc)
{
return Utils.getColumnValue(cc,ContactsContract.Contacts.DISPLAY_NAME);
}
/**
* Konstruuje identyfikator wyszukiwania na podstawie
* identyfikatora URI kontaktw i klucza wyszukiwania
*/
private String getLookupUri(String lookupkey)
{
String luri = ContactsContract.Contacts.CONTENT_LOOKUP_URI + "/" + lookupkey;
return luri;
}
/**
* Wykorzystuje identyfikator URI wyszukiwania
* do odczytania pojedynczego kontaktu zbiorczego
*/
private Cursor getASingleContact(String lookupUri)
{
// Uruchamia kwerend
Activity a = (Activity)this.mContext;
return a.managedQuery(Uri.parse(lookupUri), null, null, null, null);
}
/*
* Funkcja sprawdzajca, czy identyfikator URI stworzony za pomoc identyfikatora
* URI wyszukiwania zwraca kursor zawierajcy inny zestaw kolumn.
* Jak mona byo si spodziewa, zwracany jest podobny kursor
* zawierajcy podobny zbir kolumn.
*/
/*
* Pobiera list kontaktw
* Wyszukuje pierwszy kontakt
* Przekazuje warto null, jeli nie znajdzie adnego kontaktu
*/
private String getFirstLookupUri(Cursor c)
{
c.moveToFirst();
if (c.isAfterLast())
{
Log.d(tag,"Brak wierszy, z ktorych mozna pobrac pierwszy kontakt");
return null;
}
//Znaleziono wiersz
String lookupKey = this.getLookupKey(c);
String luri = this.getLookupUri(lookupKey);
return luri;
}
/*
* Pobiera list kontaktw
* Wyszukuje pierwszy kontakt i zwraca go
985
//Znaleziono kontakt
AggregatedContact firstcontact = new AggregatedContact();
firstcontact.fillinFrom(c);
return firstcontact;
}
finally
{
if (c!=null) c.close();
}
}
}
Gwne funkcje publiczne zostay wyrnione pogrubion czcionk. Przeznaczenie kadej z nich
zostao wyjanione w przypisanym do niej komentarzu. Po utworzeniu tej funkcji testowej dodajmy elementy menu z listingu 27.26 do pliku menu (/res/menu/main_menu.xml).
Listing 27.26. Elementy menu zwizane z funkcj testow kontaktw zbiorczych
<item android:id="@+id/menu_show_contact_cursor"
android:title="kursor kontaktw" />
<item android:id="@+id/menu_show_contacts"
android:title="kontakty" />
<item android:id="@+id/menu_show_single_contact_cursor"
android:title="kursor pojedynczego kontaktu" />
Moemy wstawi je w dowolnym miejscu pliku main_menu.xml, proponujemy jednak umieszczenie ich w pocztkowej czci kodu, dziki czemu nowsze funkcje bd wywietlane na pocztku menu. Po dodaniu opcji menu modyfikujemy aktywno sterujc w taki sposb, eby
przypominaa kod z listingu 27.27.
Listing 27.27. Gwna aktywno sterujca dostosowana do testowania kontaktw zbiorczych
public class TestContactsDriverActivity extends DebugActivity
implements IReportBack
{
public static final String tag="TestContactsDriverActivity ";
AccountsFunctionTester accountsFunctionTester = null;
987
Zwrmy uwag na trzy publiczne funkcje, ktre s wywoywane w wyniku wcinicia odpowiednich opcji menu:
listContactCursorFields(),
listContacts(),
listLookupUriColumns().
Omwimy dziaanie tych funkcji, opierajc si na kodzie umieszczonym na listingu 27.26.
Funkcja listContactCursorFields odczytuje ca list kontaktw i wywietla w kursorze
nazwy kolumn. Identyfikatorem URI sucym do odczytywania wszystkich kontaktw jest
ContactsContract.Contacts.CONTENT_URI.
W celu odczytania kursora przekazujemy ten identyfikator URI metodzie managedQuery().
Moemy przekaza warto null w trakcie rzutowania kolumn, aby wywietli wszystkie
kolumny. Chocia nie jest to zalecane rozwizanie, w naszym przypadku jest ono logiczne, gdy
chcemy pozna wszystkie kolumny przekazane przez identyfikator URI. Na listingu 27.28
widzimy spis wszystkich kolumn otrzymywanych dziki temu identyfikatorowi.
Nasz przykadowy program wygeneruje list tych kolumn zarwno w widoku programu, jak
rwnie w oknie LogCat. Skopiowalimy te pola z okna LogCat i sformatowalimy w sposb
widoczny na listingu 27.28.
Podczas pracy z dostawcami treci technika polegajca na korzystaniu z identyfikatora
URI oraz wywietlaniu przekazywanych kolumn moe si okaza bardzo przydatna.
989
wskazujcego kontakt, ktrego dotyczy ten klucz wyszukiwania. Zauwamy rwnie, e wprowadzilimy nastpujc definicj identyfikatora URI wyszukiwania:
ContactsContract.Contacts.CONTENT_LOOKUP_URI
Podczas dyskusji na temat identyfikatorw URI wyszukiwania stwierdzilimy, e kady tego typu
obiekt symbolizuje zbir poczonych ze sob, nieprzetworzonych kontaktw. W takim przypadku powinnimy si spodziewa, e otrzymamy zestaw takich samych nieprzetworzonych
kontaktw. Powyszy test (listing 27.28) udowadnia nam jednak, e nie dostajemy kursora
zawierajcego nieprzetworzone kontakty, lecz kursor przechowujcy kontakty zbiorcze.
W efekcie wyszukiwania opartego na identyfikatorze wyszukiwania kontaktu
otrzymujemy kontakt zbiorczy, a nie kontakt nieprzetworzony.
Kolejn istotn i ciekaw cech procesu wyszukiwania kontaktu zbiorczego opartego na identyfikatorze wyszukiwania jest to, e nie zachodzi w sposb liniowy i e nie jest dokadny. Oznacza to, e system nie bdzie poszukiwa trafienia dokadnie odpowiadajcego kluczowi dopasowania. Zamiast tego Android analizuje skadni klucza wyszukiwania pod ktem tworzcych go
nieprzetworzonych kontaktw, nastpnie odnajduje identyfikator kontaktu zbiorczego, ktry
jest zgodny z wikszoci rekordw nieprzetworzonych kontaktw, i przekazuje rekord kontaktu zbiorczego utworzonego w ten sposb.
Jedn z konsekwencji tego mechanizmu jest brak moliwoci publicznego przejcia od klucza
wyszukiwania do nieprzetworzonych kontaktw, ktre stanowi jego skadowe. Jedynym rozwizaniem jest odnalezienie identyfikatora kontaktu zwizanego z kluczem wyszukiwania,
a nastpnie uruchomienie obiektu URI nieprzetworzonego kontaktu wobec identyfikatora
znalezionego kontaktu, dziki czemu zostan odczytane jego nieprzetworzone kontakty.
Kady z tych elementw menu powoduje wywoanie trzech funkcji umieszczonych w pliku
RawContactFunctionTester.java. Zawarty w tym pliku kod jest widoczny na listingu 27.31.
Listing 27.31. Testowanie nieprzetworzonych kontaktw
public class RawContactsFunctionTester
extends AggregatedContactFunctionTester
{
public RawContactsFunctionTester(Context ctx, IReportBack target)
{
super(ctx, target);
}
991
Na listingu 27.32 znalaz si zaktualizowany kod aktywnoci sterujcej, przejmujcej od elementw menu proces wywoywania funkcji publicznych pochodzcych z testera funkcji nieprzetworzonych kontaktw.
Listing 27.32. Zaktualizowana aktywno sterujca, pozwalajca na testowanie
nieprzetworzonych kontaktw
public class TestContactsDriverActivity extends DebugActivity
implements IReportBack
{
//........kontynuacja
RawContactsFunctionTester rawContactFunctionTester = null;
public TestContactsDriverActivity()
{
//........kontynuacja
rawContactFunctionTester = new RawContactsFunctionTester(this,this);
}
protected boolean onMenuItemSelected(MenuItem item)
{
//........kontynuacja
if (item.getItemId() == R.id.menu_show_single_contact_cursor)
{
aggregatedContactFunctionTester.listLookupUriColumns();
return true;
}
993
return true;
}
}
Na powyszym listingu zaprezentowalimy jedynie nowe wiersze, ktre naley wstawi do aktywnoci sterujcej, gdy jest to jeden z aktualizowanych plikw.
Podobnie jak zrobilimy w przypadku kontaktw zbiorczych, przyjrzyjmy si najpierw naturze
identyfikatorw URI nieprzetworzonych kontaktw oraz przekazywanym przez nie wartociom. Sygnatura identyfikatora nieprzetworzonego kontaktu wyglda nastpujco:
ContactsContract.RawContacts.CONTENT_URI;
Skoro zapoznalimy si z kolumnami kursora nieprzetworzonych kontaktw, mog nas zainteresowa rwnie wiersze tej tabeli. Kliknijmy teraz opcj menu wszystkie nieprzetworzone
kontakty. Zostanie wywoana metoda showAllRawContacts(). Metoda ta bdzie manipulowaa
kursorem bez uycia klauzuli WHERE (dziki czemu uzyskamy dostp do wszystkich wierszy)
oraz utworzy obiekt RawContact dla kadego wiersza, po czym wywietli wyniki. Lista nieprzetworzonych kontaktw zostanie ukazana w widoku aplikacji oraz w oknie LogCat.
Za pomoc widocznych na listingu 27.33 kolumn kursora sprawdmy, czy moemy zmodyfikowa kwerend w taki sposb, aby odczytywa kontakty za pomoc danego identyfikatora
kontaktw zbiorczych. Przetestujemy tak moliwo, klikajc element menu nieprzetworzone
995
Aktywno sterujca musi zosta zmodyfikowana zgodnie z zawartoci listingu 27.37, gdy
w przeciwnym wypadku nie bdzie reagowaa na wcinicia wspomnianych elementw menu
i nie bdzie wywoywaa funkcji klasy ContactDataFunctionTester.
Listing 27.37. Zaktualizowana aktywno sterujca, pozwalajca na testowanie danych kontaktu
public class TestContactsDriverActivity extends DebugActivity
implements IReportBack
{
public static final String tag="TestContacts";
997
Przeanalizujmy teraz powyszy kod i cay przykadowy program. Android ustanawia specjalny
widok, RawContactEntity, sucy do odczytywania danych z tabeli nieprzetworzonych kontaktw oraz z odpowiadajcych jej tabel danych, co zostao zaprezentowane w punkcie dotyczcym widoku Contact_entities_view. Identyfikator URI uzyskujcy dostp do tego widoku
zosta zdefiniowany w pomocniczej klasie. Pena cieka staej tego identyfikatora zostaa zaprezentowana na listingu 27.38.
Listing 27.38. Identyfikator treci nieprzetworzonego kontaktu
ContactsContract.RawContactsEntity.CONTENT_URI
Powyszy identyfikator jest wykorzystywany w omawianym programie do uzyskiwania informacji na temat przekazywanych pl. Spis tych pl otrzymamy po wciniciu opcji menu kursor
encji kontaktu. Listing 27.39 prezentuje spis kolumn uzyskiwany po klikniciu wspomnianego
elementu menu.
Listing 27.39. Kolumny kursora encji kontaktu
data_version;
contact_id;
version;
data12;data11;data10;
mimetype;
res_package;
_id;
data15;data14;data13;
name_verified;
is_restricted;
is_super_primary;
data_sync1;dirty;data_sync3;data_sync2;
data_sync4;account_type;data1;sync4;sync3;
data4;sync2;data5;sync1;
data2;data3;data8;data9;
deleted;
group_sourceid;
data6;data7;
account_name;
data_id;
starred;
sourceid;
is_primary;
Po zapoznaniu si z tym zestawem kolumn moemy zawzi wyniki przekazywane przez ten
kursor poprzez sformuowanie klauzuli WHERE. Przykadowo po klikniciu nastpnego elementu
menu uzyskamy elementy danych, ktrym przypisano identyfikatory kontaktu o wartociach
3, 4 i 5. W tym celu wystarczyo doda w kodzie nastpujc klauzul WHERE:
"contact_id in (3,4,5)"
i wysa j wraz z kursorem. Dokadnie taka operacja zostaa powizana z obiektem menu dane
kontaktu. Po jego wciniciu zostan wywietlone takie informacje, jak imi i nazwisko oraz adres e-mail (element danych rozpoznajemy po jego typie MIME).
999
this.mContext.getContentResolver().insert(Data.CONTENT_URI, cv);
}
private long insertRawContact()
{
ContentValues cv = new ContentValues();
cv.put(RawContacts.ACCOUNT_TYPE, "com.google");
cv.put(RawContacts.ACCOUNT_NAME, "satya.komatineni@gmail.com");
Uri rawContactUri =
this.mContext.getContentResolver()
.insert(RawContacts.CONTENT_URI, cv);
long rawContactId = ContentUris.parseId(rawContactUri);
return rawContactId;
}
private void showRawContactsDataForRawContact(long rawContactId)
{
Cursor c = null;
try
{
Uri uri = ContactsContract.RawContactsEntity.CONTENT_URI;
c = this.getACursor(uri,"_id = " + rawContactId);
this.printRawContactsData(c);
}
finally
{
if (c!=null) c.close();
}
}
}
Jedyn funkcj publiczn jest addContact(). W celu jej wywoania potrzebny nam bdzie element menu zamieszczony na listingu 27.41.
Listing 27.41. Element menu pozwalajcy na dodawanie kontaktu
<item android:id="@+id/menu_add_contact"
android:title="Dodaj kontakt" />
...inne mechanizmy
AddContactFunctionTester addContactFunctionTester = null;
public TestContactsDriverActivity()
{
...inne mechanizmy
if (item.getItemId() == R.id.menu_add_contact)
{
addContactFunctionTester.addContact();
return true;
}
return true;
}
}
Jeeli klikniemy teraz opcj menu Dodaj kontakt, kod zawarty na listingu 27.40 (funkcja testowa dodawania kontaktw) wykona nastpujce czynnoci:
1. Najpierw doda do predefiniowanego konta (korzystajc z jego nazwy i typu)
nieprzetworzony kontakt za pomoc metody insertRawContact().
2. Pobierze identyfikator nieprzetworzonego kontaktu i wstawi w tabeli danych rekord
imienia i nazwiska (metoda insertName()).
3. Znowu pobierze identyfikator nieprzetworzonego kontaktu i wstawi w tabeli danych
rekord numeru telefonu (metoda insertPhoneNumber()).
Na listingu 27.40 widzimy aliasy kolumn wykorzystywane przez wymienione metody podczas
wstawiania rekordw. Umiecilimy je rwnie na listingu 27.43, aby uproci ich przegldanie.
Listing 27.43. Uywanie aliasw kolumn w standardowych strukturach danych kontaktu
cv.put(Data.RAW_CONTACT_ID, rawContactId);
cv.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
cv.put(StructuredName.DISPLAY_NAME,"Gall Anonim_" + rawContactId);
cv.put(Data.RAW_CONTACT_ID, rawContactId);
cv.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
cv.put(Phone.NUMBER,"123 123 " + rawContactId);
cv.put(Phone.TYPE,Phone.TYPE_HOME);
cv.put(RawContacts.ACCOUNT_TYPE, "com.google");
cv.put(RawContacts.ACCOUNT_NAME, "satya.komatineni@gmail.com");
Szczeglnie istotne jest, aby pamita, e takie stae, jak Phone.TYPE czy Phone.NUMBER, odnosz si w rzeczywistoci do nazw kolumn data1 i data2 umieszczonych w tabeli danych.
Aby w kocu ujrze dodany rekord, kliknijmy element menu Dodaj kontakt. Zostan dodane
i wywietlone szczegowe informacje tego rekordu, gdy zostan one odczytane za pomoc
funkcji showRawContactsDataForRawContact(). Kade z pl danych bdzie umieszczone
w strukturze ContactData.
1001
Kontrola agregacji
W tej chwili dla Czytelnika powinno ju by jasne, e klienty aktualizujce lub dodajce kontakty nie modyfikuj tabeli contact w jawny sposb. Tabela ta jest modyfikowana przez obiekty
wyzwalajce, ktre ledz tabel nieprzetworzonych kontaktw oraz tabel danych.
Z kolei dodawane lub zmieniane nieprzetworzone kontakty oddziauj na kontakty zbiorcze
znajdujce si w tabeli kontaktw. Niekiedy jednak powizanie dwch kontaktw ze sob jest
niekorzystne.
Moemy kontrolowa proces czenia nieprzetworzonych kontaktw poprzez ustalenie trybu
agregacji w czasie ich tworzenia. Jak wida po umieszczonych na listingu 27.33 nazwach
kolumn tabeli nieprzetworzonych kontaktw, zawiera ona pole aggregation_mode. Wartoci umieszczone w tym polu zostay wymienione na listingu 27.2 i objanione w punkcie
Kontakty zbiorcze.
Moemy rwnie zapewni, by dwa kontakty pozostaway rozdzielone, poprzez umieszczenie
odpowiednich wierszy w tabeli agg_exceptions. Identyfikatory URI wymagane do wstawiania
danych do tej tabeli s zdefiniowane w klasie ContactsContract.AggregationExceptions.
Struktura tabeli agg_exceptions zostaa zaprezentowana na listingu 27.44.
Listing 27.44. Definicja tabeli zawierajcej wyjtki agregacji
CREATE TABLE agg_exceptions
(_id INTEGER PRIMARY KEY AUTOINCREMENT,
type INTEGER NOT NULL,
raw_contact_id1 INTEGER REFERENCES raw_contacts(_id),
raw_contact_id2 INTEGER REFERENCES raw_contacts(_id))
Rwnie stae wykorzystywane wraz z definicjami pl w tej tabeli s dostpne w klasie Contacts
Contract.AggregationExceptions.
Konsekwencje synchronizacji
Przez wikszo rozdziau zajmowalimy si wycznie manipulowaniem kontaktami w obrbie
urzdzenia. Zazwyczaj jednak konta i przypisane im kontakty s synchronizowane cznie. Jeli
na przykad utworzylimy konto Google w telefonie obsugiwanym przez system Android,
wszystkie kontakty zostan skopiowane z serwera do tego telefonu.
Za kadym razem, gdy w urzdzeniu dodajemy nowy kontakt lub konto serwerowe, nastpi
proces synchronizacji i odzwierciedlenia danego obiektu w obydwu miejscach.
Jednak w tym wydaniu ksiki nie zajlimy si omwieniem interfejsu synchronizowania ani
mechanizmem jego dziaania. Jest to zagadnienie rwnie obszerne jak tematyka kontaktw.
Znajomo dziaania interfejsu kontaktw znacznie pomaga w zrozumieniu interfejsu synchronizacji. Zalecamy wic zagldanie na stron www.androidbook.com, ktra jest do regularnie aktualizowana.
Natura mechanizmu synchronizacji wpywa rwnie na proces usuwania kontaktw z urzdzenia. W czasie usuwania kontaktu za pomoc identyfikatora kontaktu zbiorczego zostan
wykasowane wszystkie zwizane z nim nieprzetworzone kontakty, a take elementy danych powizane z tymi kontaktami. Jednak system jedynie oznacza te obiekty jako usunite i dopiero
w procesie przebiegajcej w tle synchronizacji z serwerem zaznaczone kontakty zostan trwale
usunite z urzdzenia. Taka kaskada procesw kasowania wystpuje take na poziomie nieprzetworzonych kontaktw, gdzie zostaj usunite elementy danych powizane z danym nieprzetworzonym kontaktem.
Odnoniki
Zaprezentowane poniej odnoniki umoliwi Czytelnikowi dostp do materiaw pomocniczych
oraz rozszerzajcych zakres informacji zawartych w tym rozdziale. Ostatni z zamieszczonych
adresw URL umoliwia pobranie projektw utworzonych specjalnie na potrzeby tego rozdziau.
http://www.google.com/support/mobile/bin/answer.py?answer=182077 adres instrukcji
obsugi Androida w wersji 2.3. Znajdziemy w niej informacje dotyczce aplikacji
Kontakty, pozwalajcej na zarzdzanie kontaktami. Chocia omwilimy podstawowe
informacje zwizane z obsug tej aplikacji, najwaniejsza w tej kwestii pozostaje
instrukcja obsugi. Czytelnik moe w niej znale informacje, ktre my moglimy
przypadkowo przeoczy.
http://www.google.com/help/hc/pdfs/mobile/AndroidUsersGuide-30-100.pdf instrukcja
obsugi Androida w wersji 3.0.
http://developer.android.com/resources/articles/contacts.html ten adres URL
prowadzi do artykuu, w ktrym omwiono sposb korzystania z interfejsu kontaktw.
Jest to podstawowa dokumentacja dotyczca interfejsu kontaktw, stworzona przez
firm Google.
http://www.androidbook.com/item/3585 zrozumienie koncepcji interfejsu
kontaktw polega przede wszystkim na pojciu struktury tworzcych go tabel. Klasa
ContactsContract stanowi jedynie cienk oson wok podstawowej struktury
tabel. Pod tym adresem mona znale informacje o rnorodnych strukturach tabel
opracowanych przez autorw ksiki. Znajdziemy tam nazwy pl, ich typy, widoki
kontaktw zbiorczych i tak dalej.
1003
http://developer.android.com/reference/android/provider/ContactsContract.html
dokumentacja Javadoc opisujca klas wejciow opublikowanego kontraktu
kontaktw. Bardzo przydatny adres dla osb zajmujcych si pisaniem programw
wykorzystujcych interfejs kontaktw.
http://www.netmite.com/android/mydroid/2.0/packages/providers/ContactsProvider/
z powodu niedostatku informacji dotyczcych obsugi kontaktw by moe
Czytelnika zainteresuje kod rdowy dostawcy kontaktw. Pod tym adresem
znajdziemy stron Netmite, gdzie zamieszczono kody rdowe wszystkich plikw
tworzcych dostawc treci.
http://www.netmite.com/android/mydroid/2.0/packages/apps/Contacts/src/com/andr
oid/contacts podobnie jak w poprzednim przypadku, cze to prowadzi do kodu
rdowego aplikacji Kontakty. Jeeli Czytelnik chce pozna mechanizm tworzenia
lub aktualizowania kontaktu zbiorczego, wanie znalaz y zota.
http://www.androidbook.com/item/3537 jeeli Czytelnik przeglda kody rdowe
dostpne pod dwoma powyszymi adresami, prawdopodobnie poczu si nieco
oguszony. Pod tym adresem znajdziemy wic podsumowanie danych tam zawartych,
ktre by moe komu si przyda.
ftp://ftp.helion.pl/przyklady/and3ta.zip pod tym adresem znajdziemy projekty
utworzone specjalnie na potrzeby niniejszej ksiki. Interesujcy nas katalog nosi
nazw ProAndroid3_R27_Kontakty.
Podsumowanie
W niniejszym rozdziale zapoznalimy si ze struktur kontaktw, dostpn w systemie Android.
Moemy wykorzysta zawarte tu informacje do odczytywania lub aktualizowania kontaktw za
pomoc publicznego interfejsu kontaktw.
Chocia powicilimy mnstwo uwagi interfejsowi kontaktw, nie omwilimy pracy z dostawcami treci pracujcymi w trybie wsadowym, w ktrym mona dodawa lub usuwa kontakty. Zestaw Android SDK zawiera klas ContentProviderOperation umoliwiajc przeprowadzenie procesw wstawiania, aktualizowania i usuwania kontaktw w trybie wsadowym,
co pozwala na optymalizacj dziaania systemu.
Tryb wsadowy staje si tym istotniejszy dla dostawcw synchronizacji, im wiksza liczba kontaktw jest aktualizowana i dodawana. W przypadku kwerend oraz sporadycznych aktualizacji
omwione w tym rozdziale rozwizania s cakowicie wystarczajce. Warto jednak co jaki czas
zaglda na stron www.androidbook.com.
R OZDZIA
28
Wdraanie aplikacji na rynek
Android Market i nie tylko
1007
1009
Firma Google wymaga przestrzegania cech marki Android. Do tych cech zaliczaj si
ograniczenia w wykorzystywaniu sowa Android, jak rwnie ikony robota, znaku
towarowego oraz kroju pisma. Informacje na ten temat znajdziemy pod adresem
http://www.android.com/branding.html.
Konsola programisty
Konsola programisty jest nasz stron docelow, pozwalajc na kontrolowanie aplikacji umieszczonych w serwisie Android Market. Z poziomu konsoli programisty moemy zakupi urzdzenie ADP (ang. Android Developer Phone telefon programisty systemu Android), skonfigurowa konto handlowe w usudze Google Checkout (dziki czemu moemy nalicza opat
za aplikacj), publikowa aplikacje oraz przeglda informacje na temat opublikowanych programw. Moemy take edytowa takie szczegy konta, jak imi i nazwisko programisty, adres
e-mail, adres strony WWW oraz numer telefonu. Konsola programisty zostaa zaprezentowana
na rysunku 28.1.
Rysunek 28.1. Konsola programisty pozwalajca uzyska dostp do serwisu Android Market
Obecnie istniej trzy rodzaje telefonw testowych obsugujcych system Android: Android Developer Phone, Google Nexus One oraz Google Nexus S. Android Developer Phone (ADP) by
przez dugi czas jedynym telefonem pozwalajcym na testowanie programowanych aplikacji.
Jest to specjalne urzdzenie, zaprojektowane przede wszystkim dla programistw aplikacji dla
systemu Android. Jest to profesjonalny telefon, posiadajcy odblokowane wszystkie funkcje,
niezaleny od operatorw telefonii komrkowej. Akceptuje wszystkie rodzaje kart SIM, a w jego
wyposaeniu znajduje si karta pamici 1 GB, aparat fotograficzny, wysuwana klawiatura
i system GPS. Piszc o odblokowanych funkcjach, mamy na myli moliwo wykonania kadej
1011
Prawdopodobnie najczciej wykorzystywanymi funkcjami konsoli programisty bd umieszczanie i monitorowanie aplikacji. W dalszej czci rozdziau zajmiemy si procesem publikowania aplikacji w sklepie. W kwestii monitoringu otrzymujemy narzdzia pozwalajce obserwowa cakowit liczb pobra aplikacji oraz liczb uytkownikw, ktrzy zainstalowali aplikacj.
Widoczna jest oglna ocena programu w zakresie od 0 do 5 gwiazdek, a take liczba osb,
ktre wystawiy ocen. Z poziomu konsoli programisty moemy ponownie opublikowa aplikacj na przykad jej aktualizacj lub wycofa j ze sklepu. Ta ostatnia czynno nie
usuwa aplikacji z urzdze, a nawet nie musi jej usuwa z serwerw Google, dotyczy to zwaszcza patnych aplikacji. Uytkownik, ktry zapaci za aplikacj, a nastpnie j odinstalowa, lecz
nie zada zwrotu kosztw, ma prawo do jej ponownego zainstalowania, nawet jeli zostaa
ona wycofana z obiegu. Program przestaje by naprawd dostpny dla uytkownikw jedynie
w przypadku zamania zasad firmy Google. W marcu 2011 roku firma Google dodaa w konsoli
programisty zestawienia i wykresy pozwalajce na obserwowanie statystyk aplikacji w zalenoci
od wersji systemu operacyjnego, rodzaju urzdzenia, a take krajw i jzykw.
Oprcz oceniania aplikacji uytkownicy mog rwnie pozostawia komentarze. Dla wasnego
dobra powinnimy jak najczciej czyta komentarze dotyczce naszej aplikacji, aby mc szybko
rozwizywa zauwaone problemy. Wraz z komentarzem dostpne s ocena aplikacji, nazwa
uytkownika oraz data umieszczenia komentarza. Niestety, nie moemy bezporednio odpowiada na komentarze ani nawet umieszcza komentarzy pod komentarzami uytkownikw.
W ekstremalnych przypadkach, gdy tre komentarza jest wyjtkowo szkodliwa lub niecenzuralna,
moemy zgosi to obsudze firmy Google, dostpnej pod adresem http://market.android.com/
support/.
Moemy rwnie przeglda komunikaty o bdach wygenerowane przez aplikacj oraz sprawdza, w ktrych momentach ulegaa zawieszeniu lub zostawaa niespodziewanie zamknita.
Na rysunku 28.2 widzimy ekran raportw o bdach aplikacji.
Przegldajc szczegy raportu, moemy dotrze do ladu stosu, w ktrym nastpia awaria, jak
rwnie uzyska takie informacje, jak rodzaj urzdzenia, na ktrym dziaaa aplikacja, oraz data
awarii. Podobnie jednak jak ma to miejsce w przypadku komentarzy, nie moemy skomunikowa si z uytkownikiem, ktrego aplikacja ulega awarii, aby pozna dalsze szczegy lub
1013
Wewntrz kodu aplikacji powinnimy wywoa klas PackageManager w celu okrelenia, czy
technologia Bluetooth jest dostpna, czego moemy dokona za pomoc poniszego fragmentu:
boolean hasBluetooth = getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH);
Nastpnie moemy wykona odpowiedni czynno, w zalenoci od obecnoci funkcji Bluetooth. Dokumentacja Androida w tym miejscu jest do niejednoznaczna. Jeeli zajrzymy do informacji o znaczniku <uses-feature> w konsoli programisty, nie znajdziemy tak wielu funkcji
jak w dokumentacji klasy PackageManager, w ktrej zostay zdefiniowane stae FEATURE_*
dla kadej dostpnej funkcji.
Znacznik <uses-configuration> jest nieco inny. Definiuje on rodzaje wymaganych elementw urzdzenia, takich jak rodzaj klawiatury, ekran dotykowy czy mechanizm sterowania.
Jednak w przeciwiestwie do wza <uses-feature> nie wprowadzamy tutaj niezalenych
wyborw, lecz tworzymy kombinacje konfiguracji wymaganych przez nasz aplikacj. Jeeli na
przykad nasz program wymaga obecnoci kontrolera ruchu w postaci podkadki kierunkowej
lub manipulatora kulkowego oraz ekranu dotykowego (wykorzystujcego rysik lub palec),
moemy zdefiniowa dwa nastpujce wiersze:
<uses-configuration android:reqFiveWayNav="true" android:reqTouchScreen="stylus" />
<uses-configuration android:reqFiveWayNav="true" android:reqTouchScreen="finger" />
Lokalizacja aplikacji
Jeli aplikacja bdzie wykorzystywana w innych krajach, moemy rozway proces jej lokalizacji.
Z technicznego punktu widzenia jest to wzgldnie prosta czynno. Znalezienie osoby odpowiedzialnej za przeprowadzenie tego procesu to zupenie inna sprawa. Z technicznego punktu
widzenia tworzymy po prostu kolejny folder w katalogu /res na przykad /res/values-fr przechowujcy francusk wersj pliku strings.xml. Nastpnie bierzemy plik strings.xml, tumaczymy
wartoci typu string na nowy jzyk i zachowujemy tak zmodyfikowany plik w nowym folderze
1015
zasobw, nie zmieniajc jednoczenie nazwy tego pliku. W przypadku innych rodzajw zasobw na przykad obiektw rysowanych lub menu stosujemy dokadnie tak sam technik.
Obrazy i kolory mog lepiej spenia swoje zadanie, jeli bd przystosowane do odmiennych
krajw i kultur. Z tego wanie powodu nie warto stosowa prawdziwych nazw zasobw kolorw.
W internetowej dokumentacji dotyczcej kolorw czsto mona natrafi na taki zapis:
<color name="solid_red">#f00</color>
Oznacza on, e w takim kodzie lub innym pliku zasobw odnosimy si do koloru za pomoc
jego rzeczywistej nazwy, w naszym przypadku jest to solid_red. Aby dostosowa kolor do innego
kraju lub kultury, najlepiej stosowa nazwy kolorw typu accent_color1 lub alert_color.
W Wielkiej Brytanii odpowiedniejszy moe by kolor czerwony, podczas gdy w Hiszpanii swoje
zadanie bdzie lepiej spenia jaki odcie ci. Poniewa nazwa alert_color nie okrela zastosowanego koloru, jego zmiana nie jest ju tak bardzo dezorientujca. Jednoczenie moemy
zaprojektowa przyjemny schemat kolorystyczny, zawierajcy barwy bazowe i ich odcienie,
majc jednoczenie pewno, e waciwe kolory zostay uyte we waciwych miejscach.
W niektrych krajach opcje menu powinny zosta zmienione poprzez dodanie lub usunicie
pewnych elementw, ewentualnie mona wprowadzi inn organizacj menu, w zalenoci od
miejsca stosowania aplikacji. Pliki menu s zazwyczaj przechowywane w katalogu /res/menu.
Jeeli natrafimy na tak sytuacj, prawdopodobnie bdzie lepiej, jeli umiecimy wszystkie tekstowe cigi znakw w pliku strings.xml lub innym pliku przechowywanym w podkatalogu
/res/values, a nastpnie bdziemy wszdzie odnosi si do tych zasobw za pomoc ich identyfikatorw. Zmniejszymy w ten sposb znacznie niebezpieczestwo pominicia tumaczenia
tekstu w jakim zapomnianym pliku zasobw. Tumaczenie jest wtedy zatem ograniczone do
plikw znajdujcych si w podkatalogu /res/values.
SIGNATURE_MATCH,
Kolejnym rozwizaniem umoliwiajcym zarabianie na aplikacjach jest wprowadzenie wewntrznych reklam. Istnieje mnstwo okazji, aby doczy reklamy do aplikacji. Dwoma powszechnie wystpujcymi moliwociami s AdMob i AdSense. Proces polega przede wszystkim
na wstawieniu ich pakietw SDK do naszej aplikacji, okreleniu odpowiedniego miejsca i czasu,
w ktrym bd wywietlane reklamy i dodaniu uprawnienia INTERNET do programu (dziki
czemu wywietlane reklamy bd pobierane z internetu). Na koniec bdziemy otrzymywa
pienidze za kade kliknicie reklamy. Sama aplikacja moe by bezpatna, dziki czemu atwiej
bdzie j umieci w serwisie Android Market, a do tego nie musimy si tak bardzo przejmowa
kwesti piractwa. Wielu programistw przyznaje, e zarabia w ten sposb przyzwoite pienidze.
Kolejn now funkcj jest wprowadzona w lutym 2011 roku waluta klienta (ang. Buyers Currency).
Przedtem klienci uytkownicy musieli paci w walucie sprzedawcy, co mogo stanowi
problem dla osb majcych kopoty z wymian waluty sprzedawcy na swoj wasn. Oznaczao
to rwnie, e sprzedawca mg tak naprawd ustali tylko jedn cen dla uytkownikw na
caym wiecie. Teraz, gdy sprzedawca moe ustali cen dla danego kraju, moe j nie tylko
podnosi lub obnia w zalenoci od rejonu geograficznego, lecz take komfort uytkownika
staje si wyranie wikszy.
1017
Za pomoc tego kodu zostanie uruchomiona aplikacja Market, ktra wywietli uytkownikowi
nazw tego pakietu. Uytkownik moe wtedy pobra lub zakupi aplikacj. Zwrmy uwag, e powyszy schemat nie dziaa w standardowej przegldarce WWW. Moemy wyszukiwa za pomoc nazwy pakietu (pname), a take szuka wydawcy za pomoc wyraenia
market://search?q=pub:\"Fname Lname\" lub przy uyciu dowolnego pola publicznego
sklepu Android Market (nazwa aplikacji, nazwa wydawcy oraz opis aplikacji), wpisujc market://
search?q=<querystring>.
Jeeli Czytelnik poczy zdobyt wiedz z technikami omwionymi w poprzednim podrozdziale, powinien umie napisa kod, ktry bdzie prbowa odnale pakiet odblokowujcy
w danym urzdzeniu. Jeli pakiet nie zostanie znaleziony, moemy wywietli monit i zapyta
uytkownika, czy nie chce go pobra z internetu. W przypadku odpowiedzi twierdzcej aplikacja wywoa intencj otwierajc usug Android Market i automatycznie uruchamiajc stron,
na ktrej bdzie mona pobra lub zakupi aplikacj odblokowujc.
Wysyanie aplikacji
Wysyanie aplikacji jest prostym procesem, wymagajcym jednak nieco przygotowa. Przed
rozpoczciem wysyania musimy ustanowi kilka parametrw oraz podj pewne decyzje. Niniejszy podrozdzia zosta powicony omwieniu tych przygotowa i decyzji. Gdy ju wszystko
bdzie gotowe, przejdziemy do konsoli programisty i wybierzemy opcj Upload Application.
Zostaniemy poproszeni o wprowadzenie wielu informacji dotyczcych naszej aplikacji, usuga
Market przebada aplikacj oraz dostarczone dane i w kocu nasz program zostanie przygotowany do publikacji w sklepie Android Market.
1019
W poprzednim podrozdziale omwilimy proces przygotowania pliku .apk do wysania. Przycignicie uwagi klientw wymaga z naszej strony szczypty marketingu. Musimy stworzy dobry
opis aplikacji oraz jej przeznaczenia, konieczne te bdzie wykonanie zrzutw ekranu, aby uytkownicy wiedzieli, e nie kupuj kota w worku.
Jednym z pierwszych elementw, o jakie zostaniemy poproszeni podczas wysyania aplikacji,
s zrzuty ekranu. Najprostszym sposobem ich uzyskania jest zastosowanie narzdzia DDMS.
Uruchamiamy rodowisko Eclipse, nastpnie wczamy aplikacj na emulatorze lub w rzeczywistym urzdzeniu i przeczamy perspektyw na widoki DDMS i Device. Wewntrz widoku
Device wybieramy urzdzenie, na ktrym uruchomilimy aplikacj, i klikamy przycisk Screen
Capture (symbolizuje go ikona maego obrazu umieszczona w prawym grnym rogu ekranu)
lub wybieramy go z menu View. Jeeli pojawi si taka moliwo, wybierzmy 24-bitowy kolor.
Android Market przekonwertuje zrzuty ekranu na skompresowane pliki JPEG; pocztkowa
warto 24 bitw przyniesie lepsze rezultaty ni pocztkowa warto 8 bitw. Wybierzmy takie
zrzuty, ktre przy ukazywaniu oryginalnoci aplikacji prezentuj jednoczenie jej funkcje. Musimy
wprowadzi co najmniej dwa zrzuty ekranu, maksymalnie za moemy zamieci ich osiem.
Nastpn kwesti jest wygenerowana ikona aplikacji w wysokiej rozdzielczoci. Moemy w tym
celu wykorzysta projekt tej samej ikony, ktra zostanie zastosowana w urzdzeniu, ale musi
ona posiada wymiary 512512 pikseli. Jest to jeden z wymogw usugi Android Market.
Moemy umieci rwnie grafik promujc, jej rozmiar bdzie jednak mniejszy od zrzutu
ekranu. Chocia taka grafika stanowi jedynie dodatek, warto j rwnie opublikowa. Nigdy nie
wiadomo, kiedy zostanie wywietlona; bez niej nie bdziemy pewni, co (jeeli cokolwiek) pojawi si na jej miejscu. Jednym z miejsc, w ktrym pojawia si grafika promocyjna, jest grna cz
ekranu wywietlajcego szczegy aplikacji w sklepie Android Market.
Kolejnym opcjonalnym rodzajem obrazu jest grafika wymieniajca cechy aplikacji, o rozmiarze
1024500 pikseli. Grafika ta bdzie wykorzystywana w sekcji Polecane serwisu Android Market,
wic powinnimy si naprawd postara, aby ten obraz wyglda jak najlepiej.
Ostatnim elementem graficznym zwizanym z nasz aplikacj jest opcjonalny plik wideo, ktry
moemy zamieci w serwisie YouTube. Adres do tego pliku moemy umieci w opisie aplikacji.
Android Market poprosi nastpnie o informacj tekstow dotyczc aplikacji, ktra bdzie widoczna dla klientw, wcznie z tytuem, opisem oraz tekstem promujcym. Moliwo wprowadzenia tekstu promujcego stanie si dostpna jedynie po umieszczeniu grafiki promujcej.
Moemy opublikowa tekst w wielu jzykach, gdy nasza aplikacja bdzie dostpna na caym
wiecie. Grafika promujca moe by umieszczona w sklepie Android Market wycznie jednorazowo, zatem jeli zrzuty ekranu wygldaj inaczej w rnych ustawieniach regionalnych,
powinnimy rozway umieszczenie ich w innym miejscu dostpnym dla klientw, na przykad
na stronie domowej. Takie podejcie moe w przyszoci ulec zmianie.
Jeli napisalimy wasn wersj umowy EULA, powinnimy w opisie zamieci odnonik do niej,
dziki czemu uytkownicy zapoznaj si z jej treci przed pobraniem aplikacji. Naley wzi pod
uwag, e klienci prawdopodobnie bd chcieli wykorzysta funkcj wyszukiwania aplikacji,
zatem najlepiej byoby umieci w tekcie odpowiednie sowa kluczowe, dziki czemu znaczco
wzronie prawdopodobiestwo natrafienia na nasz aplikacj przez osoby szukajce zapewnianych przez ni funkcji. W kocu warto rwnie umieci krtki komentarz wraz z adresem
e-mail na wypadek pojawienia si problemw z aplikacj. Bez tej prostej zachty uytkownicy
mog czciej wystawia negatywne opinie, a taka negatywna opinia naprawd ogranicza moliwo rozwizania problemu w porwnaniu do wymiany informacji z uytkownikiem, ktry natrafi na jaki bd.
1021
rednia dojrzao i Wysoka dojrzao. Wybr odpowiedniej grupy wiekowej zaley od treci
aplikacji oraz od iloci tej treci. Firma Google zdefiniowaa zasady zwizane z pooeniem geograficznym oraz umieszczaniem lub publikowaniem lokacji. Najlepiej samemu przejrze te zasady, znajdziemy je pod adresem www.google.com/support/androidmarket/bin/answer.py?hl=
en&answer=188189.
Jedn z ostatnich decyzji, jakie naley podj, jest wybr regionw geograficznych oraz operatorw telefonii komrkowej, dla ktrych nasza aplikacja bdzie widoczna. Poprzez wybr opcji
All aplikacja bdzie dostpna na caym wiecie. Czasami jednak naley ograniczy dystrybucj
do danego regionu geograficznego lub operatora. W zalenoci od funkcji oferowanych przez aplikacj wymagane jest nieraz ograniczenie liczby krajw, w ktrych aplikacja bdzie dopuszczona do obrotu, ze wzgldu na konieczno przestrzegania prawa eksportowego Stanw Zjednoczonych. Ograniczenie dystrybucji aplikacji do okrelonych operatorw jest konieczne,
gdy wystpi problemy z kompatybilnoci z urzdzeniami lub niezgodno z zasadami danego operatora. Aby przejrze list dostpnych operatorw, klikamy w konsoli programisty nazw
wybranego kraju, dziki czemu zostanie wywietlony spis dostpnych operatorw w danym
pastwie. Zaznaczenie opcji All spowoduje rwnie, e dana aplikacja bdzie dostpna dla
wszelkich kolejnych pastw i operatorw, ktrym kiedy firma Google udostpni serwis Android Market w tym celu nie bd potrzebne adne dziaania programisty.
Chocia profil programisty zawiera informacje kontaktowe, moemy wprowadzi inne dane
podczas wysyania kadej aplikacji. Usuga Market monituje o wprowadzenie adresu strony
WWW, adresu e-mail oraz numeru telefonu, sucych jako informacje kontaktowe zwizane
z dan aplikacj. W celu umoliwienia obsugi klientw musimy wypeni przynajmniej jedno
z wymienionych pl, nie jest konieczne jednak wprowadzenie danych do wszystkich trzech
elementw. Podawanie tutaj prywatnego adresu e-mail nie jest najlepszym pomysem, tak samo jak nie chcielibymy najprawdopodobniej podawa tutaj prywatnego numeru telefonu. Gdy
bdziemy zarabia miliony dolarw na sprzeday naszej aplikacji, raczej zatrudnimy kogo odbierajcego telefony i odpowiadajcego na e-maile od uytkownikw. Jeeli od razu zaoymy
adres e-mail do celw obsugi pomocy technicznej, nie bdziemy mieli pniej problemu z rozdzieleniem poczty osobistej i wiadomoci od uytkownikw.
Po podjciu wszystkich omwionych decyzji musimy naszej aplikacji nada atest przestrzegania
polityki treci w usudze Android Market dla programistw (nie jest zbyt rygorystyczna),
a take drugi atest umoliwiajcy eksportowanie programu poza granice Stanw Zjednoczonych. Aplikacje udostpniane poprzez serwis Android Market podlegaj prawu eksportowemu Stanw Zjednoczonych, poniewa serwery Google s umieszczone w tym kraju. Dotyczy to nawet aplikacji utworzonych w innym pastwie oraz sytuacji, kiedy zarwno programista,
jak i jego uytkownicy znajduj si poza USA. Nie zapominajmy, e zawsze moemy dystrybuowa aplikacj innymi kanaami. Gdy ju wprowadzimy wszystkie niezbdne informacje i wylemy zdjcie, moemy w kocu wcisn przycisk Save. Nasza aplikacja zostanie wtedy przygotowana do wysania w wiat.
W kocu moemy opublikowa nasz aplikacj poprzez wcinicie przycisku Publish. Android
Market sprawdzi publikowany program, zwaszcza pod ktem daty wyganicia certyfikatu aplikacji. Jeeli cay proces przebiegnie pomylnie, nasz kod stanie si dostpny do pobrania przez
innych uytkownikw. Gratulacje!
1023
Odnoniki
Poniej prezentujemy odnoniki do materiaw, ktre mog pomc w zrozumieniu koncepcji
omwionych w niniejszym rozdziale:
http://developer.android.com/guide/topics/manifest/manifest-intro.html jest to
strona poradnika programisty powicona plikowi AndroidManifest.xml, zawierajca
opisy zastosowania znacznikw <supports-screens>, <uses-configuration>
oraz <uses-feature>.
http://developer.android.com/guide/practices/screens_support.html strona
poradnika programisty zawierajca informacje o tak zwanej obsudze wielu ekranw.
Znajdziemy tu wiele przydatnych informacji na temat korzystania z rnorodnych
rozmiarw i gstoci wywietlaczy.
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html strona
poradnika programisty zawierajca wskazwki dotyczce projektowania ikon.
Umieszczono tu interesujce informacje na temat tworzenia ikon wpywajcych
na jako naszej aplikacji.
http://android-developers.blogspot.com/2010/09/securing-android-lvl-applications.html oraz
http://android-developers.blogspot.com/2010/09/proguard-android-and-licensing-server.html
dwa wpisy dotyczce metod stosowania biblioteki LVL w celu zapobieenia piractwu.
http://developer.android.com/guide/market/billing/index.html dokumentacja dotyczca
wbudowanego moduu pobierania opat.
Podsumowanie
Teraz moemy ju podbi cay wiat naszymi aplikacjami utworzonymi dla systemu Android!
Pokazalimy, w jaki sposb naley przygotowa siebie oraz swoj aplikacj, jak naley j opublikowa oraz umoliwi uytkownikom jej wyszukanie, pobranie i uytkowanie.
R OZDZIA
29
Koncepcja fragmentw
oraz inne pojcia
dotyczce tabletw
1027
Struktura fragmentu
Jak ju wspomnielimy, fragment przypomina nieco podaktywno: posiada jasno okrelone
przeznaczenie i niemal zawsze prezentuje interfejs uytkownika. Jednak aktywno stanowi klas
podrzdn w stosunku do kontekstu, natomiast fragment jest rozszerzeniem klasy Object, bdcej czci pakietu android.app. Fragment nie stanowi rozszerzenia aktywnoci. Jednak, podobnie jak ma to miejsce w przypadku aktywnoci, klasa Fragment (lub jej elementy podrzdne)
bdzie zawsze rozszerzana w celu przesonicia jej zachowania.
1029
Fragment jest bardzo uzaleniony od aktywnoci, do ktrej jest przypisany, i moe przej przez
kilka etapw cyklu ycia, podczas gdy aktywno w tym samym czasie znajduje si cigle na
jednym etapie.
Na samym pocztku tworzy si wystpienie fragmentu. Istnieje on teraz jako obiekt w pamici.
Prawdopodobnie pierwsz czynnoci bdzie dodanie argumentw inicjalizacji do tego obiektu.
Stwierdzenie to jest najbardziej prawdziwe w przypadku odtwarzania wczeniej zachowanego
stanu fragmentu. W momencie odtwarzania stanu fragmentu zostaje przywoany domylny
konstruktor, po czym nastpuje doczenie pakietu argumentw inicjalizacji. W przypadku
tworzenia nowego wystpienia fragmentu warto zapozna si z kodem z listingu 29.1 zaprezentowalimy w nim bardzo przydatny schemat metody fabrykujcej obiekty wewntrz definicji klasy MyFragment.
Listing 29.1. Tworzenie wystpienia fragmentu za pomoc statycznej metody fabrykujcej
public static MyFragment newInstance(int index) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
1031
zachowanego stanu, zostanie on przekazany tej metodzie. Metoda ta moe jako pierwsza utworzy wtek drugoplanowy, pobierajcy dane wymagane przez fragment. Kod fragmentu jest
przetwarzany w wtku interfejsu uytkownika i nie chcemy, aby byy w nim przeprowadzane
rwnie operacje wejcia-wyjcia lub sieciowe. W rzeczywistoci logicznym rozwizaniem jest
przygotowanie danych za pomoc wtku pobocznego. To wanie w nim powinny wystpowa
wszystkie wywoania blokujce. Musimy pniej poczy si w jaki sposb z tymi danymi,
istniej na to jednak odpowiednie sposoby.
Jednym ze sposobw wczytania danych w wtku pobocznym jest zastosowanie klasy
Loader. Zabrako nam miejsca na jej opis w ksice, zapraszamy jednak do przejrzenia
naszej oficjalnej strony WWW w celu zapoznania si z niezbdnymi informacjami.
Widzimy tutaj, w jaki sposb moemy uzyska dostp do ukadu graficznego wykorzystywanego
wycznie przez ten fragment i rozwin go do widoku, ktry zostanie nastpnie przekazany
obiektowi wywoujcemu. Takie rozwizanie posiada kilka zalet. Moemy zawsze utworzy
hierarchi widokw za pomoc kodu, jednak poprzez rozwinicie ukadu graficznego z pliku
XML wykorzystujemy moliwoci technologii wyszukiwania zasobw. W zalenoci od konfiguracji urzdzenia lub, dokadniej mwic, od aktualnie uywanego urzdzenia zostanie wybrany najwaciwszy plik ukadu graficznego. Moemy wtedy uzyska dostp do okrelonego
widoku przechowywanego w ukadzie graficznym; w naszym przypadku pola text1 klasy
TextView. Jeszcze raz powtarzamy: nie naley docza w tej metodzie zwrotnej widoku danego
fragmentu do klasy nadrzdnej. Na listingu 29.2 widzimy, e wykorzystujemy pojemnik
w wywoaniu metody inflate(), przekazujemy jednak rwnie warto false w parametrze
attachToRoot.
1033
1035
Powyszy ukad graficzny nie rni si zbytnio od ukadw graficznych prezentowanych w poprzednich rozdziaach: jest on uoony poziomo, z dwoma elementami umieszczonymi z lewej
i z prawej strony. Mamy jednak do czynienia z nowym znacznikiem <fragment>, w ktrym
znajdziemy nowy atrybut noszcy nazw class. Pamitajmy, e fragment nie jest widokiem,
zatem ukad graficzny fragmentu nieco si rni od innych ukadw graficznych. Naley rwnie pamita, e znacznik ten stanowi jedynie wypeniacz w tym ukadzie graficznym.
Pozostae atrybuty fragmentu wygldaj podobnie jak atrybuty widoku i peni analogiczne
funkcje. Atrybut class znacznika fragmentu definiuje rozszerzon klas dla listy tytuw. Oznacza to, e musimy rozszerzy klas Fragment w celu zaimplementowania logiki, a w znaczniku
<fragment> musi si znale nazwa tej rozszerzonej klasy. Fragment posiada wasn hierarchi
widokw, ktra zostanie w pniejszym okresie samoistnie utworzona. Nastpnym znacznikiem jest FrameLayout, a nie kolejny znacznik <fragment>. Dlaczego? Wyjanimy to dokadniej w dalszej czci rozdziau, na razie jednak Czytelnik powinien wiedzie, e bdziemy wprowadza pewne modyfikacje tekstu i wymienia fragmenty midzy sob. Znacznik FrameLayout
suy jako pojemnik widoku przechowujcy biecy fragment tekstu. W przypadku fragmentu
przechowujcego tytuy istnieje jeden (i tylko jeden) fragment, o ktry trzeba zadba; nie ma
adnego zamieniania miejscami i adnych innych modyfikacji. W przypadku obszaru wywietlajcego szekspirowski poemat mamy do czynienia z kilkoma fragmentami.
Kod klasy MainActivity zosta umieszczony na listingu 29.4.
Listing 29.4. Kod rdowy klasy MainActivity
// Jest to plik MainActivity.java
import
import
import
import
import
import
import
import
import
android.app.Activity;
android.app.Fragment;
android.app.FragmentManager;
android.app.FragmentTransaction;
android.content.Intent;
android.content.res.Configuration;
android.os.Bundle;
android.os.Environment;
android.util.Log;
/**
* Funkcja pomocnicza ukazujca szczegy zaznaczonego elementu albo poprzez
* wywietlanie fragmentu znajdujcego si w biecym interfejsie uytkownika, albo
* poprzez rozpoczcie nowej aktywnoci, w ktrej bd one wywietlane.
*/
public void showDetails(int index) {
Log.v(TAG, "w metodzie showDetails(" + index + ") aktywnosci MainActivity");
if (isMultiPane()) {
1037
details = DetailsFragment.newInstance(index);
//ft.addToBackStack("details");
ft.replace(R.id.details, details);
ft.commit();
}
} else {
android.app.Activity;
android.app.Fragment;
android.os.Bundle;
android.util.AttributeSet;
android.util.Log;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.TextView;
1039
}
public int getShownIndex() {
return mIndex;
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
Log.v(MainActivity.TAG,
"w metodzie onCreateView klasy DetailsFragment. pojemnik = " +
container);
1041
W przypadku naszej przykadowej aplikacji moemy stosowa ten sam ukad graficzny dla trybu
krajobrazowego i portretowego podczas wywietlania szczegowych informacji. Jest to ukad
przeznaczony nie dla aktywnoci, lecz wycznie do wywietlania tekstu fragmentu. Poniewa
moe by on uznany za domylny ukad graficzny, moemy umieci go w katalogu res/layout,
gdzie zostanie znaleziony i zastosowany, nawet jeli wywietlacz znajduje si w trybie krajobrazowym. Podczas wyszukiwania pliku ukadu graficznego sucego do wywietlania szczegw
system sprawdza najpierw katalogi cile powizane z konfiguracj urzdzenia, powrci jednak
do katalogu res/layout, jeli nigdzie indziej nie znajdzie pliku details.xml. Oczywicie, jeeli chcemy
zaprojektowa inny ukad graficzny fragmentu dla trybu krajobrazowego, moemy zdefiniowa
osobny plik details.xml i umieci go w katalogu /res/layout-land. Nic nie stoi na przeszkodzie,
eby eksperymentowa z rnymi plikami details.xml.
W momencie wywoania metody onCreate() fragmentu przechowujcego szczegy system
wybierze i rozwinie ukad graficzny z odpowiedniego pliku details.xml, do ktrego wstawi tekst
z klasy Shakespeare. Nie zamiecimy tu caego kodu klasy Shakespeare, lecz jedynie jego cz
(listing 29.7), aby uatwi Czytelnikowi zrozumienie jego dziaania. Peny kod rdowy znajdziemy w gotowym projekcie, do ktrego adres zosta umieszczony w podrozdziale Odnoniki
na kocu rozdziau.
Zatem obecnie nasza hierarchia widokw we fragmencie zawierajcym szczegowe informacje przechowuje tekst z wybranej sztuki. Fragment ten jest ju przygotowany do wywietlenia. Moemy teraz wrci do metody showDetails(), aby zaj si omwieniem klasy
FragmentTransaction.
Klasy FragmentTransaction
i drugoplanowy stos fragmentw
Kod w metodzie showDetails(), ktry suy do wstawiania nowego fragmentu przechowujcego
szczegowe informacje (ukazany ponownie na listingu 29.8), wyglda do prosto, wykonuje
jednak mnstwo zada. Warto powici troch czasu na wyjanienie, co tu si dzieje i dlaczego tak
jest. Jeeli nasza aktywno dziaa w trybie wielopanelowym, chcemy zaprezentowa fragment
przechowujcy szczegy obok fragmentu zawierajcego list. By moe nasza aplikacja wywietla
ju ten pierwszy fragment, co oznacza, e sta si widoczny dla uytkownika. W kadym przypadku identyfikator zasobu R.id.details jest przeznaczony dla pojemnika FrameLayout
aktywnoci, co jest widoczne na listingu 29.3. Jeeli fragment przechowujcy szczegy znajduje
si wewntrz ukadu graficznego, z powodu braku wasnego identyfikatora otrzyma wanie
wspomniany identyfikator. Aby wic dowiedzie si, czy w ukadzie graficznym znajduje si jaki
fragment, moemy wysa zapytanie do metody findFragmentById() menedera fragmentw.
Zostanie przekazana albo warto null, jeli ukad graficzny jest pusty, albo informacje na temat
biecego fragmentu. Moemy wtedy zadecydowa, e powinnimy umieci nowy fragment
w ukadzie graficznym, poniewa ukad graficzny moe by pusty lub moe by w nim umieszczony ukad graficzny reprezentujcy szczegy nieodpowiedniego tytuu. Po podjciu decyzji
o utworzeniu i wykorzystaniu nowego fragmentu wywoujemy w tym celu metod fabrykujc.
Moemy teraz wstawi nowy fragment, ktry zostanie zaprezentowany uytkownikowi.
Listing 29.8. Przykad transakcji fragmentu
public void showDetails(int index) {
Log.v(TAG, "w metodzie showDetails(" + index + ") aktywnosci MainActivity");
if (isMultiPane()) {
1
1043
//ft.addToBackStack("details");
ft.replace(R.id.details, details);
ft.commit();
}
Kluczow koncepcj, ktr trzeba zrozumie, jest to, e fragment musi si znale w pojemniku widokw, zwanym rwnie grup widokw. Wynika to czciowo z faktu, e fragment
sam w sobie nie jest widokiem. Klasa ViewGroup zawiera takie elementy, jak ukady graficzne
i ich pochodne klasy. To wanie dlatego wybralimy klas FrameLayout do pliku main.xml
stanowicego ukad graficzny aktywnoci. Nasz fragment przechowujcy szczegy zostanie
umieszczony w pojemniku FrameLayout. Gdybymy zamiast tego zdefiniowali jeszcze jeden
wze <fragment> w pliku ukadu graficznego aktywnoci, nie moglibymy przeprowadzi wymaganej operacji zamiany. To wanie za pomoc klasy przeprowadzamy zamian fragmentw.
W czasie transakcji zamieniamy miejscami dowolny fragment umieszczony w ramce z nowym
fragmentem przechowujcym szczegy. Moglibymy rozwiza to w inny sposb, mianowicie
lokalizujc identyfikator zasobu kontrolki TextView, ktra przechowuje tekst szczegw,
i wprowadzajc w niej nowy tekst dla nowej wybranej sztuki Szekspira. Istnieje jednak jeszcze
jeden fakt dotyczcy fragmentw, przemawiajcy za korzystaniem z klasy FragmentTransaction.
Jak wiemy, aktywnoci s poukadane na stosie i w trakcie zagbiania si w aplikacj okazuje
si, e czsto na stosie znajduje si kilka jednoczenie uruchomionych aktywnoci. Po wciniciu przycisku cofania aktywno znajdujca si na wierzchu stosu jest z niego usuwana, a jej
miejsce zajmuje nastpna w kolejce aktywno, ktra zostaje wznowiona. Proces ten jest przeprowadzany wzdu caego stosu a do poziomu ekranu startowego.
Stanowio to znakomite rozwizanie w przypadku prostych aktywnoci, teraz jednak omawiamy
takie, ktre zawieraj po kilka jednoczenie dziaajcych fragmentw. Ponadto, skoro moemy
coraz bardziej zagbia si w aplikacj bez koniecznoci opuszczania aktywnoci znajdujcej
si na wierzchu stosu, trzeba byo rozwin koncepcj korzystania z przycisku cofania w taki
sposb, aby uwzgldni take fragmenty. W istocie fragmenty wymagaj zastosowania tej koncepcji nawet bardziej ni proste aktywnoci. Gdy mamy do czynienia z kilkoma fragmentami
oddziaujcymi ze sob rwnoczenie wewntrz aktywnoci i nastpuje jednoczesne przejcie
do nowej treci, ktre dotyczy wszystkich tych fragmentw, to wcinicie przycisku cofania powinno sprawi, e fragmenty te razem zostan cofnite o jeden etap. Aby zapewni, e to cofnicie
obejmie wszystkie fragmenty w ramach danej aktywnoci, utworzono klas FragmentTransaction
zapewnia ona koordynacj tego procesu.
Powoduje on, e stary fragment zanika, podczas gdy nowy stopniowo si pojawia. Pierwszy parametr odnosi si do nowego fragmentu, a drugi do fragmentu usuwanego. Warto przejrze
katalog animator, aby zapozna si z innymi domylnymi animacjami. Jeeli chcemy utworzy
wasn animacj, w dalszej czci rozdziau znajdziemy sekcj powicon animatorowi obiektw. Bardzo wana jest rwnie informacja, e wywoanie przejcia musi nastpi przed wywoaniem metody replace(), w przeciwnym wypadku zostanie ono zignorowane.
Uywanie animatora obiektw do programowania efektw specjalnych widocznych podczas
zmiany fragmentw moe by zabawne. W klasie FragmentTransaction znajdziemy jeszcze
dwie metody, z ktrymi powinnimy si zapozna: hide() i show(). Parametrem w przypadku
obydwu tych metod jest fragment. Zadania, jakie metody te realizuj, wynikaj z ich nazw.
W przypadku fragmentu powizanego z kontenerem widokw powoduj one jego ukrywanie
lub wywietlanie w interfejsie uytkownika. W ten sposb fragment nie zostaje usunity z menedera fragmentw, musi by jednak zwizany z kontenerem widokw, aby metody miay
wpyw na jego widoczno. Jeeli fragment nie zawiera hierarchii widokw lub hierarchia ta nie
jest powizana z hierarchi wywietlanych widokw, metody te oka si bezuyteczne.
1045
Klasa FragmentManager
Klasa FragmentManager jest skadnikiem obsugujcym fragmenty przechowywane w aktywnoci.
Zaliczaj si do nich rwnie fragmenty przechowywane w stosie drugoplanowym oraz niepodczone fragmenty. Wyjanijmy to. Fragmenty powinny by tworzone wycznie w kontekcie aktywnoci. Dzieje si to albo poprzez rozwinicie ukadu graficznego aktywnoci, albo poprzez bezporednie utworzenie wystpienia obiektu, co zostao ukazane na listingu 29.1. W tym
drugim przypadku fragment zostaje zazwyczaj doczony do aktywnoci za pomoc transakcji, natomiast w kadym wypadku uzyskujemy dostp i zarzdzamy fragmentami poprzez klas
FragmentManager.
W rzeczywistoci moemy otrzyma fragment, ktry nie eksponuje hierarchii widokw. Moe
si to okaza przydatne, jeli zechcemy zawrze okrelon logik w taki sposb, aby mc j
doczy do aktywnoci, lecz jednoczenie pozostawi jej pewn doz autonomii, odgradzajc
j od cyklu ycia aktywnoci i pozostaych fragmentw. Gdy aktywno przechodzi przez
cykl odtwarzania wynikajcy ze zmiany konfiguracji urzdzenia, taki fragment niebdcy
czci interfejsu uytkownika moe pozostawa w duej czci nietknity, podczas gdy sama
aktywno zostaje usunita i na jej miejsce wkracza nowa. Takie rozwizanie jest interesujc
opcj dla metody setRetainInstance().
Meneder fragmentw obsuguje rwnie stos drugoplanowy. Podczas transakcji fragmentw
system umieszcza fragmenty na tym stosie, podczas gdy meneder fragmentw moe je stamtd usuwa. Zazwyczaj dokonujemy tego przy uyciu identyfikatora lub znacznika fragmentu,
rwnie dobrze moemy jednak w tym celu wprowadzi pozycj w stosie lub po prostu usun
fragment znajdujcy si na wierzchu.
Na koniec naley stwierdzi, e meneder fragmentw zawiera metody uatwiajce proces debugowania, w tym takie jak umoliwiajce umieszczanie komunikatw w oknie LogCat (metoda
enableDebugLogging()) lub umieszczanie biecego stanu menedera fragmentw w strumieniu (metoda dump()). Zwrmy uwag, e na listingu 29.4 wczylimy tryb debugowania menedera fragmentw w metodzie onCreate().
1047
android.app.Activity;
android.app.ListFragment;
android.os.Bundle;
android.util.AttributeSet;
android.util.Log;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.ListView;
1049
super.onActivityCreated(savedState);
Podobnie jak wczeniej wikszo zamieszczonego tu kodu jest zbdna z punktu widzenia dziaania aplikacji i suy jedynie do przechowania instrukcji wywietlajcych informacje w dzienniku, dziki czemu bdzie wiadomo, kiedy fragment przechodzi do okrelonego etapu cyklu ycia.
W przeciwiestwie do klasy DetailsFragment, w tym fragmencie metoda onCreateView() nie
posiada specjalnego przeznaczenia. Wynika to z faktu, e rozszerzamy klas ListFragment,
ktra ju zawiera widok ListView. Domylne ustawienia metody onCreateView() w klasie
ListView powoduj przekazanie widoku kontrolki ListView. Waciwe operacje s przeprowadzane dopiero na etapie wywoania metody onActivityCreated(). Do tego czasu moemy
by pewni, e zostanie utworzona hierarchia widokw aktywnoci wraz z hierarchi aktywnoci
fragmentu. Identyfikatorem zasobu dla kontrolki ListView jest android.R.id.list1. eby
jednak uzyska do niej odniesienie, naley wywoa metod getListView() wewntrz metody
onActivityCreated(). Poniewa jednak klasa ListFragment nie jest tym samym co kontrolka
ListView, nie podczamy adaptera bezporednio do widoku ListView. Musimy zamiast
tego uy metody setListAdapter() klasy ListFragment. Poniewa zostaa skonfigurowana
hierarchia widokw aktywnoci, moemy bezpiecznie powrci do aktywnoci, aby wywoa
metod showDetails().
Na tym etapie cyklu ycia aktywnoci dodalimy adapter do widoku listy, odczytalimy biec
pozycj (jeeli powrcilimy z etapu przywracania, spowodowanego na przykad zmian pooenia urzdzenia) oraz zaprogramowalimy aktywno (w metodzie showDetails()), aby
wprowadzia tekst zwizany z odpowiednim tytuem sztuki szekspirowskiej.
Klasa TitlesFragment rwnie posiada obiekt nasuchujcy listy, zatem gdy uytkownik zaznaczy inny tytu, system wywoa metod zwrotn onListItemClick() i zmieni tekst na odpowiadajcy danej sztuce, znowu za pomoc metody showDetails().
1051
Kolejnym elementem rnicym ten fragment od wczeniej omawianego fragmentu przechowujcego szczegy jest zapisywanie stanu w pakiecie (wartoci wskazujcej biec pozycj na
licie) oraz odczytywanie jej w metodzie onCreate() podczas zamykania i odtwarzania fragmentu. W przeciwiestwie do fragmentu przechowujcego szczegy, ktry jest wymieniany
w kontrolce FrameLayout ukadu graficznego aktywnoci, mamy tu do czynienia z tylko jednym fragmentem przechowujcym tytuy. Jeli wic nastpuje zmiana konfiguracji i nasz fragment przechodzi przez operacj zapisywania i odczytywania, chcemy zapamita biec pozycj. W przypadku fragmentw przechowujcych szczegy odtwarzamy je bez koniecznoci
zapamitywania poprzedniego stanu.
Details().
Listing 29.10. Wywietlanie nowej aktywnoci, jeli okrelony fragment nie mieci si na ekranie
// Jest to plik DetailsActivity.java
import
import
import
import
android.app.Activity;
android.content.res.Configuration;
android.os.Bundle;
android.util.Log;
Warto przyjrze si kilku interesujcym aspektom tego kodu. Przede wszystkim jest on naprawd prosty do implementacji. Dokonujemy prostego okrelenia trybu orientacji urzdzenia
i jeeli znajduje si ono w trybie portretowym, wstawiamy fragment przechowujcy szczegy
do oddzielnej aktywnoci. W trybie krajobrazowym aktywno MainActivity moe wywietla
obydwa fragmenty obok siebie, zatem nie ma potrzeby pokazywania dodatkowej aktywnoci.
Czytelnik moe si zastanawia, po co chcielibymy w ogle tworzy t aktywno w trybie
krajobrazowym. Odpowied jest prosta: nie chcemy. Jeeli jednak aktywno przechowujca
szczegy zostaa utworzona w trybie portretowym i uytkownik obrci urzdzenie do trybu
krajobrazowego, zostanie ona uruchomiona ponownie z powodu zmiany konfiguracji. Otrzymalimy wic dodatkow aktywno w trybie krajobrazowym. W tym momencie jedynym rozsdnym rozwizaniem okazuje si zakoczenie tej aktywnoci i przekazanie klasie MainActivity
wszystkich zada.
Kolejnym interesujcym aspektem aktywnoci przechowujcej szczegy jest brak moliwoci
ustawienia gwnego widoku treci za pomoc metody setContentView(). W jaki wic sposb
zostaje utworzony interfejs uytkownika? Jeeli przyjrzymy si uwanie wywoaniu metody
add() w transakcji fragmentu, zauwaymy, e pojemnik widokw, do ktrego dodajemy dany
fragment, zosta okrelony jako zasb android.R.id.content. Jest to gwny pojemnik widokw aktywnoci, zatem jeli doczamy do niego hierarchi widokw fragmentw, oznacza
to, e hierarchia ta staje si jedyn hierarchi widokw w aktywnoci. Do utworzenia nowego
fragmentu (na przykad przyjmujcego pakiet w postaci argumentu) wykorzystalimy tutaj dokadnie tak sam klas DetailsFragment co wczeniej, lecz wprowadzilimy inn metod
newInstance(), a nastpnie doczylimy go po prostu do gwnego poziomu hierarchii
widokw aktywnoci. W ten sposb fragment zostaje wywietlony we wntrzu tej aktywnoci.
Z punktu widzenia uytkownika oglda on teraz jedynie widok zawierajcy fragment, w ktrym jest przechowywany tekst sztuki Szekspira. Jeeli bdzie chcia przej do innego tytuu,
musi wcisn przycisk cofania, co spowoduje powrt do gwnej aktywnoci (przechowujcej
wycznie fragment z tytuami). Alternatywnym rozwizaniem jest obrt urzdzenia i przejcie
do trybu krajobrazowego. Wtedy w aktywnoci przechowujcej szczegy zostanie wywoana
metoda finish(), co spowoduje jej zamknicie i pojawienie si odtworzonej aktywnoci gwnej.
Kiedy urzdzenie znajduje si w trybie portretowym i jeli w gwnej aktywnoci nie wywietlamy fragmentu przechowujcego szczegy, naley utworzy osobny plik ukadu graficznego
main.xml, ktrego zawarto zostaa zaprezentowana na listingu 29.11.
Listing 29.11. Ukad graficzny aktywnoci dla trybu portretowego
<?xml version="1.0" encoding="utf-8"?>
1053
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.androidbook.fragments.bard.TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Oczywicie, ten ukad graficzny mona zmodyfikowa w dowolny sposb. W celach demonstracyjnych suy on jedynie do wywietlania fragmentu przechowujcego tytuy. Bardzo dobrze si stao, e klasa tego fragmentu nie wymaga duej iloci kodu do przetwarzania zmian
konfiguracji urzdzenia.
Ostatnim elementem, jaki chcemy doczy w tym przykadzie, jest plik AndroidManifest.xml,
zaprezentowany na listingu 29.12.
Listing 29.12. Plik AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0" package="com.androidbook.fragments.bard">
<uses-sdk android:minSdkVersion="11" />
<application android:icon="@drawable/icon"
android:label="Szekspir">
<activity
android:name="com.androidbook.fragments.bard.MainActivity"
android:label="Szekspir">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.androidbook.fragments.bard.DetailsActivity"
android:label="Szekspir szczegy">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
Jest to standardowy plik manifest. Widzimy gwn aktywno zawierajc kategori LAUNCHER,
dziki czemu zostanie ona umieszczona na licie aplikacji urzdzenia. Widzimy nastpnie
oddzieln aktywno DetailsActivity ze zdefiniowan kategori DEFAULT. W ten sposb
moemy uruchomi t aktywno za pomoc kodu, nie zostanie ona jednak umieszczona na
licie aplikacji.
Trwao fragmentw
W trakcie testowania omawianej aplikacji nie naley zapomina o obracaniu urzdzenia (w emulatorze dokonamy tego za pomoc skrtu klawiaturowego Ctrl+F11). Zauwaymy, e wraz z obrotem urzdzenia obraca si bd rwnie fragmenty. Jeeli Czytelnik bdzie ledzi komunikaty
w oknie LogCat, zauway, e aplikacja wygeneruje ich bardzo duo. W szczeglnoci naley
zwrci uwag na te komunikaty wywietlane w momencie obracania urzdzenia, ktre dotycz
fragmentw; usuwana i odtwarzana jest nie tylko aktywno, lecz rwnie fragmenty.
Dotychczas napisalimy jedynie niewielk ilo kodu do obsugi fragmentu przechowujcego
szczegy. Kod ten suy do zachowywania biecej pozycji na licie tytuw w przypadku ponownego uruchomienia aktywnoci. W przypadku fragmentw przechowujcych szczegy nie
musielimy wprowadza kodu obsugujcego zmiany konfiguracji, poniewa nie ma takiej potrzeby. Android sam obsuy przechowywanie fragmentw znajdujcych si w menederze, ich
zachowywanie, a nastpnie odczytywanie w przypadku odtwarzania stanu aktywnoci. Czytelnik
powinien mie ju wiadomo, e fragmenty otrzymywane po zmianie konfiguracji najprawdopodobniej nie s tymi samymi fragmentami, ktre wczeniej znajdoway si w pamici. Fragmenty te zostay zrekonstruowane. System zachowa pakiet argumentw oraz informacje o typie
fragmentu, a w przypadku kadego fragmentu przechowujcego zapisane informacje o stanie zachowa rwnie pakiet z atrybutami zachowanego stanu, suce do pniejszego ich odtworzenia.
Komunikaty wywietlane w oknie LogCat informuj nas o fragmentach przechodzcych przez
cykl ycia w synchronizacji z cyklem ycia aktywnoci. Zauwaymy, e fragment przechowujcy
szczegy zostaje odtworzony, lecz system nie wywouje ponownie metody newInstance(). Zamiast tego Android korzysta po prostu z domylnego konstruktora, nastpnie docza do niego
pakiet argumentw i rozpoczyna wywoywanie metod zwrotnych danego fragmentu. Dlatego
tak wane jest, aby nie umieszcza adnego wymylnego kodu w metodzie newInstance(),
poniewa w momencie odtwarzania fragmentu metoda ta zostanie pominita.
Czytelnik powinien ju take doceni moliwo wielokrotnego uytkowania fragmentw
w rnych miejscach. Fragment przechowujcy tytuy jest wykorzystywany w dwch rnych
ukadach graficznych, jeli jednak przyjrzymy si jego kodowi, zauwaymy, e atrybuty umieszczone w tych plikach nie maj wikszego znaczenia. Moglibymy utworzy dwa zupenie rnice si od siebie ukady graficzne, a kod tego fragmentu wygldaby dokadnie tak samo.
To samo mona powiedzie o fragmencie przechowujcym szczegy. Zosta on wprowadzony
do gwnego ukadu graficznego trybu krajobrazowego oraz w aktywnoci przechowujcej
szczegy. Take i w tym przypadku ukady graficzne mog si od siebie znacznie rni, a kod
fragmentu przechowujcego szczegy nie ulegby zmianom. Rwnie kod aktywnoci przechowujcej szczegy by bardzo prosty.
Do tej pory analizowalimy dwa typy fragmentw: klas bazow Fragment oraz jej podklas
ListFragment. Przejdziemy teraz do kolejnego elementu klasy Fragment, jakim jest klasa
podrzdna DialogFragment.
1055
W tym podrozdziale Czytelnik dowie si, w jaki sposb wykorzystywa fragmenty do wywietlania
prostego okna dialogowego oraz niestandardowego okna dialogowego, ukazujcego tekst zachty.
moemy wywietli taki fragment MyDialogFragment w postaci okna dialogowego przy uyciu
transakcji fragmentu. Na listingu 29.13 umiecilimy pseudokod obrazujcy ten proces.
Listing 29.13. Wywietlanie fragmentu przechowujcego dialog
JakasAktywnosc
{
Zgodnie z listingiem 29.13, aby wywietli fragment przechowujcy okna dialogowe, naley
wykona nastpujce czynnoci:
1. Utworzenie fragmentu wywietlajcego okna dialogowe.
2. Uruchomienie transakcji fragmentu.
3. Wywietlenie okna dialogowego za pomoc transakcji utworzonej na etapie 2.
Przyjrzyjmy si kademu z wymienionych etapw.
...inne funkcje
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState)
{
...inne funkcje
}
W kodzie z listingu 29.14 wczytujemy widok zdefiniowany przez ukad graficzny. Nastpnie
wyszukujemy dwa przyciski i okrelamy dla nich metody zwrotne. W bardzo podobny sposb
tworzylimy wczeniej fragment przechowujcy szczegy. W przeciwiestwie jednak do prezentowanych wczeniej fragmentw, omawiany typ zawiera jeszcze jeden mechanizm pozwalajcy na utworzenie hierarchii widokw.
Przesanianie metody onCreateDialog
Alternatyw dla umieszczenia widoku w metodzie onCreateView() jest przesonicie metody
onCreateDialog() i dostarczenie wystpienia okna dialogowego. Na listingu 29.15 zosta
zaprezentowany przykadowy kod takiego rozwizania.
1057
...inne funkcje
@Override
public Dialog onCreateDialog(Bundle icicle)
{
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
b.setTitle("Tytu mojego okna dialogowego");
b.setPositiveButton("OK", this);
b.setNegativeButton("Anuluj", this);
b.setMessage(this.getMessage());
return b.create();
}
...inne funkcje
}
Pierwsza metoda show() wywietla okno dialogowe poprzez dodanie tego fragmentu do przekazanej transakcji wraz z okrelonym znacznikiem. Przekazuje ona nastpnie identyfikator
przeprowadzanej transakcji.
Druga metoda show() automatyzuje proces otrzymywania transakcji z menedera transakcji.
Jest to metoda skrtowa. Jednak w przypadku korzystania z niej tracimy moliwo umieszczenia transakcji w stosie drugoplanowym. Jeeli chcemy uzyska kontrol nad tym aspektem,
musimy zastosowa metod o pierwszej z wymienionych sygnatur. Druga metoda okazuje si
przydatna, gdy chcemy wywietli jedynie okno dialogowe i nie mamy innego powodu, aby
w danym momencie przeprowadza transakcj fragmentu.
1059
//Warto null symbolizuje brak nazwy dla transakcji przeprowadzanej w stosie drugoplanowym
HelpDialogFragment hdf =
HelpDialogFragment.newInstance(R.string.helptext);
hdf.show(ft, "NA POMOC");
return;
}
W obrbie jednej transakcji usuwamy biecy fragment i dodajemy nowy fragment wywietlajcy okna dialogowe. Wizualnie poprzednie okno dialogowe znika, a w jego miejscu
pojawia si nowe. Jeeli uytkownik wcinie przycisk cofania, nowe okno dialogowe zostanie wycofane i zostanie wywietlone poprzednie okno dialogowe, poniewa zachowalimy t transakcj w stosie drugoplanowym. Jest to bardzo przydatny sposb wywietlania
na przykad okna dialogowego pomocy.
1061
Rysunek 29.3. Interfejs uytkownika przykadowej aplikacji fragment przechowujcy okno dialogowe
android.app.Activity;
android.app.FragmentManager;
android.app.FragmentTransaction;
android.os.Bundle;
android.util.Log;
android.view.Menu;
android.view.MenuInflater;
android.view.MenuItem;
android.widget.Toast;
1063
hdf.show(ft, HELP_DIALOG_TAG);
}
public void onDialogDone(String tag, boolean cancelled,
CharSequence message) {
String s = tag + " reaguje na: " + message;
if(cancelled)
s = tag + " zostao anulowane przez uytkownika";
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
Log.v(LOGTAG, s);
}
}
Kod gwnej aktywnoci jest niezwykle prosty. W metodzie onCreate() ustanawiamy widok
treci i wczamy debugowanie menedera fragmentw. Widzimy nastpnie dwie metody zwizane z konfigurowaniem opcji menu. Wybr poszczeglnych opcji menu powoduje wywoanie
rnych metod o prostej budowie. Kada z tych metod wykonuje praktycznie takie samo zadanie: pozyskuje transakcj fragmentu, a nastpnie tworzy i wywietla dany fragment. Zwrmy
uwag, e kady fragment posiada niepowtarzalny znacznik, ktry zostaje dostarczony metodzie
show(). Znacznik ten zostaje powizany z fragmentem w menederze, dziki czemu moemy pniej lokalizowa fragmenty. Fragmenty mog rwnie same okrela warto znacznika
za pomoc metody getTag() klasy Fragment.
Ostatni definicj metody w naszej gwnej aktywnoci jest onDialogDone(), ktra jest czci
implementowanego interfejsu OnDialogDoneListener. Jak wida, omawiana metoda zwrotna
zawiera znacznik wywoujcego fragmentu, warto logiczn wskazujc, czy fragment zosta anulowany, oraz komunikat. Dla naszych celw wystarczy wiedza, e komunikat zostaje wywietlony
w oknie LogCat; jest on rwnie prezentowany uytkownikowi za pomoc kontrolki Toast.
Jak wida, mamy do czynienia z bardzo prostym interfejsem. Wybralimy tylko jedn metod
zwrotn dla tego interfejsu. Metoda ta koniecznie musi by zaimplementowana przez aktywno. Nasze fragmenty nie musz posiada informacji o szczegach aktywnoci wywoujcej, a jedynie o tym, e aktywno ta musi implementowa interfejs OnDialogDoneListener.
android:text="Zachowaj">
</Button>
<Button android:id="@+id/btn_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="Wycofaj">
</Button>
<Button android:id="@+id/btn_help"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="Pomoc">
</Button>
</LinearLayout>
</LinearLayout>
android.app.Activity;
android.app.DialogFragment;
android.app.FragmentTransaction;
android.content.DialogInterface;
android.os.Bundle;
android.util.Log;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.Button;
android.widget.EditText;
android.widget.TextView;
1065
1067
}
public void onClick(View v)
{
OnDialogDoneListener act = (OnDialogDoneListener)getActivity();
if (v.getId() == R.id.btn_save)
{
TextView tv =
(TextView)getView().findViewById(R.id.inputtext);
act.onDialogDone(this.getTag(), false, tv.getText());
dismiss();
return;
}
if (v.getId() == R.id.btn_dismiss)
{
act.onDialogDone(this.getTag(), true, null);
dismiss();
return;
}
if (v.getId() == R.id.btn_help)
{
FragmentTransaction ft =
getFragmentManager().beginTransaction();
ft.remove(this);
}
}
HelpDialogFragment hdf =
HelpDialogFragment.newInstance(R.string.help1);
hdf.show(ft, MainActivity.HELP_DIALOG_TAG);
return;
}
Ukad graficzny okna dialogowego zachty nie rni si od wczeniej tworzonych ukadw graficznych. Widzimy w nim kontrolk TextView zawierajc tekst zachty, kontrolk EditText,
w ktrej uytkownik wprowadza wasne dane, oraz trzy przyciski, zapewniajce obsug, kolejno,
zachowania danych wejciowych, wycofania (na przykad anulowania) fragmentu wywietlajcego okna dialogowe oraz wywietlania okna dialogowego pomocy.
Kod klasy PromptDialogFragment na pocztku niczym si nie rni od innych wczeniej
utworzonych fragmentw. Znalaza si tu statyczna metoda newInstance() suca do tworzenia nowych obiektw, a w jej wntrzu wywoujemy domylny konstruktor i generujemy pakiet
argumentw, ktry zostaje nastpnie doczony do tego obiektu. Nastpnie Czytelnik zapewne
spostrzeg co nowego w metodzie onAttach(). Chodzi o to, aby si upewni, e aktywno
doczajca posiada zaimplementowany interfejs OnDialogDoneListener. Aby to sprawdzi,
rzutujemy aktywno przekazywan do interfejsu OnDialogDoneListener. Jeeli interfejs nie
jest zaimplementowany w aktywnoci, zostanie wywietlony wyjtek ClassCastException.
Moglibymy sprbowa obej ten wyjtek i wprowadzi bardziej eleganckie rozwizanie, zaley
nam jednak na utrzymaniu jak najmniejszej zoonoci kodu.
1069
i umiecilimy j na stosie drugoplanowym. W wyniku tego fragment wywietlajcy okno zachty znika z pojemnika widokw, cigle jest jednak dostpny w menederze fragmentw oraz
z poziomu stosu drugoplanowego. Na jego miejscu pojawia si nowy fragment wywietlajcy
okno pomocy, w ktrym zawarto widoczny dla uytkownika tekst pomocy. W momencie wycofania omawianego fragmentu jego wpis zostanie usunity ze stosu, w wyniku czego zniknie
(zarwno sprzed oczu uytkownika, jak rwnie z poziomu menedera fragmentw) i zostanie
przywrcony fragment wywietlajcy okno zachty. Caa operacja jest w rzeczywistoci banalna
do przeprowadzenia. Kod zamieszczony na listingu 29.21 jest niezwykle prosty, a jednoczenie
niesamowicie skuteczny; dziaa bezbdnie nawet wtedy, gdy podczas wywietlania okna dialogowego zmieni si tryb wywietlania.
Listing 29.21. Ukad graficzny i kod Java klasy HelpDialogFragment
<?xml version="1.0" encoding="utf-8"?>
android.app.DialogFragment;
android.os.Bundle;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.Button;
android.widget.TextView;
Mamy tu do czynienia z kolejnym fragmentem wywietlajcym okno dialogowe, jeszcze prostszym od prezentowanego wczeniej. Zadaniem tego fragmentu jest wywietlanie tekstu pomocy.
Na ukad graficzny skada si kontrolka TextView i przycisk Zamknij. Kod Java powinien
wyglda ju znajomo dla Czytelnika. Znajdziemy w nim metody newInstance() suc do
utworzenia nowego fragmentu, ktry wywietla okno pomocy, onCreate() pozwalajc na
zdefiniowanie stylu i motywu okna dialogowego, a take onCreateView() generujc hierarchi widokw. W naszym przypadku potrzebny jest nam zasb typu string, ktrym posuymy
si do zapenienia kontrolki TextView, zatem uzyskujemy dostp do zasobw z poziomu aktywnoci i wybieramy identyfikator zasobu przekazany w metodzie newInstance(). Na kocu
metoda onCreateView() ustanawia procedur obsugi kliknicia przycisku Zamknij. W tym
przypadku w czasie zamykania okna system nie wykonuje niczego nadzwyczajnego.
1071
Istniej dwa sposoby wywoywania tego fragmentu: z poziomu aktywnoci oraz poprzez fragment wywietlajcy okno zachty. Jeeli wybierzemy pierwsze rozwizanie, pniejsze wycofanie tego fragmentu poskutkuje jego usuniciem ze szczytu stosu i wywietleniem gwnej
aktywnoci znajdujcej si pod spodem. Jeeli wywietlimy ten fragment z poziomu fragmentu
wywietlajcego okno zachty, jego wycofanie spowoduje wycofanie transakcji fragmentu (poniewa fragment ten by czci transakcji umieszczonej w stosie drugoplanowym) i usunicie
okna pomocy wraz z jednoczesnym przywrceniem fragmentu wywietlajcego okno zachty.
W efekcie uytkownik ponownie ujrzy okno zachty.
android.app.AlertDialog;
android.app.Dialog;
android.app.DialogFragment;
android.content.DialogInterface;
android.os.Bundle;
W przypadku tego fragmentu nie potrzebujemy ukadu graficznego, poniewa klasa AlertBuilder
zapewnia jego utworzenie. Czytelnik zauway, e ten fragment jest tworzony tak jak pozostae, jednak zamiast metody zwrotnej onCreateView() stosujemy w tym przypadku metod
onCreateDialog(). Implementujemy albo metod onCreateView(), albo onCreateDialog(),
nigdy obydwie naraz. Element przekazywany przez metod onCreateDialog() nie jest widokiem, lecz oknem dialogowym. Od tego miejsca moemy zacz wykorzystywa informacje
przedstawione w rozdziale 8., aby utworzy okno dialogowe w standardowy sposb. Rnica
polega na tym, e w celu uzyskania dostpu do parametrw okna dialogowego powinnimy
wzi pod uwag pakiet parametrw. W naszej przykadowej aplikacji wykorzystujemy go do
zaprezentowania komunikatu alertu, nie ma jednak przeszkd, aby uzyska dostp do innych
parametrw pakietu.
Zwrmy take uwag, e w przypadku tego typu fragmentu wywietlajcego okno dialogowe
wymagana jest implementacja interfejsu DialogInterface.OnClickListener w klasie fragmentu, co oznacza, e nasz fragment musi implementowa metod zwrotn onClick(). Metoda
ta zostanie wywoana, jeeli uytkownik zacznie w jaki sposb wpywa na okno dialogowe. Po
raz wtry uzyskujemy odniesienie do uruchomionego okna dialogowego oraz wskazanie,
ktry przycisk zosta wcinity. Podobnie jak poprzednio, nie moemy polega na metodzie
onDismiss(), poniewa moe ona zosta wywoana wskutek zmiany konfiguracji urzdzenia.
1073
android:layout_height=" match_parent"
android:gravity="fill"
>
<TextView android:id="@+id/textViewId"
android:layout_width=" match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:text="@string/help_text"
android:textColor="@android:color/black"
android:textSize="25sp"
android:scrollbars="vertical"
android:scrollbarStyle="insideOverlay"
android:scrollbarSize="25dip"
android:scrollbarFadeDuration="0"
/>
</LinearLayout>
Po uruchomieniu aplikacji naley przetestowa wszystkie opcje w rnorodnych trybach wywietlania obrazu. Sprbujmy obrci urzdzenie w momencie wywietlania fragmentw.
Czytelnikowi powinno si spodoba, e okna dialogowe obracaj si wraz z reszt obrazu oraz
e nie trzeba zbytnio przejmowa si kodem odpowiedzialnym za zachowywanie i odczytywanie fragmentw w trakcie zmian konfiguracji.
Mamy nadziej, e kolejnym elementem, jaki Czytelnik doceni, jest atwo nawizywania komunikacji pomidzy fragmentami a aktywnoci. Oczywicie, aktywno zawiera odniesienia
(lub moe je uzyska) do wszystkich istniejcych fragmentw, posiada wic moliwo uzyskania dostpu do metod eksponowanych przez te fragmenty. Nie jest to jedyny sposb komunikowania si fragmentw z aktywnoci. Moemy zawsze zastosowa metody pobierajce wobec
menedera fragmentw w celu odczytania wystpienia zarzdzanego fragmentu, a nastpnie
odpowiednio rzutowa takie odniesienie i bezporednio wywoa metod wobec tego fragmentu.
Stopie odizolowania fragmentw od siebie za pomoc interfejsw oraz poprzez aktywnoci lub
utworzenia sieci zalenoci pomidzy fragmentami zaley od zoonoci aplikacji oraz stopnia
jej wykorzystania.
Za pomoc tych kilku wierszy utworzylimy nowy obiekt CalledFragment, ustawilimy w biecym fragmencie wywoywany fragment jako fragment docelowy oraz za pomoc mechanizmu transakcji dodalimy ten wywoywany fragment do menedera fragmentw oraz do
aktywnoci. Po uruchomieniu wywoywanego fragmentu bdzie mg on wywoa metod
getTargetFragment(), ktra przekae odniesienie do fragmentu wywoujcego. Za pomoc tej
metody wywoywany fragment moe korzysta z metod fragmentu wywoujcego, a nawet uzyska bezporedni dostp do jego skadowych widoku. Na listingu 29.26 zademonstrowalimy
przykadowy kod, w ktrym wywoywany fragment wprowadza tekst bezporednio do interfejsu
uytkownika znajdujcego si we fragmencie wywoujcym.
Listing 29.26. Komunikacja fragmentu docelowego z fragmentem wywoujcym
TextView tv = (TextView)
getTargetFragment().getView().findViewById(R.id.text1);
tv.setText("Ustawiony z poziomu wywoywanego fragmentu");
1075
Poza wzrokiem uytkownika animator obiektu znajduje gwny widok fragmentu i regularnie
wywouje metod setAlpha(), za kadym razem nieznacznie zmieniajc warto parametru.
Czstotliwo powtrze wywoa zaley od interpolatora. Interpolator liniowy wprowadza
wywoania w rwnych odstpach czasowych. Interpolator accelerate_decelerate najpierw
ustanawia mniejsze wartoci okrelonego parametru na jednostk czasu i stopniowo je zwiksza,
co daje wraenie przypieszenia, natomiast pod koniec odwraca ten proces, przez co wydaje si,
e nastpuje spowolnienie animacji danego wymiaru obiektu.
jest
Obydwa animatory nie musz by ze sob nawet powizane, najlepiej jednak by byo, gdyby
wizualnie do siebie pasoway. Innymi sowy, jeeli jeden fragment zanika, drugi mgby stopniowo pojawi si na miejscu poprzednika. Jeeli jeden fragment wysuwa si z prawego brzegu
ekranu, drugi moe wsun si z lewej strony.
Zasoby animatora mona znale w folderze zestawu SDK, w katalogu zwizanym z waciw
platform, a dalej w podkatalogu /data/res/animator. To wanie tu znajdziemy uywane wczeniej pliki fade_in.xml i fade_out.xml. Moemy utworzy rwnie wasne zasoby. Jeeli zdecydujemy si na ten krok, najlepiej umieci taki plik w katalogu /res/animator naszego projektu.
Plik ten w razie potrzeby mona doda rcznie. Przyjrzyjmy si przykadowi umieszczonemu
na listingu 29.27, gdzie widzimy prosty lokalny plik animatora (slide_in_left.xml).
Listing 29.27. Niestandardowy animator powodujcy wysuwanie si obiektu z lewej strony ekranu
<?xml version="1.0" encoding="utf-8" ?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:valueFrom="-1280"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="x"
android:duration="2000" />
1077
Odnoniki
Poniej zamieszczamy odnoniki do zasobw zawierajcych dodatkowe informacje na tematy
przedstawione w tym rozdziale:
ftp://ftp.helion.pl/przyklady/and3ta.zip znajdziemy tu list wszystkich projektw
utworzonych na potrzeby niniejszej ksiki. Waciwe pliki s umieszczone w katalogu
o nazwie ProAndroid3_R29_Fragmenty. Zamiecilimy w nim take plik Czytaj.TXT,
stanowicy dokadn instrukcj importowania projektw do rodowiska Eclipse.
Doczylimy rwnie projekty ukazujce zastosowanie pakietu kompatybilnoci
fragmentw (Fragment Compatibilty SDK) w starszych wersjach systemu Android.
ApiDemos wrd przykadw umieszczonych w zestawie Android SDK znajdziemy
projekt ApiDemos. Umieszczono w nim kilka aplikacji wykorzystujcych koncepcj
fragmentw, ktre mog nam pomc w jej zrozumieniu.
http://developer.android.com/guide/topics/fundamentals/fragments.html artyku
z poradnika programisty, w ktrym omwiono koncepcj fragmentw.
http://android-developers.blogspot.com/2011/02/android-30-fragments-api.html
wpis stanowicy wstp do nauki posugiwania si fragmentami.
http://android-developers.blogspot.com/2011/02/animation-in-honeycomb.html
wpis wprowadzajcy do nowej struktury animacji oraz do zastosowa animatora
obiektw.
Podsumowanie
W niniejszym rozdziale zaprezentowalimy zupenie now klas Fragment, wprowadzon
w wersji 3.0 Androida, a take jej podklasy i klasy pokrewne, zwizane z menederem oraz
transakcjami. Fragmenty stanowi zupenie nowe, potne rozwizanie, pozwalajce na organizacj funkcjonalnoci oraz powizanych z ni interfejsw uytkownika. Chocia fragmenty
zostay zaprojektowane specjalnie dla tabletw, dostpne bd rwnie w przypadku urzdze
wyposaonych w niewielkie wywietlacze. Bd pomocne w rozdzielaniu logiki dziaania aplikacji na niewielkie, proste w uytkowaniu skadowe, ktre bd wykorzystywane, przenoszone
i zarzdzane na wczeniej niedostpne sposoby. Przedstawilimy jedn z ciekawszych nowych
funkcji Androida mowa tu o animatorze obiektw, ktry w prosty sposb moe zmodyfikowa przejcia pomidzy fragmentami.
W nastpnym rozdziale zajmiemy si kolejnym istotnym aspektem aplikacji tworzonych z myl
o tabletach, czyli klas ActionBar.
R OZDZIA
30
Analiza klasy ActionBar
Klasa ActionBar jest nowym interfejsem API wprowadzonym w wersji 3.0 Androida. Pozwala ona na modyfikowanie paska tytuowego aktywnoci. W starszych
wersjach systemu pasek tytuowy aktywnoci przechowywa wycznie jej nazw.
Wraz z rozwojem systemu Android przejmuje on coraz wicej wzorcw interfejsu
uytkownika wykorzystywanych w komputerach biurkowych. W aplikacji biurowej
mamy do czynienia z paskiem tytuowym, paskiem menu oraz z pewn liczb przyciskw na pasku narzdzi. Implementacja interfejsu ActionBar stanowi odzwierciedlenie takiej struktury paska tytuowego i menu, spotykanej w komputerach stacjonarnych.
Interfejs ActionBar opiera si zwaszcza na modelu paska tytuowego i paska menu
spotykanego w przegldarkach WWW. Zosta on zaprojektowany w taki sposb, aby
programici mogli uwzgldnia w aplikacjach schematy nawigacji znane wanie
z przegldarek.
Kluczowym zadaniem paska narzdzi jest zagwarantowanie uytkownikowi byskawicznego dostpu do najczciej wykorzystywanych narzdzi bez koniecznoci
przeszukiwania menu opcji lub menu kontekstowych.
We wspczesnej literaturze informatycznej dogodny dostp do dziaa
zwany jest czsto afordancj, co odnosi si do wygodnego odkrywania czy
te wykonywania dziaa. Na kocu rozdziau zamiecilimy kilka adresw
URL kierujcych do informacji na temat afordancji.
1080
Sposb, w jaki adaptery obiektw typu Spinner oraz obiekty nasuchujce listy s
wykorzystywane do wspdziaania z paskiem wywietlajcym listy.
Mechanizm oddziaywania przycisku ekranu startowego oraz paska dziaania na
struktur menu.
Sposb umieszczania przyciskw wewntrz paska stanu oraz ich obsugiwanie.
Zademonstrujemy te pojcia poprzez utworzenie trzech rnych aktywnoci. Kada z nich posuy do zaprezentowania innego rodzaju paska dziaania. W ten sposb Czytelnik zyska moliwo przetestowania zachowania tej klasy w kadym z trzech trybw. Najpierw przyjrzyjmy
si jednak elementom paska dziaania widocznym dla uytkownika.
Jest to zrzut ekranu z dziaajcej przykadowej aplikacji, ktra zostaa omwiona w dalszej czci
tego rozdziau. Pasek dziaania widoczny na rysunku 30.1 skada si z piciu czci. S to (liczc
od lewej do prawej):
Obszar przycisku ekranu startowego. Przycisk widoczny w lewej grnej czci
paska dziaania jest czasami nazywany przyciskiem ekranu startowego (ang. home
icon). Przypomina on nieco kontekst nawigacji z przegldarek WWW, w ktrych
kliknicie przycisku strony startowej spowoduje jej uruchomienie. Przekonamy si
pniej, e kliknicie tego przycisku spowoduje wysanie metody zwrotnej do opcji
menu posiadajcej identyfikator android.R.id.home.
Obszar tytuu. W tym obszarze umieszczamy tytu aplikacji bd aktywnoci.
1081
Aktywno z rysunku 30.1 oprcz paska dziaania zawiera rwnie widok tekstowy debugowania, w ktrym s wywietlane informacje dotyczce wykonanych dziaa. Dziaania te mog by
wynikiem kliknicia ktrej z zakadek, przycisku ekranu startowego, opcji menu dziaania lub
opcji waciwego menu.
Zastanwmy si teraz nad sposobem implementacji trzech wymienionych rodzajw paska dziaania: paska zakadek, paska wywietlajcego list oraz standardowego paska dziaania. Skoro
zaprezentowalimy zrzut ekranu aplikacji z paskiem dziaania w trybie paska zakadek, zajmiemy si najpierw tym przypadkiem.
1082
1083
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public abstract class DebugActivity
extends Activity
implements IReportBack
{
1084
if (item.getItemId() == R.id.menu_da_clear){
this.emptyText();
return true;
}
boolean b = onMenuItemSelected(item);
if (b == true)
{
return true;
}
return super.onOptionsItemSelected(item);
}
protected TextView getTextView(){
return
(TextView)this.findViewById(this.debugTextViewId);
}
protected void appendMenuItemText(MenuItem menuItem){
String title = menuItem.getTitle().toString();
appendText("MenuItem:" + title);
}
protected void emptyText(){
TextView tv = getTextView();
tv.setText("");
}
protected void appendText(String s){
TextView tv = getTextView();
tv.setText(s + "\n" + tv.getText());
Log.d(tag,s);
}
public void reportBack(String tag, String message)
{
this.appendText(tag + ":" + message);
Log.d(tag,message);
}
public void reportTransient(String tag, String message)
{
String s = tag + ":" + message;
Toast mToast =
Toast.makeText(this, s, Toast.LENGTH_SHORT);
mToast.show();
reportBack(tag,message);
Log.d(tag,message);
}
}//eof-class
Podstawowym zadaniem tej bazowej klasy jest wywietlenie aktywnoci zawierajcej widok
debugowania. Widok ten suy do prezentowania komunikatw przesyanych przez metod
reportBack(). Aktywno ta bdzie nadrzdna w stosunku do aktywnoci paskw dziaania.
1085
Gwnym zadaniem tej klasy jest zagwarantowanie wsplnego kodu, przetwarzanego w odpowiedzi na kliknicia elementw menu. Elementy te posu nam do zmiany trzech aktywnoci
reprezentujcych tryby wywietlania paska dziaania. Po zmianie trybu bdziemy mogli przetestowa kontrolujc go aktywno.
Aktywno BaseActionBarActivity widzimy na listingu 30.3.
Listing 30.3. Wsplna klasa bazowa dla aktywnoci przechowujcej paski dziaania
// BaseActionBarActivity.java
package com.androidbook.actionbar;
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public abstract class BaseActionBarActivity
extends DebugActivity
{
private String tag=null;
public BaseActionBarActivity(String inTag)
{
super(R.menu.menu,
R.layout.main,
R.id.textViewId,
inTag);
tag = inTag;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
TextView tv = this.getTextView();
tv.setText(tag);
}
protected boolean onMenuItemSelected(MenuItem item)
{
1086
if (getNavMode() ==
ActionBar.NAVIGATION_MODE_LIST)
{
this.reportBack(tag,
"Ju przebywamy w aktywnoci paska wywietlajcego list");
}
else{
this.invokeListNav();
}
return true;
}
if (item.getItemId() == R.id.menu_invoke_standardnav){
if (getNavMode() ==
ActionBar.NAVIGATION_MODE_STANDARD)
{
this.reportBack(tag,
"Ju przebywamy w aktywnoci standardowego paska dziaania");
}
else{
this.invokeStandardNav();
}
return true;
}
return false;
}
private int getNavMode(){
ActionBar bar = this.getActionBar();
return bar.getNavigationMode();
}
private void invokeTabNav(){
Intent i = new Intent(this,
TabNavigationActionBarActivity.class);
startActivity(i);
}
1087
Ta bazowa aktywno upraszcza rwnie kod pochodnych aktywnoci paskw dziaania, w tym
rwnie aktywnoci paska zakadek.
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class BaseListener
{
protected IReportBack mReportTo;
protected Context mContext;
public BaseListener(Context ctx, IReportBack target)
{
mReportTo = target;
mContext = ctx;
}
}
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class TabListener extends BaseListener
implements ActionBar.TabListener
{
private static String tag = "tc>";
public TabListener(Context ctx,
IReportBack target)
{
super(ctx, target);
}
1088
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class TabNavigationActionBarActivity
extends BaseActionBarActivity
{
private static String tag =
"Klasa ActionBarActivity do nawigacji za pomoc zakadek";
public TabNavigationActionBarActivity()
{
super(tag);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
workwithTabbedActionBar();
}
public void workwithTabbedActionBar()
{
ActionBar bar = this.getActionBar();
bar.setTitle(tag);
1089
bar.setNavigationMode(
ActionBar.NAVIGATION_MODE_TABS);
TabListener tl = new TabListener(this,this);
Tab tab1 = bar.newTab();
tab1.setText("Zakadka1");
tab1.setTabListener(tl);
bar.addTab(tab1);
Tab tab2 = bar.newTab();
tab2.setText("Zakadka2");
tab2.setTabListener(tl);
bar.addTab(tab2);
}
}//eof-class
W kolejnych podpunktach omwimy teraz kod skadajcy si na aktywno paska zakadek (listing 30.6). Zwrcimy Czytelnikowi uwag na kady aspekt pracy z paskiem zakadek. Rozpoczniemy od kodu uzyskujcego dostp do paska dziaa, ktry stanowi cz aktywnoci.
Po skonfigurowaniu trybu wywietlania zakadek widzimy wiele zwizanych z nimi metod wewntrz klasy ActionBar, z ktrych moemy korzysta. W kodzie z listingu 30.6 wykorzystalimy te metody do dodania dwch zakadek do paska dziaa. Do inicjalizacji tych zakadek uylimy z kolei obiektu nasuchujcego zakadki, zdefiniowanego na listingu 30.5.
Poniej umiecilimy krtki wycinek kodu z listingu 30.6 ukazujcy sposb dodawania zakadki
do paska zada:
Tab tab1 = bar.newTab();
tab1.setText("Zakadka1");
1090
tab1.setTabListener(tl);
bar.addTab(tab1);
Jeeli zapomnimy wywoa metod setTabListener() wobec zakadki dodawanej do paska dziaania, zostanie wywietlony komunikat o bdzie wskazujcy na brak obiektu nasuchujcego.
Warto zwrci uwag na kilka elementw tego ukadu graficznego. Wprowadzamy biay kolor
ta widoku tekstowego. W ten sposb zrzuty ekranu bd nieco wyraniejsze. Rwnie w podobnym celu zwikszylimy rozmiar czcionki.
Konfigurujemy take widok tekstowy w taki sposb, aby mona go byo przewija. Chocia zazwyczaj do takich zastosowa uywane s widoki ScrollView, widok tekstowy sam w sobie posiada dostpn funkcj przewijania. Oprcz uruchomienia funkcji przewijania w pliku XML
musimy take wywoa metod setMovementMethod() wobec tego widoku tekstowego tak
jak zostao pokazane na listingu 30.8.
Listing 30.8. Wprowadzenie funkcji przewijania w widoku tekstowym
TextView tv = this.getTextView();
tv.setMovementMethod(
ScrollingMovementMethod.getInstance());
1091
W pliku menu z listingu 30.9 wykorzystalimy trzy ikony (creep001, 002 i 003)
pochodzce ze strony www.androidicons.com. Zgodnie z informacjami
zawartymi na tej stronie ikony te s objte licencj Creative Commons 3.0.
1092
Wywietlanie menu
Urzdzenia pracujce pod kontrol systemu Android w wersjach 2.3 i wczeniejszych czsto byy
wyposaone we wbudowany przycisk menu. Emulator dziaajcy w trybie dla wersji 3.0 systemu
nie symuluje fizycznych przyciskw powrotu do menu startowego, cofania lub menu, mog by
one jednak wci dostpne w niektrych urzdzeniach.
Jak widzimy na rysunku 30.2, przyciski ekranu startowego i cofania s teraz przyciskami programowymi, umieszczonymi u dou ekranu. Jednak przycisk menu jest wywietlany w kontekcie aplikacji, a dokadniej jako cz paska dziaa, i jest umieszczony w prawym grnym
rogu ekranu.
1093
1094
Aplikacja zostaa zaprojektowana w taki sposb, aby kade dziaanie na pasku dziaania zostao
zapisane w widoku debugowania. Podczas testowania aplikacji Czytelnik moe sprawdzi, co
si stanie po wykonaniu nastpujcych czynnoci:
Jeeli klikniemy przycisk strony startowej, ujrzymy w widoku tekstowym komunikat,
e wcinito przycisk ekranu startowego.
Jeeli klikniemy zakadk Zakadka1, pojawi si informacja, e Zakadka1 zostaa
ponownie wybrana.
Jeeli klikniemy zakadk Zakadka2, zostan wywietlone dwa komunikaty.
Pierwszy z nich informuje, e Zakadka1 schodzi z pierwszego planu, a z drugiego
dowiadujemy si o klikniciu Zakadka2. Komunikaty te zostaj dostarczone przez
zaprezentowane na listingu 30.5 obiekty nasuchujce zakadki.
Jeeli klikniemy widoczne po prawej stronie przyciski dziaania, zauwaymy, e zostan
wywoane powizane z nimi elementy menu, a tym samym zostan wstawione kolejne
komunikaty w widoku tekstowym.
Po rozwiniciu menu zobaczymy elementy menu suce do wywoywania innych
aktywnoci, ktre pozwalaj na zaprezentowanie pozostaych trybw wywietlania
paska dziaania. Musimy jednak z tym poczeka do zakoczenia procesu tworzenia
projektu. Do tego czasu klikanie tych elementw spowoduje jedynie wywietlanie
komunikatw w widoku tekstowym.
Na tym zakoczymy implementacj nie tylko aktywnoci wywietlajcej pasek zakadek, lecz
rwnie konfiguracj podstawowej struktury, dziki ktrej utworzenie pozostaych aktywnoci
okae si znacznie atwiejsze. Przejdmy teraz do paska dziaa zdefiniowanego w trybie wywietlania listy.
1095
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class SimpleSpinnerArrayAdapter
extends ArrayAdapter<String>
implements SpinnerAdapter
{
public SimpleSpinnerArrayAdapter(Context ctx)
{
super(ctx,
android.R.layout.simple_spinner_item,
new String[]{"raz","dwa"});
this.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
}
public View getDropDownView(
int position, View convertView, ViewGroup parent)
{
return super.getDropDownView(
position, convertView, parent);
}
}
Nie istnieje klasa bezporednio implementujca wymagany przez t list interfejs SpinnerAdapter.
Utworzylimy zatem pochodn klasy ArrayAdapter i wprowadzilimy do niej prost implementacj wspomnianego interfejsu. Na kocu rozdziau zamiecilimy adres URL do dalszych
materiaw powiconych tego typu adapterom. Przejdmy teraz do obiektu nasuchujcego listy.
//
1096
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class ListNavigationActionBarActivity
extends BaseActionBarActivity
{
private static String tag=
"Klasa ActionBarActivity";
public ListNavigationActionBarActivity()
{
super(tag);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
1097
super.onCreate(savedInstanceState);
workwithListActionBar();
}
public void workwithListActionBar()
{
ActionBar bar = this.getActionBar();
bar.setTitle(tag);
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
bar.setListNavigationCallbacks(
new SimpleSpinnerArrayAdapter(this),
new ListListener(this,this));
}
}//eof-class
Istotna cz kodu na listingu 30.14 zostaa zaznaczona pogrubion czcionk. Sam kod nie jest
zbyt skomplikowany: w metodach zwrotnych paska dziaania konfigurujemy adapter obiektu
typu Spinner oraz obiekt nasuchujcy jako list.
Efektem usunicia znakw komentarza z tego fragmentu kodu bdzie powizanie elementu
menu. W konsekwencji bdzie mona wywoa omawian aktywno.
1098
Na rysunku 30.3 widzimy nierozwinit list, znajdujc si tu obok tytuu aktywnoci. W tym
samym miejscu s umieszczane zakadki, jeli pasek dziaania pracuje w trybie wywietlania zakadek. Jeeli klikniemy teraz element nazwany raz, lista zostanie rozwinita. Widzimy to na
rysunku 30.4.
1099
Aktywno przechowujca
standardowy pasek dziaania
W niniejszym podrozdziale zapoznamy si z funkcjonowaniem standardowego paska dziaania.
Wprowadzimy now aktywno, w ktrej ustanowimy standardowy tryb nawigacji paska dziaania. Nastpnie przyjrzymy si wygldowi i zachowaniu tego paska dziaania.
Podobnie jak miao to miejsce w przypadku klasy ListNavigationActionBarActivity, wikszo operacji jest wykonywana przez klasy bazowe, zatem implementacja i testowanie omawianej aktywnoci nie powinny nastrcza zbyt duych problemw. Aby zaimplementowa t
aktywno, potrzebny nam bdzie nastpujcy plik:
StandardNavigationActionBarActivity.java plik ten suy do skonfigurowania
standardowego trybu wywietlania paska dziaania (listing 30.17).
Po utworzeniu tego pliku musimy zmodyfikowa nastpujce dwa pliki:
BaseActionBarActivity.java naley usun znaki komentarza wywoania
aktywnoci wywietlajcej standardowy pasek dziaania po klikniciu odpowiedniego
elementu menu (zmiany zostay pokazane na listingu 30.18, a oryginalny kod
na listingu 30.3).
AndroidManifest.xml musimy zdefiniowa t now aktywno w pliku manifecie
(definicj tej aktywnoci widzimy na listingu 30.19, a naley j umieci w pliku
AndroidManifest.xml umieszczonym na listingu 30.11).
Zajmiemy si teraz kadym z wymienionych plikw.
1100
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class StandardNavigationActionBarActivity
extends BaseActionBarActivity
{
private static String tag=
"Standardowa nawigacja za pomoca klasy ActionBarActivity";
public StandardNavigationActionBarActivity()
{
super(tag);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
workwithStandardActionBar();
}
public void workwithStandardActionBar()
{
ActionBar bar = this.getActionBar();
bar.setTitle(tag);
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
1101
tab2.setTabListener(tl);
bar.addTab(tab2);
}
}//eof-class
Jedyn czynnoci, jak naley wykona w celu zdefiniowania standardowego paska dziaania,
jest wybr trybu nawigacji. Na listingu 30.17 wyrnilimy odpowiedni fragment kodu pogrubion czcionk.
Na listingu 30.17 wprowadzilimy rwnie kod generujcy zakadki, aby sprawdzi, co
si z nimi stanie w standardowym trybie nawigacji. Nasze testy wykazay, e obecno
tego fragmentu kodu nie generuje adnych bdw, ale jest on po prostu ignorowany.
Zanim dowiemy si, jak wyglda standardowy pasek dziaania, musimy wprowadzi dwie zmiany
do ju istniejcych plikw.
Po usuniciu znakw komentarza z tego fragmentu element menu zostanie powizany z kodem w sposb umoliwiajcy wywoywanie klasy StandardNavigationActionBarActivity.
1102
Pierwsz rzecz przykuwajc uwag Czytelnika bdzie zapewne brak obszaru, w ktrym wczeniej byy wywietlane zakadki oraz lista. Jeeli klikniemy teraz widoczne po prawej stronie
przyciski dziaania, informacje o ich wywoaniu zostan zapisane w widoku debugowania.
Kliknijmy teraz przycisk ekranu startowego. Rwnie w tym przypadku w widoku debugowania zostanie zapisana sygnatura wywoania. Po wykonaniu tych trzech klikni widok debugowania bdzie wyglda tak jak na rysunku 30.6.
Odnoniki
Ponisze adresy URL okazay si bardzo przydatne w trakcie wyszukiwania materiaw do niniejszego rozdziau. Zawieraj one rwnie ogrom dodatkowych informacji. Dodatkowo ostatnie
cze prowadzi do strony, z ktrej moemy pobra gotowe projekty.
Donald A. Norman, The Design of Everyday Things. Ksika ta rozpowszechnia
wspomnian wczeniej koncepcj wizualnej percepcji, zwan afordancj, w stosunku
do dziedziny HCI (ang. Human-Computer Interaction interakcja czowiek komputer).
Termin ten jest stosowany coraz czciej w literaturze powiconej interfejsom
uytkownika systemu Android. Omawiany w tym rozdziale pasek dziaania jest
zachwalany jako jedna z gwnych afordancji interfejsu uytkownika.
1103
1104
Podsumowanie
Jak wida, paski dziaania nie stanowi wcale takiej tajemnicy. S one znanym paradygmatem,
stosowanym podczas tworzenia aplikacji biurowych. Pocztkujcym programistom zachowanie
klasy, ktra w zalenoci od wyboru jednego z trzech trybw zyskuje odmienne waciwoci,
moe si wyda problematyczne. Nigdy nie wiadomo, czy posiadany podzbir klas wykona zamierzone zadanie. Jednak rnica pomidzy tymi trybami jest tak maa, e ich umieszczenie
w obrbie jednej klasy naprawd nie byo zym pomysem.
Wydaje si, e jedn z intencji wprowadzenia paskw dziaania byo skierowanie procesu projektowania w stron modelu nawigacji sieciowej.
Wydaje si take, e projektanci systemu Android staraj si powiza pasek dziaania z fragmentami w celu osignicia podanej jednorodnoci interfejsu uytkownika. Jeeli istnieje
potrzeba wykorzystania w aplikacji kilku wymiennie uywanych aktywnoci, zalecane jest rozwaenie, czy taki efekt mona osign, wprowadzajc fragmenty, a nie dodatkowe aktywnoci.
Fragmenty maj wiele udogodnie, zwaszcza zwizanych z zarzdzaniem ich stanem w przypadku obracania urzdzenia, a tym samym zmiany jego konfiguracji. Aktywno przechowujca
fragmenty utrzymuje stan pomidzy zmianami konfiguracji. Fragmenty zostay omwione dokadniej w rozdziale 29.
W tym rozdziale zaprezentowalimy take jeden ze sposobw wprowadzania jednolitoci projektowej za pomoc bazowej aktywnoci. Podobny efekt mona rwnie osign, wprowadzajc delegacj zamiast dziedziczenia. Istnieje take moliwo zapoyczenia znanych wzorcw,
stosowanych do tworzenia gwnych stron witryn sieciowych, oraz sprawdzenia, jak w tej roli
spisuj si klasy rodowiska Android SDK.
Funkcja paska dziaania zostaa wprowadzona dopiero w wersji 3.0 Androida. Na biec chwil
nie wiadomo, czy zostan utworzone odpowiednie biblioteki przeznaczone dla starszych wersji
systemu.
Istniej rwnie pewne rozbienoci pomidzy dokumentacj a faktycznym stanem interfejsw
API. W dokumentacji znalazo si stwierdzenie, e istniej tylko trzy tryby wywietlania paska
dziaania, jednak w interfejsie mona znale dodatkowy tryb, zwany trybem rozwijalnego menu
(ang. dropdown navigation mode). W trakcie testowania zachowywa si on dokadnie tak samo
jak tryb wywietlania listy, z t jedn rnic, e znikn tytu aktywnoci.
Moemy rwnie za pomoc flag kontrolowa elementy wywietlane w pasku dziaania. Jest to
do proste rozwizanie, zatem wystarczy zajrze do dokumentacji omawianego interfejsu API.
R OZDZIA
31
Dodatkowe zagadnienia
zwizane z wersj 3.0 systemu
1106
W rozdziale 22. zamiecilimy list ukadw graficznych i widetw, ktre mog stanowi cz
widoku zdalnego. Widoki zbiorcze, takie jak listy czy siatki, nie byy klasyfikowane jako elementy skadowe widetw w wersji 2.3 systemu. Sytuacja ulega zmianie w wersji 3.0, dziki
czemu uzyskujemy wicej moliwoci na ekranie startowym. Wydanie 3.0 oferuje rwnie miniaturow architektur pozwalajc na asynchroniczne wczytywanie i wywietlanie danych
w tego typu zbiorczych widetach. W wykorzystaniu tej waciwoci przydadz nam si nowe
klasy i metody.
Zajmiemy si najpierw teoretycznym omwieniem tych zagadnie, a nastpnie, w celu utrwalenia przekazanej wiedzy, zademonstrujemy je na dziaajcych przykadach. Rozpocznijmy od
omwienia nowych widokw zdalnych, dostpnych w wersji 3.0 systemu.
1107
AnalogClock,
Button,
Chronometer,
ImageButton,
ProgressBar,
ListView,
GridView,
StackView,
TextView,
DateTimeView,
ImageView,
AdapterViewFlipper,
ViewFlipper.
W kolejnych wersjach systemu do tej listy bd zapewne dodawane kolejne elementy. Gwnym czynnikiem decydujcym o tym, czy dany obiekt stanowi element skadowy widoku zdalnego, jest jego obecno w interfejsie RemoteViews.RemoteView.
Majc t wiedz, wykorzystajmy rodowisko Eclipse do sprawdzenia, ktre klasy uwzgldnione
w danym projekcie mog by czci widoku zdalnego. W tym celu:
1. Wprowad w kodzie rdowym instrukcj import odnoszc si do interfejsu
RemoteView.
2. Zaznacz nazw tego interfejsu.
3. Kliknij nazw interfejsu prawym przyciskiem myszy i przejd do zakadki References.
4. Wybierz odniesienia do tego interfejsu.
Zostanie wywietlona lista klas przechowywanych w interfejsie RemoteView.
1108
Moemy skonfigurowa rwnie zdarzenia onClick wobec zdalnych widokw widetu, umoliwiajc tym samym uruchomienie odpowiednich intencji opartych na zdarzeniach. Intencje te
mog wywoywa dowolne skadniki, w tym takie jak wysyanie komunikatw do odbiorcy
AppWidgetProvider.
W oglnej perspektywie s to jedyne czynnoci przeprowadzane w widetach ekranu startowego.
Reszt stanowi jedynie mechanizmy implementacji oraz rne wariacje omwionych podstawowych koncepcji.
Jednak a do wersji 2.3 Androida widoki bazujce na licie nie byy klasyfikowane jako elementy
widoku zdalnego i nie istnia skuteczny mechanizm wypeniania widokw zdalnych przypominajcych list. W wersji 3.0 systemu dodano nastpujce klasy, suce do obsugi widokw
zdalnych w postaci listy:
RemoteViewsFactory klasa ta umoliwia zapenienie widoku zdalnego w podobny
sposb, jak adaptery listy zapeniaj standardowe widoki listy. Jest to klasa otaczajca
adapter widoku listy, dziki czemu w sposb asynchroniczny dostarcza pojedyncze
widoki zdalne do widoku zdalnego przyjmujcego posta listy.
RemoteViewsService mamy tu do czynienia z usug odpowiedzialn za
przekazywanie klasy RemoteViewsFactory do obiektu RemoteViews. Zadaniem klasy
AppWidgetProvider jest poczenie takiej usugi ze zdalnym widokiem w postaci listy.
Dokonujemy tego poprzez doczenie intencji wywoujcej t usug do wspomnianego
widoku zdalnego. Usuga ta umoliwia przeduenie czasu istnienia procesu
przechowujcego klas AppWidgetProvider. W przeciwnym wypadku po przekazaniu
odbiorcy komunikatw proces ten zostanie odzyskany. W rozdziale 14. zostaa
omwiona symbiotyczna relacja pomidzy odbiorcami komunikatw
a dugoterminowymi usugami.
W celu zapewnienia obsugi widokw zdalnych w postaci listy w wersji 3.0 systemu znalazy si
nastpujce nowe metody interfejsu API:
RemoteViews.setPendingIntentTemplate() metoda ta pozwala na ustawienie
szablonu intencji oczekujcej wobec zdalnego widoku w celu reagowania na zdarzenia
klikni poszczeglnych elementw listy. Zajmiemy si koncepcj szablonu po
omwieniu pozostaych szczegw.
RemoteViews.setOnClickFillIntent() jest ona ustawiana wobec pojedynczych
elementw listy i cile wsppracuje z powysz metod.
Dziki cznemu wykorzystaniu tych dwch dodatkowych metod moemy reagowa na kliknicia poszczeglnych elementw listy zawartej w widoku zdalnym. Metody te zaprojektowano
w taki sposb, aby naleao ustawia jak najmniej intencji oczekujcych.
W trakcie omawiania przykadowej aplikacji przeanalizujemy dokadnie wymienione klasy
i metody. Poniej pokazalimy, w jaki sposb moemy je zastosowa podczas pracy z widokami
listy umieszczonymi w widecie ekranu startowego. Podczas przegldania poszczeglnych etapw warto zaglda do oglnego omwienia mechanizmu dziaania widetw ekranu startowego (zawarlimy je na pocztku podrozdziau):
1. Przygotuj zdalny ukad graficzny. Utwrz odpowiedni ukad graficzny, w ktrym
znajdzie si widok listy. Zdalny ukad graficzny nie rni si od standardowego
ukadu, lecz moemy w nim umieszcza jedynie widoki dozwolone dla widoku
zdalnego. Zasada jest taka sama jak w przypadku wszelkich innych widetw ekranu
startowego (co zostao wyranie powiedziane w rozdziale 22.).
1109
1110
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center">
<ListView android:id="@+id/listwidget_list_view_id"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/listwidget_empty_view_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone"
android:textColor="#ffffff"
android:text="Widok zawierajcy puste rekordy"
android:textSize="20sp" />
</FrameLayout>
<TextView
android:id="@+id/listwidget_footer_textview_id"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:text="Widok stopki"
android:background="@drawable/box1"
android:gravity="center"
android:layout_weight="0"/>
</LinearLayout>
Na listingu 31.1 kady wze XML reprezentuje poprawny widok zdalny. Omawiany ukad graficzny jest wywietlany w taki sposb, e w postaci widetu ekranu startowego bdzie si prezentowa tak jak na rysunku 31.1.
1111
Na wzorzec ukadu graficznego z rysunku 31.1 skada si prosty nagwek, cz gwna i stopka.
Nagwek i stopka posiadaj sta wysoko; w tym przypadku zostaa ona ustalona na 30 dp.
Chcemy ponadto, by wysoko czci gwnej bya taka, aby zajmowaa pozosta cz dostpnej wysokoci widetu. Osigniemy to poprzez wprowadzenie wartoci 0 w atrybutach
android:layout_weight nagwka i stopki. Dla czci gwnej wprowadzamy warto 1 atrybutu android:layout_weight oraz match_parent w android:layout_height.
Wze <framelayout> stanowicy cz gwn widetu wymaga krtkiego objanienia. Ze
wszystkich elementw potomnych wybiera on tylko jeden jako widok. W tym przypadku bdzie to <listview>, jeeli lista bdzie zawieraa jakie dane. W przypadku pustego widoku listy
bdzie stosowany pusty widok tekstowy. Konfigurujemy to w klasie RemoteViewsFactory.
W tym ukadzie znajdziemy take graficzny niestandardowy obiekt rysowany (@drawable/box1),
sucy do zaokrglenia rogw. Listing 31.2 zawiera tre pliku box1.xml, ktry naley umieci
w podkatalogu /res/drawable.
Listing 31.2. Plik res/drawable/box1.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="4dp" android:color="#888888" />
<padding android:left="2dp" android:top="2dp"
android:right="2dp" android:bottom="2dp" />
<corners android:radius="4dp" />
</shape>
Skoro ju mamy przykadowy ukad graficzny widetu ekranu startowego, zastanwmy si,
w jaki sposb moemy go wczyta do widoku zdalnego.
1112
appWidgetManager.updateAppWidget(appWidgetId, rv);
}
super.onUpdate(context,appWidgetManager, appWidgetIds);
}
Provider.
Dlaczego to musi by usuga? Dlaczego nie powizalimy klasy fabrykujcej bezporednio z widokiem listy?
Przyczyna jest taka, e klasa AppWidgetProvider jest odbiorc komunikatw, zatem metoda
onUpdate() dostawcy widetw jest ograniczona czasowo przez tego odbiorc. Aby unikn
potencjalnych problemw, do zapeniania widoku listy w Androidzie 3.0 przeznaczono oddzieln usug, wywodzc si z pakietu android.widget.RemoteViewsService. Usuga ta jest
odpowiedzialna za przekazywanie adaptera wykonujcego operacj zapeniania listy. Adapter
musi nalee do typu RemoteViewsService.RemoteViewsFactory. Jest to procedura, ktra
w schematyczny sposb dy do ostatecznego powizania widoku listy z klas fabrykujc.
Na listingu 31.5 prezentujemy przykadowy sposb pisania kodu usugi widoku zdalnego oraz
mechanizm przekazywania przez ni klasy fabrykujcej.
1113
Po zdefiniowaniu takiej usugi zdalnych widokw moemy doczy j do zdalnego widoku listy
za pomoc kodu zamieszczonego na listingu 31.7 (przypominamy, e kod ten jest umieszczony
w metodzie onUpdate() klasy AppWidgetProvider).
Listing 31.7. Powizanie usugi RemoteViewsService z obiektem RemoteViewList
final Intent intent =
new Intent(context, TestRemoteViewsService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId);
intent.setData(
Uri.parse(
intent.toUri(Intent.URI_INTENT_SCHEME)));
rv.setRemoteAdapter(appWidgetId,
R.id.listwidget_list_view_id, intent);
W kodzie z listingu 31.7 najpierw tworzymy jawn intencj poprzez powizanie z ni klasy
RemoteViewsService. Nastpnie wstawiamy do tej intencji dodatkowe dane, pozwalajce na
zidentyfikowanie widetu, dla ktrego bdzie wywoywana usuga. Kolejnym etapem jest
przeprowadzenie tej dziwnej operacji odnoszenia si intencji do samej siebie wczytania
porcji danych do intencji za pomoc identyfikatora tej intencji. W ten sposb intencja staje si
1114
niepowtarzalna, poniewa dodatkowe informacje s doczane do jej danych. Bez tych dodatkowych danych intencje nie s niepowtarzalne. Gdy ju to uzyskamy, moemy doczy intencj
do zdalnego widoku listy poprzez wywoanie metody setRemoteAdapter() i przekazanie jej
identyfikatora widoku listy.
Zajmijmy si omwieniem tych metod oraz zwizanych z nimi wymaga. Zaczniemy od konstruktora.
Konstruktor klasy RemoteViewsFactory
Konstruktor przyjmuje tutaj dwa argumenty (by moe Czytelnik bdzie mia do czynienia
z inn klas fabrykujc przyjmujc inne argumenty). W przypadku widetw klasa ta jest
tworzona przez usug RemoteViewsService (co zostao pokazane na listingu 31.5), zatem
kontekst stanowi tu dostawca widetu, ktry sam w sobie jest odbiorc komunikatw.
Drugim argumentem konstruktora jest intencja. Jest to ta sama intencj, ktra bya wykorzystana w procesie wywoania usugi widoku zdalnego. Po utworzeniu tej intencji (listing 31.7)
i doczeniu jej do zdalnego widoku pozostawia ona zazwyczaj dodatkow warto stanowic
identyfikator widetu.
Obydwie te wartoci (kontekst oraz intencja) mog by utrzymywane w konstruktorze jako
zmienne lokalne, dziki czemu kolejne metody bd mogy z nich korzysta. Przydatne zwaszcza okazuje si pobranie identyfikatora widetu z intencji i zachowanie go w formie zmiennej
lokalnej.
1115
Metoda ta stanowi dopenienie metody onCreate(). Zgodnie z dokumentacj zostaje ona wywoana w momencie odczenia ostatniego adaptera widoku zdalnego powizanego z danym
obiektem (lub klas fabrykujc). Nie jest jednak do koca jasne, kiedy ta metoda zostaje wywoana; nie zauwaylimy jej wywoania ani po usuniciu pojedynczego widetu z ekranu
startowego, ani po usuniciu ostatniego widetu.
Metoda zwrotna getCount()
Sygnatur metody getCount() jest:
public int getCount()
Metoda getCount() przekazuje cakowit liczb elementw z widoku listy. Metoda ta bardzo przypomina analogiczn metod spotykan w adapterach listy, ktre zostay omwione
w rozdziale 6.
Metoda zwrotna getViewAt()
Metoda getViewAt() posiada nastpujc sygnatur:
public RemoteViews getViewAt(int position)
Zadaniem tej metody jest przekazanie zdalnego widoku zgodnego z pozycj w widoku listy.
Zazwyczaj w tej metodzie wczytujemy dostosowany ukad graficzny dla tego widoku zdalnego
na danej pozycji, a nastpnie wprowadzamy w tym widoku wartoci, gdzie pozycja jest wskanikiem wczytywania odpowiednich danych. Na listingu 31.9 znajduje si przykad wczytywania
pojedynczego ukadu graficznego dla elementu listy.
1116
Ukad graficzny, do ktrego odnosimy si na listingu 31.9, moe by zdefiniowany tak, jak
zostao to zaprezentowane na listingu 31.10.
Listing 31.10. Ukad graficzny utworzony na potrzeby pojedynczego elementu listy
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview_widget_list_item_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Tymczasowy tekst"
/>
Metoda ta przekazuje niestandardowy widok wczytywania, ktry pojawia si pomidzy wywoaniem metody getViewAt(position) a jej powrotem. Aby skorzysta z domylnego widoku
wczytywania, wprowadzamy tu warto null.
Metoda zwrotna getViewTypeCount()
Sygnatura metody getViewTypeCount() jest nastpujca:
public int getViewTypeCount()
Jeeli zdalny widok listy posiada tylko jeden typ widoku potomnego, metoda ta powrci
z wartoci 1. Jeeli bdzie dostpna wiksza liczba typw widokw, przekae ona warto rwn
liczbie dostpnych typw.
Metoda zwrotna getItemId()
Sygnatura metody getItemId() to:
public long getItemId(int position)
Metoda ta przekazuje waciwy identyfikator elementu znajdujcego si na danej pozycji w widoku listy. Metoda ta bardzo przypomina analogiczn metod spotykan w adapterach listy,
ktre zostay omwione w rozdziale 6.
1117
Metoda ta powinna powraca z wartoci true, w przypadku gdy identyfikator obiektu z metody getItemId() wskazuje ten sam obiekt. Metoda ta bardzo przypomina analogiczn metod
spotykan w adapterach listy, ktre zostay omwione w rozdziale 6.
Metoda zwrotna onDataSetChanged()
Sygnatura metody onDataSetChanged() jest nastpujca:
public void onDataSetChanged()
Metoda ta jest wywoywana, gdy klasa AppWidgetManager otrzyma informacj o zmianie wprowadzonej w widecie, ktry przechowuje zdalny widok listy. Wywoanie menedera widetw
dotrze w kocu do klasy fabrykujcej w postaci metody onDataSetChanged(). W odpowiedzi musimy skonfigurowa wykorzystywane dane, dziki czemu pozostae metody zwrotne,
takie jak getViewAt() albo getCount(), bd mogy zareagowa na nowe informacje. Zgodnie
z dokumentacj dozwolone s w tym przypadku dugotrwae operacje suce do konfigurowania danych.
Na tym zakoczymy dyskusj dotyczc uwidocznienia widoku listy w widecie. Zajmijmy si
teraz zagadnieniem doczenia zdarze kliknicia do widoku listy, a nawet do jego widokw
potomnych.
Zwrmy uwag na sposb konfiguracji nazwy klasy dostawcy widetu, ktra staje si docelowym skadnikiem intencji. Intencja ta zostanie dostarczona do dostawcy widetu. Dostawca ten
odpowiada jednak rwnie na inne intencje pochodzce z innych dziaa zwizanych z widetem. Aby odrni t intencj od pozostaych, musimy ustawi dla niej jawne dziaanie. Na
listingu 31.12 zosta ukazany przykad.
1118
Listing 31.12. Definiowanie unikatowego dziaania w dostawcy widetu dla zdarzenia kliknicia
onListClickIntent.setAction(
TestListWidgetProvider.ACTION_LIST_CLICK);
Oczywicie, dziaanie TestListWidgetProvider.ACTION_LIST_CLICK jest niestandardowe i najlepiej je definiowa jako cz dostawcy widetu TestListWidgetProvider.
Poniewa kliknicia mog wystpowa wrd wielu wystpie tego widetu, musimy wczyta
identyfikator okrelonego widetu jako dodatkowe dane intencji wywoujcej. Odpowiednie
rozwizanie prezentujemy na listingu 31.13.
Listing 31.13. Wczytywanie identyfikatora widetu do intencji zdarzenia onClick
onListClickIntent.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
Intencja jest ju niemal gotowa do ustanowienia wobec zdarze onClick w zdalnym widoku listy.
Musimy przeprowadzi w niej jeszcze jedn czynno. Gdy intencje maj pojawi si dopiero
pniej, staj si intencjami oczekujcymi. W rozdziaach 5. (dotyczcym intencji) oraz 15.
(w ktrym omwilimy menedery alarmw) Czytelnik znajdzie wicej informacji na temat
intencji oczekujcych.
Intencja oczekujca nie uwzgldnia adnych definiowanych w niej dodatkowych danych, chyba
e informacje te decyduj o jej unikatowoci. Te dodatkowe dane intencji nie s jednak brane
pod uwag, jeeli dana intencja jest unikatowa. Aby rozwiza ten problem, musimy wprowadzi w intencji metod toUri().
Metoda ta pobiera wszystkie dodatkowe dane intencji i generuje dugi cig znakw reprezentujcy intencj. Na kocu tego cigu znakw znajd si dane dodatkowe intencji. Wprowadzenie tego cigu znakw jako porcji danych jest rwnoznaczne z nadaniem intencji unikatowoci. Wynika to z faktu, e te dane zostan pobrane przez intencj gwnie w celu jej odrnienia
od innych intencji. Na listingu 31.14 widzimy przykad definiowania niepowtarzalnej intencji
za pomoc metody toUri().
Listing 31.14. Zastosowanie metody toUri()
onListClickIntent.setData(
Uri.parse(
onListClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
1119
Na listingu 31.15 flaga FLAG_UPDATE_CURRENT oznacza, e po znalezieniu podobnej intencji zostan zaktualizowane jedynie jej dodatkowe dane. Nieco pniej, kiedy bdziemy omawia sposb wykorzystywania intencji oczekujcej przez widoki zdalne, wyjanimy dokadniej, dlaczego
wspomniany proces moe si okaza konieczny.
Po utworzeniu intencji oczekujcej, na przykad takiej, jak zaprezentowalimy na listingu
31.15, moemy ustanowi reakcj na kliknicia widoku listy. Wykorzystujemy tutaj metod
setPendingIntentTemplate() do ustanowienia relacji pomidzy intencj oczekujc a widokiem
listy. Na listingu 31.16 widzimy przykad zastosowania metody setPendingIntentTemplate().
Listing 31.16. Zastosowanie metody setPendingIntentTemplate()
RemoteViews rv;
rv.setPendingIntentTemplate(R.id.listwidget_list_view_id,
onListClickPendingIntent);
1120
Kluczow metod na listingu 31.17 jest setOnClickFillIntent(). Umoliwia nam ona dostarczanie nowej intencji zawierajcej zdefiniowane dane dodatkowe, ktre zostan wczytane.
Dane te zostan pobrane przez wewntrzn struktur i naoone na szablon intencji oczekujcej,
ktry zosta skonfigurowany jako cz zdarzenia onClick.
Na listingu 31.17 pobralimy jedynie tekst z biecego wiersza, nieco go zmodyfikowalimy
i wstawilimy w postaci dodatkowych danych. Dziki widocznemu na tym listingu kodowi po
klikniciu elementu listy bdcej czci widetu pojawi si intencja, ktra zostanie przesana
wraz z dodatkowymi danymi do odbiorcy komunikatw. Przyjrzyjmy si wic, w jaki sposb
naley przygotowa takiego odbiorc, aby odczyta dane zdefiniowane dla kadego elementu listy.
Na listingu 31.19 widzimy mechanizm przesaniania metody onReceive(). Pokazalimy sposb testowania dziaania intencji oraz wywoania metody dealwithListAction(). Na kocu
tej metody musimy wywoa metod onReceive() bazowej klasy dla wszystkich pozostaych
dziaa. Jeeli tego nie zrobimy, sam widet nie bdzie odbiera adnych dziaa.
Listing 31.19. Przesanianie metody onReceive()
@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction()
.equals(TestListWidgetProvider.ACTION_LIST_CLICK))
{
dealwithListAction(context,intent);
return;
1121
Na listingu 31.20 prezentujemy metod dealwithListAction(), w ktrej odczytujemy dodatkowe dane, zaadowane do intencji w kodzie z listingu 31.17.
Listing 31.20. Odpowiadanie na zdarzenie onClick elementu listy
public void dealwithListAction(Context context, Intent intent)
{
String clickedItemText =
intent.getStringExtra(
TestListWidgetProvider.EXTRA_LIST_ITEM_TEXT);
if (clickedItemText == null)
{
clickedItemText = "Bd";
}
clickedItemText =
clickedItemText
+ "Zosta kliknity element:"
+ clickedItemText;
Toast t =
Toast.makeText(context,clickedItemText,Toast.LENGTH_LONG);
t.show();
}
Na listingu 31.20 odczytalimy dodatkowe dane za pomoc uprzednio utworzonej staej i wprowadzilimy obiekt typu Toast. Metoda ta jest przetwarzana w gwnym wtku, musimy wic
upewni si, e nie bd w niej przeprowadzane dugotrwae operacje (aspekt ten zosta dokadnie omwiony w rozdziale 14., dotyczcym dugoterminowych usug).
Zakoczymy w ten sposb cz teoretyczn dotyczc nowych funkcji wprowadzonych w widetach zawierajcych widoki listy. Przetestujmy je teraz w dziaajcej, przykadowej aplikacji.
Wiksza cz zaprezentowanego dotychczas kodu pochodzi wanie z tego projektu, zatem jego
zrozumienie nie powinno stanowi problemu.
Dziaajcy przykad
testowy widet ekranu startowego oparty na licie
Prezentowany w tym punkcie przykadowy widet posuy do zilustrowania omawianych koncepcji dotyczcych widetw opartych na listach. Po utworzeniu tego projektu ujrzymy widet
wywietlajcy list, ktry bdzie mona przeciga po ekranie startowym. W czasie takiego
przecigania zobaczymy 20 elementw listy wypenionych przykadowym tekstem. Po klikniciu
jednego z tych elementw na ekranie startowym zostanie wywietlony obiekt typu Toast,
zawierajcy tekst z wybranego pola listy.
Poniej wymienilimy wszystkie potrzebne pliki:
TestListWidgetProvider.java jest gwn klas; mamy tu do czynienia z testowym
dostawc widetw, ktry implementuje widet zawierajcy (pord innych widokw)
widok listy (listing 31.21).
TestRemoteViewsFactory.java jest klas zawierajc zbir elementw, ktre
po wczytaniu przez dostawc widetw zostan wywietlone w widoku listy
(listing 31.22).
1122
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
public class TestListWidgetProvider extends AppWidgetProvider
{
private static final String tag = "TestListWidgetProvider";
public static final String ACTION_LIST_CLICK =
"com.androidbook.homewidgets.listclick";
public static final String EXTRA_LIST_ITEM_TEXT =
"com.androidbook.homewidgets.list_item_text";
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
Log.d(tag, "Wywolana metoda onUpdate");
final int N = appWidgetIds.length;
Log.d(tag, "Liczba widzetow:" + N);
for (int i=0; i<N; i++)
{
int appWidgetId = appWidgetIds[i];
updateAppWidget(context, appWidgetManager, appWidgetId);
}
super.onUpdate(context,appWidgetManager, appWidgetIds);
}
public void onDeleted(Context context, int[] appWidgetIds)
{
Log.d(tag, "Wywolana metoda onDeleted");
super.onDeleted(context,appWidgetIds);
}
public void onEnabled(Context context)
{
Log.d(tag, "Wywolana metoda onEnabled");
super.onEnabled(context);
}
public void onDisabled(Context context)
{
Log.d(tag, "Wywolana metoda onDisabled");
super.onEnabled(context);
}
private void updateAppWidget(Context context,
AppWidgetManager appWidgetManager,
int appWidgetId)
{
Log.d(tag, "Wywolana metoda onUpdate dla widzetu:" + appWidgetId);
final RemoteViews rv =
new RemoteViews(context.getPackageName(),
R.layout.test_list_widget_layout);
rv.setEmptyView(R.id.listwidget_list_view_id,
R.id.listwidget_empty_view_id);
1123
1124
Intent onListClickIntent =
new Intent(context,TestListWidgetProvider.class);
//Aktualizuje widet
appWidgetManager.updateAppWidget(appWidgetId, rv);
}
@Override
1125
1126
//
//Za pomoc skrtu klawiaturowego Ctrl+Shift+O uzupenimy instrukcje importw
//
class TestRemoteViewsFactory
implements RemoteViewsService.RemoteViewsFactory
{
private Context mContext;
private int mAppWidgetId;
private static String tag="TRVF";
public TestRemoteViewsFactory(Context context, Intent intent)
{
mContext = context;
mAppWidgetId =
intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
Log.d(tag,"Utworzono klase fabrykujaca");
}
this.mContext.getPackageName(),
R.layout.list_item_layout);
String itemText = "Element:" + position;
rv.setTextViewText(
R.id.textview_widget_list_item_id, itemText);
this.loadItemOnClickExtras(rv, position);
return rv;
}
private void loadItemOnClickExtras(RemoteViews rv, int position)
{
Intent ei = new Intent();
ei.putExtra(TestListWidgetProvider.EXTRA_LIST_ITEM_TEXT,
"Pozycja kliknietego obiektu:" + position);
rv.setOnClickFillInIntent(R.id.textview_widget_list_item_id, ei);
return;
}
1127
1128
//wewntrz widetu.
public void onDataSetChanged()
{
Log.d(tag,"onDataSetChanged");
}
}
Wikszo powyszego kodu zostaa ju wczeniej omwiona. Klasa ta definiuje obecno dwudziestu wierszy. Ukad graficzny kadego wiersza zostaje wczytany z pliku, a zawarty w nim tekst
zostaje umiejscowiony w odpowiedniej pozycji. Nastpnie zostaje wczytany tekst z kadej pozycji
do intencji dziaania onClick. Ten wanie tekst zostaje umieszczony w kontrolce typu Toast.
1129
AndroidManifest.xml
Listing 31.25 zawiera kod pliku konfiguracyjnego aplikacji. Zaznaczylimy pogrubion czcionk
definicje dostawcy widetw oraz usugi zdalnego widoku.
Listing 31.25. Plik AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidbook.homewidgets.listwidget"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="Testowy widet wywietlajcy list">
<!-**********************************************************************
* Dostawca testowego widetu wywietlajcego list
**********************************************************************
-->
<receiver android:name=".TestListWidgetProvider">
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/test_list_appwidget_provider" />
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
1130
Nasz widet nosi nazw Testowy widet wywietlajcy list, moe wic znajdowa si na samym
kocu listy, co oznacza, e musimy przewin ekran widetw w prawo. Widzimy to na rysunku 31.3.
1131
Moemy teraz przecign Testowy widet wywietlajcy list na dowoln stron ekranu startowego. Po rozpoznaniu operacji przecigania przez system klikamy przycisk ekranu startowego,
aby go wywietli. W midzyczasie ujrzymy podstawow form widetu, ktr zaprezentowalimy na rysunku 31.1. Jeeli klikniemy ktry z elementw listy, pojawi si kontrolka Toast zawierajca odpowiedni komunikat zwizany z tym elementem (rysunek 31.4).
Na tym zakoczymy omwienie usprawnie wprowadzonych do widetw w wersji 3.0 Androida. Przejdmy teraz do zupenie nowego interfejsu przecigania.
Funkcja przecigania
Przed pojawieniem si wersji 3.0 systemu nie istniaa moliwo bezporedniej obsugi funkcji
przecigania elementw. W rozdziale 25. pokazalimy, w jaki sposb przesuwa dany widok po
ekranie; dowiedzielimy si rwnie, e mona wykorzysta biece pooenie przeciganego
obiektu, aby ustali, czy pod nim znajduje si jaki inny element. Po otrzymaniu zdarzenia
MotionEvent oznaczajcego oderwanie palca od wywietlacza aplikacja moe rozpozna, czy
jest to rwnoznaczne z upuszczeniem obiektu. Chocia wprowadzenie takiej funkcji jest moliwe, zdecydowanie wygodniejszym rozwizaniem byaby bezporednia implementacja operacji
przecigania. Otrzymalimy j wanie w wersji 3.0 Androida.
Podstawowe informacje
o funkcji przecigania w wersji 3.0 Androida
Na najbardziej oglnym poziomie proces przecigania rozpoczyna si od zadeklarowania widoku,
w ktrym zosta on zainicjalizowany, nastpnie wszystkie zaangaowane w t operacj elementy
ledz zjawisko przesuwania obiektu, dopki nie zostanie wykryte zdarzenie upuszczenia. Jeeli
zdarzenie upuszczenia zostanie wykryte przez widok, ktry je odbierze, caa operacja przecigania zostanie zakoczona sukcesem. Jeeli aden widok nie odbierze zdarzenia upuszczenia
1132
bd jeli widok odbierajcy nie moe go przyj, przeciganie okae si nieudane. Informacje
o procesie przecigania s przekazywane przez obiekt DragEvent, ktry jest przekazywany do
wszystkich dostpnych obiektw nasuchujcych pod tym ktem.
Wewntrz obiektu DragEvent znajdziemy deskryptory przechowujce mnstwo informacji,
ktre s zalene od inicjalizatora operacji przecigania. Na przykad obiekt DragEvent moe
zawiera odniesienia do samego inicjalizatora, informacji o stanie, danych tekstowych, identyfikatorw URI oraz zasadniczo wszelkich innych elementw, jakie chcielibymy przekaza za
pomoc sekwencji przecigania.
Moemy przekazywa informacje, ktre w rezultacie umoliwiaj dynamiczn komunikacj
pomidzy widokami, jednak pocztkowe dane zawarte w obiekcie DragEvent, ktre zostaj do
niego doczone w momencie utworzenia, nie ulegaj potem zmianom. Oprcz danych obiekt
DragEvent zawiera take warto dziaania, definiujc biece zachowanie w sekwencji przecigania, a take informacje o pooeniu, okrelajce lokalizacj przeciganego obiektu na ekranie.
Obiekt DragEvent moe okreli jedno z szeciu nastpujcych dziaa:
ACTION_DRAG_STARTED definiuje rozpoczcie nowej sekwencji przecigania.
ACTION_DRAG_ENTERED oznacza przecignicie obiektu do wntrza okrelonego widoku.
ACTION_DRAG_LOCATION okrela przecignicie obiektu do nowej lokalizacji na ekranie.
ACTION_DRAG_EXITED, gdy obiekt zostaje przecignity na zewntrz okrelonego
widoku.
ACTION_DROP wystpuje wtedy, gdy uytkownik puszcza przecigany obiekt.
Decyzja, czy nastpio faktyczne zjawisko upuszczenia, naley do odbiorcy tego
zdarzenia.
ACTION_DRAG_ENDED informuje wszystkie obiekty nasuchujce procesu przecigania
o zakoczeniu sekwencji przecigania. Metoda DragEvent.getResult() suy do
okrelenia, czy upuszczenie obiektu zostao zakoczone powodzeniem.
Moe si wydawa, e powinnimy skonfigurowa obiekt nasuchujcy sekwencji przecigania
w kadym widoku bdcym czci tego procesu. W rzeczywistoci moemy zdefiniowa obiekt
nasuchujcy wobec dowolnego elementu znajdujcego si w aplikacji i bdzie on otrzymywa
wszystkie zdarzenia sekwencji przecigania wystpujce we wszystkich widokach systemu. Moemy w ten sposb nieco skomplikowa sytuacj, poniewa obiekt nasuchujcy zdarzenia
przecigania nie musi by powizany ani z przeciganym obiektem, ani nawet z docelowym
miejscem. Obiekt nasuchujcy moe zarzdza ca koordynacj sekwencji przecigania.
Jeeli przyjrzymy si przykadowemu projektowi dostpnemu w zestawie Android SDK, zauwaymy, e w rzeczywistoci obiekt nasuchujcy zostaje powizany z kontrolk TextView, ktra
nie ma nic wsplnego z faktyczn sekwencj przecigania. Prezentowana przez nas aplikacja
posiada obiekty nasuchujce poczone z okrelonymi widokami. Kady z takich obiektw
nasuchujcych otrzymuje zdarzenie DragEvent, pojawiajce si w sekwencji przecigania.
Oznacza to, e dany widok moe otrzyma obiekt DragEvent i zignorowa go, poniewa
dotyczy zupenie innego widoku. Oznacza to take, e obiekt nasuchujcy musi posiada
jaki mechanizm rozrniania w kodzie, a obiekt DragEvent powinien posiada wystarczajc
ilo informacji do okrelania przeprowadzanych czynnoci.
Jeeli obiekt nasuchujcy otrzyma zdarzenie DragEvent informujce jedynie o przeciganiu
nieznanego elementu oraz o jego wsprzdnych (15, 57), to obiekt ten niewiele bdzie mg
zrobi. O wiele przydatniejszy bdzie obiekt DragEvent, ktry przekazuje dane definiujce prze-
1133
cigany element, okrelajce jego pooenie w pozycji (15, 57), jak rwnie wskazujce, e mamy
do czynienia z operacj kopiowania. Dodatkowo ta informacja jest zawarta w odpowiednim identyfikatorze URI. Jeli zastosujemy taki obiekt DragEvent, to w momencie upuszczenia elementu
bdziemy posiada wystarczajco wiele informacji, aby przeprowadzi proces kopiowania.
Lista plikw
Do utworzenia omawianej aplikacji bd potrzebne nastpujce pliki:
main.xml stanowi gwny ukad graficzny aplikacji, w ktrym zostay umieszczone
dwa fragmenty (listing 31.26).
palette.xml jest ukadem graficznym fragmentu zawierajcego kka umieszczone
po lewej stronie ekranu (listing 31.27).
dropzone.xml stanowi ukad graficzny fragmentu reprezentujcego docelowy obszar
po prawej stronie ekranu oraz komunikat o iloci upuszcze (listing 31.28).
MainActivity.java to najprostsza z moliwych aktywnoci. Ustanawia ona jedynie
gwny widok treci, a reszt operacji pozostawia fragmentom (listing 31.29).
Palette.java jest kodem rwnie nieskomplikowanego zbioru kek. Suy on jedynie
do rozwinicia ukadu graficznego palety (listing 31.30).
DropZone.java jest nieco bardziej zoonym kodem, poniewa implementujemy
w nim mechanizm upuszczania. Zostaje tu rozwinity ukad graficzny dropzone.xml,
a nastpnie zaimplementowany obiekt nasuchujcy (listing 31.31).
Dot.java stanowi klas niestandardowego widoku obiektw, ktre bd przecigane.
Tutaj przechowujemy mechanizm inicjalizacji sekwencji przecigania, obserwujemy
zdarzenia przecigania oraz rysujemy kka (listing 31.32).
attrs.xml definiuje dwa atrybuty XML wykorzystywane w ukadzie graficznym
palette.xml do opisu atrybutu kek (listing 31.33).
AndroidManifest.xml jest gwnym plikiem manifestem aplikacji (listing 31.34).
strings.xml zawiera cigi znakw wykorzystywane przez plik AndroidManifest.xml
(listing 31.35).
1134
Na listingu 31.26 zosta zaprezentowany gwny ukad graficzny tworzcy interfejs uytkownika,
widoczny na rysunku 31.5. Podobnie jak w przykadach omwionych w rozdziale 29., ukad ten
skada si z prostego, gwnego ukadu liniowego, w ktrym zostay poziomo rozmieszczone
dwa fragmenty. Pierwszy fragment bdzie przechowywa zbir kek, a drugi bdzie nasz stref
upuszczania obiektw.
Listing 31.26. Plik gwnego ukadu graficznego
<?xml version="1.0" encoding="utf-8"?>
Plik ukadu graficznego, ktry definiuje wygld fragmentu przechowujcego kka (listing 31.27),
jest nieco bardziej interesujcy. Poniewa fragment jest reprezentowany przez ten ukad graficzny, nie musimy wstawia w tym ukadzie graficznym znacznika fragmentu. Ukad graficzny
1135
zostanie rozwinity i stanie si hierarchi widokw dla fragmentu przechowujcego kka. Same kka zostay zdefiniowane niestandardowo, a dwa z nich zostay rozmieszczone pionowo. Zwrmy uwag, e w definicji kek znajdziemy dwa atrybuty XML (dot:color oraz
dot:radius). Jak wida, atrybuty te okrelaj kolor oraz promie kka. Poruszymy ten temat
ponownie przy okazji omawiania pliku attrs.xml. Pozostae atrybuty stanowi standard definiowania widokw.
Listing 31.27. Plik ukadu graficznego palette.xml, definiujcy wygld kek
<?xml version="1.0" encoding="utf-8"?>
Ukad graficzny strefy upuszczania z listingu 31.28 jest rwnie bardzo atwy do zrozumienia.
Widzimy w nim zielony kwadrat oraz umieszczony obok niego komunikat tekstowy. W tym
wanie miejscu bdziemy upuszcza przecigane kka. Komunikat tekstowy bdzie informowa o liczbie upuszczonych kek.
Listing 31.28. Plik ukadu graficznego dropzone.xml
<?xml version="1.0" encoding="utf-8"?>
1136
android:layout_gravity="center_vertical"
android:background="#00ff00" />
<TextView android:id="@+id/dropmessage"
android:text="0 kropek"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="50dp"
android:textSize="17sp" />
</LinearLayout>
Jak widzimy na listingu 31.29, mamy do czynienia z najprostsz moliw aktywnoci. Ustanawia ona jedynie nadrzdny widok treci, ktry zostaje przyporzdkowany gwnemu ukadowi
graficznemu (zawierajcemu dwa fragmenty). Kod, ktry definiuje interesujce nas operacje,
zosta zamieszczony w pozostaych plikach.
Listing 31.29. Plik gwnej aktywnoci
package com.androidbook.drag.drop.demo;
Kod z listingu 31.30 rwnie jest niezwykle prosty. Jest to kod fragmentu, a wic przesaniamy
w nim metod onCreateView() w taki sposb, aby ukad graficzny zosta rozwinity w widok,
ktry nastpnie zostaje przekazany.
Listing 31.30. Plik Palette.java
package com.androidbook.drag.drop.demo;
android.app.Fragment;
android.os.Bundle;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
1137
android.animation.ObjectAnimator;
android.app.Fragment;
android.content.ClipData;
android.os.Bundle;
android.util.Log;
android.view.DragEvent;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.view.animation.CycleInterpolator;
android.widget.TextView;
1138
1139
1140
android.content.ClipData;
android.content.Context;
android.content.res.TypedArray;
android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.util.AttributeSet;
android.util.Log;
android.view.DragEvent;
android.view.View;
setOnLongClickListener(lcListener);
setOnDragListener(this);
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
int size = 2*mRadius + getPaddingLeft() + getPaddingRight();
setMeasuredDimension(size, size);
}
// Mechanizm przecigania
public boolean onDrag(View v, DragEvent event) {
String dotTAG = (String) getTag();
1141
1142
if(event.getLocalState() != this) {
Log.v(dotTAG, "To zdarzenie przeciagania nie jest przeznaczone dla nas");
return false;
}
boolean result = true;
1143
Kod klasy Dot w duym stopniu przypomina zawarto klasy DropZone. Wynika to czciowo
z faktu, e rwnie tutaj otrzymujemy zdarzenia przecigania. Konstruktor tej klasy przetwarza
atrybuty definiujce waciwy promie i kolor kek, a nastpnie konfiguruje dwa obiekty nasuchujce, jeden wychwytujcy dugie kliknicia, a drugi zdarzenia przecigania.
Fragment kodu dotyczcy okrelania atrybutw jest szczeglnie interesujcy. Chcemy mc okreli waciwoci kka w pliku ukadu graficznego, jednak niestandardowe atrybuty XML wymagaj odpowiedniej konfiguracji w ktrym miejscu. W naszym przypadku przeprowadzamy
j w pliku attrs.xml, umieszczonym w katalogu /res/values. Definiujemy w tym pliku obiekt
nadawania stylu Dot, a take dwa atrybuty: color i radius. Na listingu 31.33 zostaa umieszczona zawarto pliku attrs.xml.
Listing 31.33. Plik attrs.xml definiujcy nowe atrybuty dla klasy Dot
<?xml version="1.0" encoding="utf-8"?>
Jak wida, nazw obiektu mona uzyska ze znacznika. Nazwy (i typy) atrybutw s umieszczone w znacznikach podrzdnych. Poprzez okrelenie atrybutw w pliku attrs.xml moemy
wykorzysta je w pliku ukadu graficznego, a take w kodzie Java. W tym drugim przypadku przeszukujemy wszystkie rozpoznawalne atrybuty, a po znalezieniu atrybutw color lub radius
pobieramy ich warto i przypisujemy do kka. Jest to cakiem wygodny sposb definiowania
wygldu obiektw za pomoc atrybutw XML.
Podczas rysowania obiektu posuymy si dwoma kolorami. Jeden kolor przypiszemy do kka
znajdujcego si w stanie spoczynku. W momencie przecigania, kiedy chcemy w jaki sposb
wyrni kko, zmieniamy jego barw na karmazynow.
Obiekt nasuchujcy dugich klikni suy do okrelania pocztku sekwencji przecigania. Jedynym sposobem rozpoczcia procesu przecigania jest kliknicie kka i przytrzymanie go.
Uaktywnienie obiektu nasuchujcego dugie kliknicia spowoduje utworzenie nowego obiektu
ClipData za pomoc cigu znakw i znacznika kka. Znacznik ten jest nazw kka zdefiniowan w pliku ukadu graficznego. Istnieje kilka innych metod wprowadzenia danych do
obiektu ClipData, dlatego te zalecamy zapoznanie si z dokumentacj tego obiektu.
Nastpna metoda jest kluczowa dla naszej aplikacji startDrag(). To wanie tutaj Android
przejmie kontrol i rozpocznie sekwencj przecigania. Zwrmy uwag, e argumentem jest
uprzednio utworzony obiekt ClipData, nastpnie cie przeciganego obiektu, lokalny stan
obiektu, a na kocu warto zero.
Cie przeciganego obiektu jest obrazem wywietlanym w trakcie trwania sekwencji przecigania. W naszym przypadku zostaje on doczony na etapie przecigania, podczas gdy oryginalne
kko pozostaje na swoim miejscu. Domylnym zachowaniem klasy DragShadowBuilder jest
utworzenie cienia przypominajcego oryginalny obiekt, wic wystarczy, e j wywoamy i przekaemy do widoku. Moemy tutaj popuci wodze fantazji i utworzy dowolny widok cienia,
jeli jednak przesonimy t klas, bdziemy musieli jeszcze zaimplementowa kilka dodatkowych klas.
1144
Metoda onMeasure() przekazuje Androidowi informacje o wymiarach wykorzystywanego niestandardowego widoku. Musimy poda systemowi te dane, aby widok ten pasowa do innych
elementw widocznych na ekranie.
Znajdziemy tu take metod zwrotn onDrag(). Jak ju wspomnielimy, kady dostosowany
obiekt nasuchujcy moe odbiera zdarzenia przecigania. Wszystkie te obiekty odbieraj na
przykad dziaania ACTION_DRAG_STARTED oraz ACTION_DRAG_ENDED. Gdy wic pojawi si takie
zdarzenie, musimy ostronie wykorzystywa zawarte w nim informacje. Poniewa w naszej aplikacji umiecilimy dwa kka, musimy uwaa, eby kada czynno bya przeprowadzana na
tym waciwym.
Gdy obydwa kka otrzymuj dziaanie ACTION_DRAG_STARTED, tylko jedno z nich powinno
zmieni kolor na karmazynowy. Aby okreli, ktre kko jest waciwe, naley porwna
przekazane obiekty lokalnego stanu z wasnym obiektem lokalnego stanu. Jeeli przyjrzymy si
fragmentowi, w ktrym definiowalimy obiekt lokalnego stanu, zauwaymy, e przekazalimy
do niego biecy widok. Zatem po otrzymaniu obiektu lokalnego stanu moemy go porwna
z naszym biecym stanem, aby okreli, czy znajduje si w widoku rozpoczynajcym sekwencj przecigania.
Jeeli obiekt znajduje si w innym widoku, w oknie LogCat zostanie wywietlony komunikat,
e sekwencja przecigania nie jest przeznaczona dla tego widoku. Nastpi rwnie przekazanie
wartoci false, ktra informuje system, e zdarzenie nie zostanie tu obsuone.
Jeeli zdarzenie przecigania trafi do waciwego widoku, naley pobra z tego zdarzenia pewne
wartoci i najczciej jedynie wywietli odpowiedni komunikat w oknie LogCat. Pierwszym
wyjtkiem jest dziaanie ACTION_DRAG_STARTED. Jeeli dziaanie to jest przeznaczone dla danego widoku, to znaczy, e kko jest czci sekwencji przecigania. Wprowadzamy wic
warto logiczn w zmiennej inDrag, dziki ktrej metoda draw() bdzie moga pniej wywietli kko w innym kolorze. Kolor jest zmieniony jedynie do czasu pojawienia si dziaania ACTION_DRAG_ENDED, po czym zostaje przywrcona pierwotna barwa kka.
Jeeli zostanie wywoane dziaanie ACTION_DROP wobec kka, to znaczy, e uytkownik prbuje
upuci to kko na inne kko, by moe w jego pocztkowym miejscu. Nie powinno mie to
adnych konsekwencji, wic w tym przypadku naley przekaza po prostu warto false.
Na koniec metoda draw() niestandardowego widoku definiuje rodek naszego okrgu (w naszym przypadku przeciganego kka), a nastpnie rysuje go za pomoc odpowiedniego
koloru. Dziki metodzie invalidate() system zostaje poinformowany o modyfikacji widoku
oraz koniecznoci ponownego narysowania interfejsu uytkownika. Za pomoc tej metody gwarantujemy bardzo szybkie zaktualizowanie interfejsu uytkownika o nowe elementy.
Na listingu 31.34 prezentujemy plik manifest tej aplikacji.
Listing 31.34. Plik AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
1145
android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Na listingu 31.35 pokazalimy zawarto pliku strings.xml, w ktrym umiecilimy cigi znakw
wykorzystywane przez plik manifest.
Listing 31.35. Plik strings.xml
<?xml version="1.0" encoding="utf-8"?>
W ten sposb uzyskalimy ju wszystkie pliki i informacje wymagane do skompilowania i wdroenia naszej przykadowej aplikacji wykorzystujcej funkcj przecigania.
dla nas
206.0
29.0,
48.0,
45.0,
41.0,
40.0,
36.0
39.0
39.0
39.0
39.0
dla nas
1146
W tym konkretnym przypadku sekwencja przecigania zostaa rozpoczta przez biae kko. Po
uruchomieniu caej procedury przez dugie kliknicie otrzymujemy wiadomo rozpoczeto proces przeciagania?.
Zwrmy uwag, e w trzech nastpnych linijkach pojawia si informacja o odebraniu dziaania
ACTION_DRAG_STARTED. Okazuje si, e wywoanie metody zwrotnej nie byo przeznaczone dla
niebieskiego kka. Jego adresatem nie by rwnie obszar dropTarget.
Zauwamy nastpnie, e kolejne komunikaty opisuj zachowanie sekwencji przecigania w obszarze dropTarget, poczwszy od dziaania ACTION_DRAG_ENTERED. Oznacza to, e kko zostao
przecignite nad obszar zielonego kwadratu. Wsprzdne X i Y s mierzone wzgldem lewego
grnego rogu tego widoku. Zatem pierwszy wpis sekwencji przecigania przeprowadzanej
w zielonym obszarze posiada wsprzdne (x, y) = (29, 36), a upuszczenie nastpio w punkcie
(40, 39). Jak wida, widok docelowy potrafi wydoby nazw znacznika definiujcego biae kko
i umieci j w oknie LogCat.
Ponownie zwracamy uwag, e wszystkie obiekty nasuchujce odebray dziaanie ACTION_
DRAG_ENDED, ale jedynie biae kko pozwolio na wywietlenie wynikw za pomoc metody
getResults().
Zachcamy do eksperymentowania z t przykadow aplikacj. Przecignijmy jedno kko nad
drugie, a nawet na jego oryginaln wersj. Dodajmy jeszcze jedno kko w pliku palette.xml.
Zwrmy uwag, e po wyjciu z zielonego obszaru pojawia si odpowiedni komunikat w oknie
LogCat. Warto take odnotowa fakt, e jeeli upucimy kko gdziekolwiek poza zielonym obszarem, sekwencja przecigania zostanie uznana za nieudan.
Odnoniki
http://developer.android.com/sdk/android-3.0-highlights.html wymieniono tu
nowe funkcje wprowadzone w wersji 3.0 Androida, a take odnotowano
informacje o zmianach wprowadzonych w widetach ekranu startowego.
www.androidbook.com/item/3624 nasze notatki robocze, powstae na etapie
zbierania materiaw do niniejszego rozdziau, dotyczce widetw ekranu startowego
dostpnych w wersji 3.0 Androida. Znajdziemy tu odnoniki do interfejsw API,
fragmentw kodw, otwartych pyta oraz dalszych bada.
www.androidbook.com/item/3299 nasze notatki robocze, powstae na etapie
zbierania materiaw do niniejszego rozdziau, dotyczce widetw ekranu startowego
dostpnych w wersji 2.2 Androida. Znajdziemy tu odnoniki do interfejsw API,
fragmentw kodw, otwartych pyta oraz poprzednich bada.
www.androidbook.com/item/3637 nasze dodatki dotyczce klasy RemoteViews,
zaktualizowane o informacje zwizane z wersj 3.0 Androida, w tym rwnie
przykadowe kody, kolejne pytania, a take zewntrzne i wewntrzne odniesienia.
http://developer.android.com/guide/topics/appwidgets/index.html gwne rdo
informacji na temat poprzednich wersji widetw ekranu startowego. Zwrmy
uwag, e nie ma tu wzmianki na temat zmian wprowadzonych w wersji 3.0 Androida,
zapoznamy si tu jednak z podstawowymi mechanizmami.
http://developer.android.com/reference/android/appwidget/AppWidgetManager.html
dokumentacja bardzo istotnej klasy AppWidgetManager.
1147
http://developer.android.com/reference/android/widget/RemoteViewsService.RemoteV
iewsFactory.html dokumentacja klasy RemoteViewsFactory.
http://developer.android.com/reference/android/widget/RemoteViews.html dokumentacja
klasy RemoteViews.
http://developer.android.com/reference/android/widget/RemoteViewsService.html
dokumentacja usugi RemoteViewsService.
ftp://ftp.helion.pl/przyklady/and3ta.zip cze, dziki ktremu moemy pobra
projekty utworzone na potrzeby niniejszej ksiki. Nazwy katalogw zawierajcych
te projekty to ProAndroid3_R31_WidetyLista oraz
ProAndroid3_R31_TestPrzecigania.
Podsumowanie
W rozdziale tym omwilimy dwa istotne usprawnienia wprowadzone w wersji 3.0 Androida:
widety ekranu startowego oparte na listach oraz interfejs przecigania.
Najpierw dokadnie przeanalizowalimy modyfikacje wprowadzone w widetach ekranu startowego. Pokazalimy, w jaki sposb wczytywa i zapenia zdalne widoki zawierajce listy poprzez
wykorzystanie usugi zdalnych widokw oraz klasy fabrykujcej. Dowiedzielimy si take, jak
moemy konfigurowa zdarzenia onClick oraz manipulowa sam klas AppWidgetProvider
w celu otrzymywania tych zdarze oraz reagowania na nie. Zaprezentowany przez nas materia
pozwala na implementowanie bogatych, uytecznych widetw ekranu startowego.
W podrozdziale powiconym funkcji przecigania zajlimy si wszelkimi dostpnymi nowinkami wykorzystywanymi w czasie sekwencji przecigania, dziki ktrym uytkownik ma do
dyspozycji jeszcze wiksze moliwoci, porwnywalne z osiganymi na komputerach stacjonarnych. Przekazalimy wiele informacji na temat rde, zdarze oraz obiektw docelowych
sekwencji przecigania.
Potencja kryjcy si w tych funkcjach jest ograniczony jedynie wyobrani Czytelnikw.
1148
Skorowidz
A
AAC, Advance Audio Coding, 626
AAPR, Android Asset Packaging Tool, 69
abstrakcja, 472
adapter
ArrayAdapter, 203
ManateeAdapter, 218
ManateeAdapter, 218
niestandardowy, 218
SimpleCursorAdapter, 200
standardowy, 218
ADB, Android Debug Bridge, 83
ADP, Android Developer Phone, 1009
ADT, Android Development Tools, 38, 51
AGC, Automatic Gain Control, 625
agregacja, 970, 1001
akcelerometr, 924
mierzenie ktw, 930
pomiar grawitacji, 927
skadowa ruchu, 929
tryb krajobrazowy, 925
tryb wywietlania, 926
wsppraca z magnetometrami, 931
wsprzdne, 924
aktualizacja lokacji, 569
aktualizowanie danych, 138
aktualizowanie widoku, 742
aktywne foldery, 42
AllContactsLiveFolderCreatorActivity.java,
725
AndroidManifest.xml, 723
BetterCursorWrapper.java, 732
folder kontaktw, 721
lista, 720
MyContactsProvider.java, 726
MyCursor.java, 731
rejestrowanie identyfikatora URI, 730
testowanie, 733
tworzenie folderu, 722
aktywno, 39, 59, 428
ACTION_DIAL, 159
animacji widoku, 537
DetailsActivity, 1053
klienta nadawczego, 457
konfiguratora widetu, 739
ListActivity, 209, 210
LocalSearchEnabledActivity, 794, 795
MainActivity, 375
MultiViewTestHarness, 669
NotesList, 72
OpenGL20MultiViewTestHarness, 705
paska dziaania, 1096
klasa bazowa, 1085
pliki projektu, 1081
paska dziaania wywietlajcego zakadki,
1081
paska zakadek, 1088
PreferenceActivity, 304, 312
przechowujca pasek dziaania, 1098, 1099
RegularActivity, 778
SearchActivity, 791, 792, 795
TestHandlersDriverActivity, 446
TestOpenGLMainDriver, 670
ViewAnimationActivity, 537
wyczajca wyszukiwanie, 786
wyszukiwania
kod rdowy, 804
wyszukiwania globalnego, 770
wywietlajca pasek zakadek, 1092
wywoania wyszukiwania, 808
1150 Skorowidz
aktywno
wywoujca klas AsyncTask, 355
z paskiem zakadek, 1080
z rozwinit list, 1098
aktywny folder, live folder, 717
alarm powtarzalny, 504
anulowanie, 506
kompilowanie kodu, 506
alert odlegociowy, 578
aliasy kolumn w strukturach danych
kontaktu, 1000
AMDDA, Android Market Developer
Distribution Agreement, 1006
AMR, Adaptive Multi-Rate, 626
analiza baz danych, 121
analiza wbudowanych dostawcw treci, 120
Android Debug Bridge, 121
Android Developer Phone, 1009
Android Market, 319, 1005, 1022
alternatywy dla serwisu, 1023
katalog Moje zamwienia, 1022
Android SDK, 32, 56
android.app, 44
android.bluetooth, 44
android.content, 44
android.content.pm, 44
android.content.res, 44
android.database, 45
android.database.sqlite, 45
android.gesture, 45
android.graphics, 45
android.graphics.drawable, 45
android.graphics.drawable.shapes, 45
android.hardware, 45
android.location, 45
android.media, 45
android.net, 45
android.net.wifi, 45
android.opengl, 46
android.os, 46
android.preference, 46
android.provider, 46
android.sax, 46
android.speech, 46
android.speech.tts, 46
android.telephony, 46
android.telephony.cdma, 46
android.telephony.gsm, 46
android.text, 46
android.text.method, 47
android.text.style, 47
android.utils, 47
android.view, 47
android.view.animation, 47
android.view.inputmethod, 47
android.webkit, 47
android.widget, 47
animacja, 517
klatek kluczowych, 524
poklatkowa, 518
dodawanie animacji do aktywnoci, 520
lista klatek, 521
planowanie, 518
rodowisko testowe, 519, 522
tworzenie aktywnoci, 519
ukad graficzny, 519
rotacyjna, 524, 531
skali, 524
definiowanie w pliku XML, 528
translacyjna, 524, 531
typu alfa, 524, 530
ukadu graficznego, 523
kod aktywnoci, 527
rodowisko testowe, 525
tworzenie aktywnoci, 525
widok ListView, 525
w osi X oraz w wymiarze alfa, 1077
widoku, 533
aktywno, 534
dodawanie animacji, 536
kod aktywnoci, 535
kod rdowy aktywnoci, 537
metody preTranslate i postTranslate, 538
ukad graficzny, 534
widoku ListView, 540
animator niestandardowy, 1076
animowane przejcia, tweening, 42
animowanie obiektw, 691
animowanie trjkta, 676
animowanie widoku ListView, 528
ANR, Application Not Responding, 351, 427
API Google Maps, 52
Skorowidz
aplikacja
adb, 123
Android Debug Bridge, 121
Android Hierarchy, 57
BDayWidget, 747
Browser, 38
Contacts, 38
Downloads, 367
Gestures Builder, 898, 901
HelloAndroidApp, 65
Home, 38
Hierarchy Viewer, 242
J2EE, 68
keytool, 318, 319
Kontakty, 958
Lista czujnikw, 909
kod Java, 909
ukad graficzny, 909
wyniki, 911
MapViewDemo, 549, 889
Monitor akcelerometru, 918
Monitor czujnika owietlenia, 911
NfcDemo, 950
Notepad, 69
analiza kodu, 71
tworzenie, 70
uruchomienie, 69
wczytywanie, 70
obsugujca mapy, 548
obsugujca multimedia, 606
Package Browser, 409
Phone, 38
pocztowa, 593
przedstawiajca map wiata, 157
przegldarki stron, 157
rejestrujca, 631
SDK Manager, 54
SipDemo, 599
suca do przecigania obiektw, 876
suca do tumacze, 397
gwny plik aplikacji, 399
plik AIDL usugi tumacza, 399
plik AndroidManifest.xml, 403
plik usugi tumaczenia, 402
plik z cigami znakw, 399
plik z funkcj tumaczenia, 402
plik z tablicami, 399
1151
1152 Skorowidz
atrybuty
uprawnienia, 329
wza data, 160
AVD, Android Virtual Device, 51, 60, 65
B
badanie aktywnoci zawierajcej pasek
dziaania, 1098, 1102
badanie kontaktw zbiorczych, 980
elementy menu, 986
funkcje uytkowe, 980
gwna aktywno, 986
klasa bazowa, 981
kolumny kursora, 988
pliki projektu, 980
testowanie kontaktw, 982
badanie nieprzetworzonych kontaktw, 989
opcje menu, 990
pliki projektu, 989
pola kursora, 993
przegldanie danych, 994
testowanie danych kontaktu, 995
testowanie kontaktw, 990
baza danych contacts.db, 133
baza danych SQLite, 119, 125
bezpieczestwo
certyfikat cyfrowy, 318
definiowanie uprawnie, 334
klucz prywatny, 318
klucz publiczny, 318
kontrolowanie dostpu do zasobw, 334
magazyn kluczy, 318
podpisywanie aplikacji, 318
przekazywanie uprawnie, 333
biblioteka
FreeType, 37, 38
jzyka C, 37
OpenGL, 37
Skia, 38
Surface Manager, 38
WebKit, 37, 38
biblioteka gestw, 900
biblioteka OpenGL, 649
animowanie trjktw, 676
dodawanie trjktw, 675
glClear, 658
glColor, 659
glDrawElements, 656
glFrustum, 661
gluLookAt, 660
glVertexPointer, 654
glViewport, 663
koncepcja widzenia, 660
ksztaty, 678
objto widzenia, 661
OpenGLTestHarnessActivity, 667
podstawy rysowania, 654
RegularPolygon, 681
rozmiar ekranu, 663
rysowanie prostokta, 679
rzutowanie ortograficzne, 662
rzutowanie perspektywiczne, 662
tekstury, 678, 694
trjwymiarowa scena, 659
tworzenie trjktw, 676
wielobok foremny, 681
biblioteka OpenGL ES, 649
proste ksztaty, 654
biblioteka OpenGL ES 2.0, 704
aktywno sterujca, 706
dostp do jednostek cieniowania, 711
funkcje, 706
jednostki cieniujce, 708
jednostki cieniujce wierzchoki, 708
jednostki cieniujce fragmenty, 708
renderowanie, 707
trjkt, 711
rda, 715
biblioteki Apache HTTP, 48
biblioteki Java Androida, 32
biblioteki multimediw, 37
blokada przechodzenia w stan zatrzymania,
477, 486
bd kompilacji we wtyczce ADT, 111
bdy w aplikacji, 81
BSD, Berkeley Software Distribution, 37
bufor koloru, 658
bufor nio, 655, 658, 666
C
CA, certificate authority, 318
CAD, Computer Aided Design, 650
cal, 235
Centrum Oprogramowania Linuksa, 53
Skorowidz
certyfikat
Android Debug, 321
cyfrowy, 318
debugowania, 320
okres wanoci, 324
PKI, 412
podpisany samoistnie, 318
produktu, 1018
programistyczny, 320
testowy, 546
wasny, 318
X.509, 318
CHOICE_MODE_MULTIPLE, 211
CHOICE_MODE_NONE, 211
CHOICE_MODE_SINGLE, 211
cig znakw, 115
ciar, weight, 228
com.google.android.maps, 47
CRUB, Create, Read, Update, Delete, 77
cyfrowy podpis aplikacji, 322
cykl ycia
aktywnoci, 446, 447
aplikacji, 78
dostawcy treci, 448
odbiorcw komunikatw, 448
usugi, 448
czas ycia procesu, 446
czas ycia skadnika, 446
czstotliwo prbkowania, 628
czujnik
aktualizacje odczytw, 915
interpretacja danych, 921
pobieranie zdarze, 911
pozostawianie wczonego ekranu, 920
rodzaje czujnikw, 908
rozwizywanie problemw, 915
termometry, 922
trudne problemy, 914
wybr wartoci odwieania, 913
wykrywanie, 908
czujnik NCF, Patrz NFC, 907
czujniki
akcelerometry, Patrz akcelerometr, 924
cinienia, 923
grawitacji, 939
informacje o pooeniu, 932
1153
D
Dalvik VM, 33, 36
dane, 159, 167
dane dodatkowe, 161
dane nieprzetworzonego kontaktu, 994
dane typu Bundle, 568
DATABASE_MODE_2LINES, 801
DATABASE_MODE_QUERIES, 801
DDMS, Dalvik Debug Monitor Server, 82
Debug, 82
debugger debug_layout_activity.xml, 977
debugowanie, 293, 1090
debugowanie aplikacji, 82
definicja
nieregularnej tabeli, 232
tabeli danych kontaktw, 968
tabeli kontaktw zbiorczych, 969
tabeli nieprzetworzonych kontaktw, 966
definiowanie
dziaania w dostawcy widetu, 1118
kontrolki GridView, 214
prostokta o zaokrglonych rogach, 109
wielokrotnoci, 102
zasobw typu Color, 105
zasobw typu color-drawable, 108
zasobw typu dimension, 106
zasobw typu string, 103
deklarowanie niestandardowego
uprawnienia, 328
deklinacja magnetyczna, 938
Developer Account, 1006
dip, 235
dugie kliknicie, 261
dugoterminowa usuga, 473
dugoterminowy odbiorca komunikatw, 476
1154 Skorowidz
dodawanie
animacji do widoku, 536
elementw do menu, 249
funkcji dotyku, 889
identyfikatorw do kontrolek, 181
kontaktu, 998
plikw do magazynu multimediw, 644
plikw dwikowych do silnika TTS, 854
pliku do dostawcy treci, 137
trjktw za pomoc indeksw, 675
typw do obiektu, 161
znacznikw, 553
dokumentacja pakietu SDK, 412
dostawca
BookProvider, 141
dodawanie ksiki, 150
usuwanie ksiki, 150
wywietlanie listy ksiek, 151
zliczanie ksiek, 151
Contacts, 131
GPS_PROVIDER, 577
lokalizacji, 567
MediaStore, 131
NETWORK_PROVIDER, 577
PASSIVE_PROVIDER, 577
pasywny, 567
pooenia, 568
propozycji, 798
aktywno wyszukiwania, 803
gwna aktywno, 810
manifest zawierajcy definicj, 802
pliki implementacji, 799
pole wyszukiwania lokalnego, 811
propozycja lokalna, 812
propozycje globalne, 813
uytkowanie, 810
wczanie, 812
wyniki wyszukiwania lokalnego, 811
zadania, 800
propozycji niestandardowy
badanie metadanych, 821
identyfikatory URI, 818
implementacja, 813
implementacja aktywnoci
wyszukiwania, 824
klasa SearchActivity, 825
korzystanie, 831
Skorowidz
E
Eclipse, 38, 51
EditText, 184
edycja kontaktu, 960
ekran dotykowy, 861
ekran preferencji, 297
ekran startowy, 1110
eksploracja kont, 972
funkcje testujce, 973
gwna aktywno sterujca, 977
plik manifest, 978
plik menu, 973
spis plikw, 978
element Activity.managedQuery, 74
element Dot, 876
element nasuchujcy, 192
element SimpleCursorAdapter, 74
element TextView, 94
elementy skadowe Androida, 68
AndroidManifest.xml, 68
anim, 68
assets, 68
drawable, 68
layout, 68
menu, 68
raw, 68
res, 68
src, 68
values, 68
xml, 68
emulacja karty NFC, 949
emulacja rozruchu urzdzenia, 64
emulator Androida, 38
EULA, 1019
EXTRA_EMAIL., 162
EXTRA_SUBJECT, 162
F
filtr intencji, 160, 166, 943, 945
flaga
CONTEXT_IGNORE_SECURITY, 414
CONTEXT_INCLUDE_CODE, 414
CONTEXT_RESTRICTED oznacza, 414
Menu.FLAG_APPEND_TO_GROUP, 266
krawdzi, 869
1155
nietrwaoci, 485
Service.START_REDELIVER, 485
Service.START_STICKY, 485
trwaoci, 485
folder
android\AVD, 65, 66
assets, 69
drawable, 897
raw, 69
res, 69, 74
res/layout-land, 240
res/layout-port, 240
res/layout-square, 240
ukadu graficznego res/layout, 240
values, 74
foldery wiadomoci SMS, 592, 593
format dwiku 3GPP, 625
format VCF, 963
fragment, 40, 1026
aplikacja ukazujca cykl ycia, 1033
cykl ycia, 1028
formy komunikowania, 1073
komunikacja pomidzy fragmentami, 1074
metoda onAttach(), 1030
metoda onCreate(), 1030
metoda onCreateView(), 1031
metoda onDestroyView(), 1033
metoda onDetach(), 1033
metoda onInflate(), 1030
metoda onPause(), 1032
metoda onResume(), 1032
metoda onStart(), 1032
metoda onStop(), 1032
metoda setRetainInstance(), 1033
metoda zwrotna onActivityCreated(), 1032
przechowujcy dialog, 1055
przejcia i animacje, 1044
stosowanie, 1027
stosowanie odniesie, 1046
struktura, 1027
transakcja fragmentu, 1042
trwao, 1054
tworzenie hierarchii widokw, 1031
wycofanie okna dialogowego, 1059
wywietlanie okna dialogowego, 1054
wywietlanie nowej aktywnoci, 1051
1156 Skorowidz
fragment wywietlajcy okna dialogowe, 1060
gwna aktywno, 1061
gwny ukad graficzny, 1072
interfejs uytkownika, 1061
kod Java, 1064
pliki projektu, 1060
ukad graficzny, 1064
funkcja
debugowania, 84
getACursor(), 982
getDistinctPendingIntent(), 510
getEventsFromAnXMLFile, 111
getExternalStorageDirectory(), 607
getExtras, 161
Install New Software, 56
java.util.AttributeSet, 110
listContacts(), 988
ProGuard, 1018
przecigania, 876, 1131
gwna aktywno, 1136
interfejs aplikacji, 1134
lista plikw, 1133
podstawowe informacje, 1131
przykadowa aplikacja, 1133
testowanie aplikacji, 1145
tworzenie ukadu graficznego, 1133
ukad graficzny, 1134
putExtras, 161
setData(), 435
StrictMode, 85, 89
StrictMode w trybie debugowania, 86
testThread(), 437
TTS, 843
type-to-search, 797
wielodotykowoci, 879, 887
funkcje
daty, 760
kalendarza, 494
uytkowe, 980
G
geokodowanie w Androidzie, 559, 563
geokodowanie w oddzielnym wtku, 563
geolokalizacja, 559
Skorowidz
glViewport
wziernik, 663
gbia w obrazie dwuwymiarowym, 539
Google Checkout, 1022
Google Maps, 546
Google Nexus One, 1009
Google Nexus S. Android Developer Phone, 1009
GPS, Global System Positioning, 545
GPU, Graphical Processing Unit, 649
grafika trjwymiarowa, 652
grawitacja, gravity, 228
grupa opcji, 193
grupy widokw, 39
GSM, Global System for Mobile
Communication, 38
H
handheld, 32
Hashimi Sayed Y., 23
hasa storepass i keypass, 321
hiperbola, 532
historia Androida, 34
I
IANA, Internet Assigned Numbers
Authority, 129
IDE, Integrated Development Environment, 38
identyfikator
Contacts.People.CONTENT_URI, 131
predefiniowany, 526
public static, 74
treci nieprzetworzonego kontaktu, 997
ukadu graficznego, 527
URI, 76, 128, 130, 332
klasa UriMatcher, 147
rozpoznawanie kolekcji, 140
rozpoznawanie URI, 147
wprowadzanie klauzuli WHERE, 134
wstawianie rekordw, 136
URI propozycji, 815
URI wyszukiwania, 815
URI wyszukiwania kontaktw, 989
zasobw, 97, 114
t1_1_en_port, 115
t1_enport, 114
t2, 115
1157
testport_port, 114
teststring_all, 114, 116
zasobw R.menu.moje_menu, 269
IDialogProtocol, 288
IETF, Internet Engineering Task Force, 597
ikony akustyczne, 855
implementacja
aktywnoci wyszukiwania, 824
bazowych klas aktywnoci, 1082
dugoterminowej usugi, 483, 486
dostawcw treci, 139
dostawcy treci BookProvider, 141
dostawcy widetu, 751
interfejsu AIDL, 379
interfejsu AnimationListener, 541
interfejsu Parcelable, 386
klasy AlarmManagerService, 514
klasy Renderer, 664
klasy ReportStatusHandler, 439
klasy WorkerThreadRunnable, 438
ksztatu RegularPolygon, 682, 683
metody delete, 147
metody getType(), 819
metody insert, 147
metody query, 146
metody update, 147
modeli widetw, 753
niestandardowego dostawcy propozycji, 814
obiektu nasuchujcego zdarze, 1087
owietlonego zielonego pokoju, 478
usugi lokalnej, 373
plik AndroidManifest.xml, 374
plik main.xml, 373
plik MainActivity.java, 373
usugi StockQuoteService2, 388
wtku roboczego, 438
instalacja narzdzi ADT, 57
instalowanie aktualizacji, 324
instancja paska dziaania, 1089
instancja widetu, 738
instrukcja awk, 123
instrukcja create, 125
instrukcja find, 123
instrukcja grep, 123
instrukcja insert, 137
1158 Skorowidz
intencja, intent, 40, 59, 155, 156
atrybuty dodatkowe, 161
dane, 159, 167
dziaanie, 159, 167
jawna nazwa klasy, 159
kategorie intencji, 168
mapa typu klucz warto, 159
MediaStore.ACTION_IMAGE_CAPTURE,
644
niejawna, 159
oczekujca, pending intent, 172
PendingIntent, 495
przydzielanie do ich skadnikw, 166
putExtras, 161
schemat danych, 167
cieka danych, 168
typ danych, 167
uprawnienia do danych, 168
VIEW, 160
interakcja aktywnoci wyszukiwania
lokalnego, 792
interakcja aktywnoci z przyciskiem
wyszukiwania, 777
interfejs
ActionBar, 1079
AJAX Language, 396
API, 43, 653
API Google Maps, 396
API multimediw, 619
aplikacji obsugujcej multimedia, 606
AttributeSet, 1030
ContactsContract, 967
createPackageContext(), 413
DDMS, 571
DialogInterface, 276
DialogInterface.OnClickListener, 1072
EGL, 651
glDrawElements, 654
GLSurfaceView.Renderer, 663
Google AJAX Language API, 396
Google Directions, 569
graficzny
pojemnik, kontener, 176
ukad graficzny, 176
widok, widet, kontrolka, 176
IDialogFinishedCallBack, 291
J
J2EE, Java 2 Platform Enterprise Edition, 58
Java Development Kit, 52
Java Standard Edition, 32
jawna nazwa klasy, 159
jawne przywoanie aktywnoci, 447
JCP, Java Community Process, 651
JDK, Java Development Kit, 52, 53, 56
JDK, Java SE Development Kit, 51
jzyk AIDL, 368
Skorowidz
jzyk HTML, 91
jzyk XML, 69
jzyk XUL, 39
JIT, Just-In-Time Compiler, 36
JRE, Java Runtime Environment, 52
JSON, JavaScript Object Notation, 343
JVM, Java Virtual Machine, 32
K
kamera
zmiana ustawie, 672
karta SD, 601, 602
foldery, 605
rdo plikw audio, 612
katalog
/res, 203
/res/layout/, 95
/res/menu, 269
/res/values, 1143
/res/xml, 297
/tools, 242
assets, 111
DCIM, 604
drawable-port, 240
drawable-square, 240
frameworks/base, 49
HOME, 53
layout, 69
layout-en, 114
Moje zamwienia, 1022
par klucz warto, 136
rawable-land, 240
tools, 55, 122
tools/android, 54
katalogi alternatywnych zasobw, 113
katalogi na karcie SD
DIRECTORY_ALARMS, 605
DIRECTORY_DCIM, 605
DIRECTORY_DOWNLOADS, 605
DIRECTORY_MOVIES, 605
DIRECTORY_MUSIC, 605
DIRECTORY_NOTIFICATIONS, 605
DIRECTORY_PICTURES, 605
DIRECTORY_PODCASTS, 605
DIRECTORY_RINGTONES, 605
1159
1160 Skorowidz
klasa
android.view.ViewGroup, 175
android.widget.ImageButton, 188
android.widget.ListAdapter, 213
android.widget.RadioButton, 193
AndroidHttpClient, 349, 350
AnimatedSimpleTriangleRenderer, 677
Animation, 541
AnimationDrawable, 521
app_name, 93
Application, 81
AppWidgetProvider, 742, 1107
ArrayAdapter, 202, 203, 208
AssetManager, 112
AsyncPlayer, 606, 617
AsyncTask, 351, 354, 357
AudioFormat, 629
AudioRecord, 626
AudioTrack, 618
BaseActionBarActivity, 1097, 1101
BaseAdapter, 218
BaseTester, 974
BDayWidgetModel, 752
BetterCursorWrapper, 732
BitmapFactory, 196
BookProviderMetaData, 139
BookTableMetaData, 140
Builder, 85
CamcorderProfile, 639
Camera, 42, 539
CameraProfile, 639
ClientCustPermMainActivity, 331
ComponentName, 162
ContactsContract.AggregationExceptions,
1001
ContentProvider, 141
ContentProviderOperation, 1003
ContentResolver, 137
ContentValues, 136, 137, 138
Context, 453
CustomHttpClient, 347
DatabaseHelper, 77
DebugActivity, 975, 1083
DeferWorkHandler, 436
DetailsFragment, 1037, 1050, 1052
DialogFragment, 1055, 1056, 1060
Dot, 1143
DownloadImageTask, 358
DownloadManager, 362
Drawable, 521
ES20AbstractRenderer, 711
Fragment, 1025, 1027
FragmentManager, 1045
FragmentTransaction, 1042, 1044, 1076
GenericManagedAlertDialog, 291
GenericPromptDialog, 292
Geocoder, 560, 565
GeomagneticField, 938
GeoPoint, 559
GestureDetector, 895
GestureOverlayView, 904
GLSurfaceView, 664, 667, 704
GridViewActivity, 214
GS20SimpleTriangleRenderer, 714
HelpDialogFragment, 1068
kod Java, 1069
ukad graficzny, 1069
HttpActivity, 358
HttpClient, 338
HttpGet, 338
HttpURLConnection, 349
ImageView, 196
Inflater, 221
Intent, 40, 162
IntentService, 469, 492
kod rdowy, 470
rozszerzanie na odbiorc
komunikatw, 472
ItemizedOverlay, 554, 557
JetPlayer, 606, 617
LayoutInflater, 278, 1031
LightedGreenRoom, 476
LinearLayout, 228
ListActivity, 593
ListFragment, 1047
ListPreference, 296
LocationManager, 567, 580, 583
MainActivity, 867, 1035, 1061
ManagedActivityDialog, 287, 288
ManagedDialogsActivity, 287, 290
ManateeAdapter, 221
MapActivity, 548, 550
Skorowidz
MapController, 551
MapView, 548, 888
Matrix, 539
MediaPlayer, 601, 610, 618
MediaRecorder, 622
MediaScanner, 647
MediaStore, 640, 646
MotionEvent, 861, 869, 873, 880
MyContactsProvider, 726
MyCursor, 731
MyFragment, 1029
MyLocationOverlay, 574
dostosowywanie, 577
zastosowanie, 574
MySMSMonitor, 590
NotePadProvider, 76
NotesList, 73
ObjectAnimator, 1075, 1139
OnGestureListener, 895
Overlay, 557
PaintDrawable,, 108
podstawowa
Activity, 427
BroadcastReceiver, 427
ContentProvider, 427
Service, 427
PolygonRenderer, 691
PrivActivity, 327
PromptDialogFragment, 1064, 1067
PromptListener, 279
RadioGroup, 194
RadioGroup.OnCheckedChangeListener,
193
RegularPolygon, 681
animowanie obiektw, 691
calcArrays, 689
Constructor, 689
getAngleArrays, 689
getIndexBuffer, 689
getVertexBuffer, 689
getXMultiplierArray, 689
getYMultiplierArray, 689
renderowanie kwadratu, 689
rysowanie koa, 693
RemoteViews, 741
RemoteViewsFactory, 1114
Renderer, 664
1161
ReportStatusHandler, 439
ScaleGestureDetector, 896
SearchActivity, 825, 828
SearchRecentSuggestionsProvider, 813, 840
SendAlarmOnceTester, 499
SensorManager, 908, 930, 936
Shakespeare, 1042
SimpleCursorAdapter, 200, 201, 202, 208
SimpleRectangleRenderer, 690
SimpleSuggestionProvider, 799
kod rdowy, 800
tryby bazodanowe, 801
SimpleTriangleRenderer, 665
SimpleTriangleRenderer2, 675, 676
SipAudioCall, 599
SipSession, 599
SmsManager, 588
SoundPool, 612, 617
maksymalna liczba prbek, 616
odtwarzanie dwiku, 613
parametr SRC_QUALITY, 616
strumie audio, 616
Spannable, 224
Spinner, 215
SpinnerAdapter, 1095
SQLiteQueryBuilder, 146, 149
static final ints, 93
StrictModeWrapper, 88
System.out.println, 81
TextToSpeech, 841
TexturedSquareRenderer, 698
ThreadGroup, 372
TitlesFragment, 1047
Toast, 293
debugowanie, 293
UriMatcher, 146, 147
Utils, 455
VelocityTracker, 874
ViewAnimation, 536
ViewGroup, 1043
widget.RadioGroup, 193
WorkerThreadRunnable, 438
XmlPullParser, 110
klasy
aktywnoci sterujcej, 975
android.view.View, 175
1162 Skorowidz
klasy
bazowe aktywnoci
ActionBar, 1084
AndroidManifest.xml, 1093
showAsAction, 1092
SpinnerAdapter, 1095
suce do obsugi widokw zdalnych, 1108
sterownika
DeferWorkHandler.java, 441
ReportStatusHandler.java, 441
Utils.java, 441
WorkerThreadRunnable.java, 441
zwizane z menu, 248
klatka kluczowa, 517
klauzula select, 141
klauzula WHERE, 134
klient usugi IStockQuoteService, 382
kliknicie, 187
klucz
API AJAX, 397
API MAP, 1018
interfejsu API mapy, 546
map-api, 321
prywatny, 318, 411
publiczny, 318, 411
KML, Keyhole Markup Language, 572
kod niestandardowy, 169
kod odbiorcy, 454
kod odbiorcy komunikatw, 465
kod usugi zdalnego widoku, 1128
kod rdowy Androida, 48
kod rdowy Git, 48
kody przyciskw dziaania, 835
kolumny kursora encji kontaktu, 997
kolumny kursora propozycji, 823
definiowanie, 824
Komatineni Satya, 23
komentarze w kodzie., 140
kompilacja, 501, 503, 506, 508, 511, 513
kompilator JIT, 36
kompilowanie jednostek cieniujcych, 709
kompilowanie kodu, 449
kompilowanie zasobw, 98
komunikat
ANR, 432, 450
testowy, 456
wysyanie komunikatu, 454
konfiguracja
alarmu, 493
kanaw, 629
strumieni audio, 855
uruchomieniowa, 63
urzdzenia pionowa, portrait, 240
urzdzenia pozioma, landscape, 240
urzdzenia tryb kwadratowy, square, 240
konfigurator widetw, 739
konfigurowanie
alertu odlegociowego, 579
ciaru, 230
klasy RemoteViewsFactory, 1114
konstruktora alertw, 278
menu za pomoc kodu, 255
obiektw nasuchujcych, 278
odbiorcy dla alarmu, 495
paska dziaania, 1096
powtarzalnego alarmu, 503
procedury obsugi kliknicia, 188
usugi RemoteViewsService, 1112
widokw obiektw, 1140
wirtualnego urzdzenia AVD, 64
zasad ThreadPolicy, 85
zasad VmPolicy funkcji, 86
zdarze onClick, 1117
rda danych, 611
konsola programisty, 1009
konstruktor alertw, 278
konstruktor klasy RemoteViewsFactory, 1114
kontakty
agregacja, 1001
analiza, 964
dodawanie kontaktu, 998
aktywno sterujca, 999
edytowanie niestandardowych danych, 961
edytowanie szczegw, 960
eksportowanie, 962
interfejs, 972
nieprzetworzone, 965, Patrz take badanie
nieprzetworzonych kontaktw
odczytywanie kontaktw zbiorczych, 971
pobieranie bazy kontaktw, 965
standardowe typy danych, 964
synchronizacja, 1002
tabela danych, 967
testowanie danych
aktywno sterujca, 996
Skorowidz
1163
listy, 205
MapView, 200, 549
ProgressBar, 223
RadioButton, 192
RatingBar, 223
ScrollBar, 223
Spinner, 215
TableRow, 233
TimePicker, 197
ToggleButton, 190
kontrolki
Androida, 182
Androida 2.2, 467
AutoCompleteTextView, 185
daty i czasu, 197
AnalogClock, 199
DatePicker, 197
DigitalClock, 199
TimePicker, 197
ImageView, 196
przyciskw, 187
Button, 187
CheckBox, 190
ImageButton, 188
RadioButton, 192
ToggleButton, 190
kontrolki tekstu, 183
AutoCompleteTextView, 185
EditText, 184
MultiAutoCompleteTextView, 186
TextView, 183
TextView, 181
widoku, 741
koprocesor graficzny, 649
kreator New Android Project, 598
kryteria dopasowania, 166
kSOAP2, 343
kursor propozycji, 822
kursor systemu Android, 133
kwalifikatory konfiguracji, 113
kwalifikatory zasobw, 241
Gsto pikseli na ekranie, 241
Jzyk i region, 241
Klawiatura, 241
Orientacja ekranu, 241
Rodzaj tekstowych danych wejciowych, 241
Rozmiary ekranu, 241
1164 Skorowidz
kwalifikatory zasobw
Sterowanie przy braku klawiatury
dotykowej, 241
Szersze/wysze ekrany, 241
Typ ekranu dotykowego, 241
Wersja rodowiska SDK, 241
kwerenda wyszukiwania, 833
L
layout, Patrz ukad graficzny
lista
aktywnych folderw, 720
animowanych klatek, 521
baz danych znajduje si w katalogu, 123
kodw przyciskw dziaania, 835
kolumn, 823
kompletacyjna, 273
pakietw, 408
preferencji, 297, 302
propozycji, 768
technologii, 946
ukadw graficznych, widetw i widokw,
1106
widetw, 1130
widetw ekranu startowego, 738
ListActivity
odczytywanie danych, 212
listingi, 449, 489
lo, 56
localhost, 56
lokalizacja certyfikatu testowego, 547
lupa, 243
LVL, License Verification Library, 1017
M
M3G, 652
macierz jednostkowa, 542
macierz transformacji, 539, 541
MacLean Dave, 23
magazyn gestw, 900
magazyn kluczy, 318, 319, 320
magazyn MediaStore, 645
magazyn multimediw, 644
ManagedActivityDialog
klasa DialogRegistry, 289
Skorowidz
menu, 247
aktywnoci, widoki i menu kontekstowe, 262
aktywno, 254
aktywnoci SearchInvokerActivity, 790
alternatywne, 264
dodatkowe znaczniki, 271
dodawanie elementw, 255, 256
dodawanie podmenu, 260
drugorzdne, 256
dynamiczne, 268
grupy, 250
konfiguracja, 255
Konta i synchronizacja, 954
kontekstowe, 261, 263
modyfikowanie AndroidManifest.xml, 258
odpowied na kliknicie, 251, 257
opcji, 249
rejestrowanie widoku TextView, 263
rozszerzone, 259
standardowe, 255
systemowe, 261
rodowisko testowe, 253
tworzone za pomoc kodu Java, 268
ukad graficzny, 254
w postaci ikon, 259
wybr elementw, 251
wykorzystanie intencji, 252
zapenianie menu, 266
zdefiniowane w plikach XML, 268
zmiana danych, 268
metadane bazy danych, 139
metadane dostawcy widetw, 1128
metadane wyszukiwania, 793, 807
metoda
activity.onCreateContextMenu(), 262
addEarcon(), 855
addPreferencesFromResource(), 297
addSubMenu(), 261
animate(), 523
ArrayAdapter.createFromResource(), 217
callService(), 393
cancel(), 515
captureImage(), 644
commit(), 314
context.getSharedPreferences(), 757
createFromResource(), 203
1165
DefaultHttpClient(), 347
detectAll(), 87
detectDiskReads(), 87
dismiss(), 1058, 1068
Display.getOrientation(), 926
Display.getRotation(), 926
distanceTo(), 569
doInBackground(), 353, 354, 356
doSpeak(), 845
doUpdate(), 937
draw(), 877
enableDefaults(), 87
enqueue(), 364
fabrykujca, 1029
findLocation(), 565
findPreference(), 312
fromRawResource(), 903
GestureLibraries.fromFile(), 903
GET, 340
getAccuracy(), 569
getAction(), 867, 886
getActionIndex(), 887
getActionMasked(), 887
getCheckedItemIds(), 213
getCheckedItemPositions(), 211
getCheckedRadioButtonId(), 195
getCount(), 150, 221
getDefaultEngine(), 856
getEdgeFlags(), 869
getExternalStoragePublicDirectory(), 607
getHttpClient(), 347, 348
getHttpContent(), 349
getInterpolation(), 532
getIntrinsicHeight(), 557
getIntrinsicWidth(), 557
getItemId(), 251
getLastNonConfigurationInstance(), 357
getMinBufferSize(), 629
getOrientation(), 931, 937
getPathSegments(), 146
getPointerCount(), 880
getPrefsToSave(), 754
getRotationMatrix(), 931
getSharedPreferences(), 303, 314
getString(), 303
getTag(), 1063
1166 Skorowidz
metoda
getText(), 208
getType(), 146, 819
getViewTypeCount(), 221
glDrawElements, 657
glVertexPointer, 654
handleMessage, 436
hasAccuracy(), 569
HTTP GET, 405
HTTP POST, 405
initCamera(), 634
initRecorder(), 637
insert(), 78, 204
invalidate(), 1144
isCancelled(), 356
isChecked(), 192
isEnabled(), 222
isLocationDisplayed(), 552, 575
isRouteDisplayed(), 552
LayoutInflater(), 278
LayoutInflater.from(), 278
ListView, 74
Log.d, 111
makeText(), 294
MenuBuilder.addIntentOptions, 267
mIndexBuffer, 658
MotionEvent.getAction(), 887
moveToFirst(), 133
newInstance(), 350
notifyDataSetChanged(), 204
nstartDrag(), 1143
obtain(), 873
obtainMessage(), 435
onAccuracyChanged(), 913, 914
onActivityCreated(), 1032
onActivityResult(), 568
onCheckedChanged(), 195
onClick(), 192, 873
onCreate(), 73, 79, 88, 206, 805, 903
onCreateContextMenu(), 264
onCreateDialog(), 284
onCreateOptionsMenu, 249, 265
onCreateView(), 1031, 1056, 1068
onDestroy(), 80, 446
onEnabled(), 753
onInflate(), 1030
onInit(), 856
onItemClick(), 208
onListItemClick(), 75
onMeasure(), 1144
onNewIntent(), 803, 805, 811
testowanie, 806
onOptionsItemSelected, 251, 252, 270
onPause, 446
onPostExecute(), 353
onPreExecute(), 353, 361
onPrepareDialog(), 284
onPrepareOptionsMenu, 268
onProgressUpdate(), 353
onProviderDisabled(), 573
onReceive(), 591, 753, 1120
onRestart(), 80
onResume(), 79, 80
onRetainNonConfigurationInstance(), 357
onSaveInstanceState(), 1047
onSearchRequested(), 789, 838
onSensorChanged(), 913, 919, 929
onStart(), 79
onStartCommand, 485
onStop(), 80, 446
onTouchEvent(), 862, 863, 877, 893
onUpdate(), 743, 744, 1111
penaltyDeath(), 85
PendingIntent.getActivity(), 172, 173
permitDiskReads(), 87
playSilence(), 856, 858
populate(), 557
postInvalidate(), 576
postTranslate, 538
PreferenceManager.getDefaultShared
Preferences(this), 303
preTranslate, 538, 542
publishProgress(), 353
putFragment(), 1047
queueSound(), 616
recognize(), 905
recycle(), 873
registerListener(), 913
requestLocationUpdates(), 571, 573
respondToMenuItem(), 433
rotate, 539
scanFile(), 646
Skorowidz
scheduleDistinctIntents(), 511
scheduleSameIntentMultipleTimes(), 510
sendAlarmOnce(), 499
sendBroadcast(), 173, 453, 454
sendDataMessage(), 588
sendMessage(), 435
sendMessageDelayed(), 435
sendMultipartTextMessage(), 589
sendSmsMessage(), 587
setAdapter(), 214
setBounds(), 557
setChecked(), 193
setContentView(), 211, 214
setData(), 435
setDataSource(), 611, 612, 621
setEdgeFlags(), 869
setEngineByPackageName(), 856
setEntries(), 313
setGroupVisible, 250
setImageResource(), 189
setLatestEventInfo(), 466
setListAdapter(), 214
setMeasureAllChildren(), 239
setOnCheckedChangeListener(), 193
setOneShot(), 522
setOnTouchListener(), 888
setOnUtteranceCompletedListener(), 846
setOptionText(), 303
setPendingIntentTemplate(), 1119
setRepeating(), 505
setRetainInstance(), 1033
setTargetFragment(), 1074
showAllRawContacts(), 993
sleep(), 432, 439
sort(), 204
speak(), 846
startActivity(), 169, 1074
startActivity(intent), 173
startSearch(), 789, 839
appSearchData, 789
globalSearch, 789
initialQuery, 789
selectInitialQuery, 789
startService(), 173, 372, 429, 469
stop(), 441
stopSelf, 485
1167
surfaceCreated(), 635
testAccounts(), 975
toUri(), 1118
translate, 539
tv.getText(), 225
uiCallback.sendEmptyMessage(0), 565
Utils.logThreadSignature(), 436, 439
VelocityTracker.obtain(), 874
zoomToSpan(), 558
zwrotna getCount(), 1115
zwrotna getItemId(), 1116
zwrotna getLoadingView(), 1116
zwrotna getViewAt(), 1115
zwrotna getViewTypeCount(), 1116
zwrotna hasStableIds(), 1117
zwrotna onAttach(), 1030
zwrotna onCreate(), 1030, 1115
zwrotna onCreateView(), 1031
zwrotna onDataSetChanged(), 1117
zwrotna onDestroy(), 1033, 1115
zwrotna onDestroyView(), 1033
zwrotna onDetach(), 1033
zwrotna onDrag()., 1144
zwrotna onInflate(), 1030
zwrotna onPause(), 1032
zwrotna onResume(), 1032
zwrotna onStop(), 1032
metody cyklu ycia aktywnoci, 79
metody gwne
delete, 139
getType, 139
insert, 139
query, 139
update, 139
metody pobierajce, 1046
metody uzyskiwania aktualizacji pooenia, 574
MFC, Microsoft Foundation Classes, 39
mikrostopnie, 562
mikrotesla, T, 930
milimetr, 235
MIME, Multipurpose Internet Mail
Extensions, 127
modu HttpClient, 338, 343, 405
monitorowanie zdarze animacji, 541
motyw, 227
MultiAutoCompleteTextView, 186
1168 Skorowidz
N
nagrywanie i odtwarzanie dwiku, 622
nakadanie tekstury, 683
nakadka ItemizedOverlay, 558
narzdzia
AAPR, 69
ADT, 38, 56
DDMS, 1019
Developer Tools, 57
do usuwania bdw, 81
wtkowania, 430
narzdzie
Abstract Window Toolkit, 32
adb, 323
AVD Manager, 83
edytora manifestu, 327
Export Unsigned Application Package, 321
Hierarchy Viewer, 175, 242
ekran urzdze, 243
tryb Pixel Perfect, 244
ukad graficzny, 242
jarsigner, 318
keytool, 321, 547
LogCat, 81, 82, 85, 867
wpisy, 870
Swing, 32
widet Toast, 571
zipalign, 322
nazwa skadnika, component name, 266
NDK, Native Development Kit, 940
NFC, Near Field Communication, 35, 939
aktywacja, 940
emulacja karty, 949
odbieranie terminali, 942
odczytywanie terminali, 946
P2P, 949
testowanie technologii, 950
trasowanie terminali, 941
tryby dziaania, 940
wybr filtru intencji, 943
numer portu 5554, 588
O
obiekt
addressContainer, 178
ApplicationInfo, 86
AudioRecord, 629
AudioRecord., 628
Builder, 87
ClipData, 1143
Criteria, 568
criteriaIntent, 266
cursor, 130, 772
DatePicker, 273
DragEvent, 1132
ACTION_DRAG_ENDED, 1132
ACTION_DRAG_ENTERED, 1132
ACTION_DRAG_EXITED, 1132
ACTION_DRAG_LOCATION, 1132
ACTION_DRAG_STARTED, 1132
ACTION_DROP, 1132
Drawable, 109
falseLayoutBottom, 873
FileDescriptor, 611
flight_sort_options_values, 301
Geocoder, 891
GridView, 214
HttpClient, 349
HttpGet, 349
HttpParams, 349
HttpPost, 349
includeInGlobalSearch, 812
IntentService, 485
klasy Spinner, 216
Location, 568
ManateeAdapter, 221
map, 141
MediaController, 621
Menu, 261
Message, 435
MotionEvent, 862, 873, 877, 880, 891
MotionView, 862
nasuchujcy, 251, 897, 1087
nasuchujcy OnInitListener, 845
NdefMessage, 949
NdefRecord, 948
nio, 666
OnCheckedChangeListener, 192
Parcelable, 391
PendingIntent, 515
PreferenceCategory, 311
RadioButton, 195
RemoteViews, 466
SensorEvent, 913
Skorowidz
SensorListener, 915
SharedPreferences, 314
SipProfile, 599
Spinner, 204, 216, 1095
SubMenu, 260
TextToSpeech, 845
TimePicker, 273
Toast, 294
trueBtnTop, 867
VelocityTracker, 875
ViewHolder, 221
wakelock, 478
XmlPullParser, 110
XmlResourceParser, 110
obiekty nasuchujce, 278
obsuga map, 888
obsuga wyjtkw, 343
obszar dropTarget, 1146
ochrona zasobw i funkcji urzdzenia, 325
odbieranie terminali NFC, 942
odbiorca BroadcastReceiver, 580, 858, 919
odbiorca komunikatw, broadcast receiver,
453, 462, 467, 579
alert odlegociowy, 579
definicja odbiorcy, 460
dugoterminowy, 467, 474
opnienia czasowe, 461
powiadomienia, 463
powielanie, 460
odbiorca pozaprocesowy, 462
odbiorca przebywajcy we wasnym
procesie, 462
odbiorca TestReceiver, 495
odbiorca treci, 461
odczytywanie danych w ListActivity, 210
odlego pomidzy dwoma obiektami, 569
odpowied na wybr elementw, 251
odpowied na zdarzenia onClick, 1120
odpowied na zdarzenie onDrag w strefie
upuszczania, 1137
odstpy, 235
odtwarzanie ciszy, 856
odtwarzanie ikony akustycznej, 856
odtwarzanie multimediw, 606
AsyncPlayer, 617
AudioTrack, 618
1169
1170 Skorowidz
okno
Launch Options, 84
LogCat, 82, 582, 629, 885, 988
Package explorer, 1018
terminalu, 52
opcja
Add External JARs, 404
Add note, 73
Android SDK and AVD Manager, 323
Android Tools, 321
Build Path/Configure Build Path., 404
Create project from existing sample, 899
Debug As/Android Application, 82
Debugowanie USB, 82
Export Signed Application Package, 323
Launch from snapshot, 84
QUEUE_ADD, 845
QUEUE_FLUSH, 845
Upload Application, 1018
Wipe user data, 84
Open Graphics Library, 650
OpenCORE PacketVideo, 37
OpenGL ES, 39
OpenGL ES 2.0, Patrz biblioteka OpenGL ES 2.0
OpenJDK, 52
operacja
setRotate, 542
setScale, 542
setSkew, 542
setTranslate, 542
operacje na macierzach, 541
oprogramowanie integracyjne, middleware, 337
optymalizacja aplikacji, 322
Oracle/Sun JDK, 53
organizowanie preferencji w kategorie, 310
orientacja w przestrzeni, 936
o gbi, 660
owietlony zielony pokj, 478
implementacja, 478
P
P2P, peer-to-peer, 949
pakiet
.apk, 610
android.app, 1027
android.location, 559
android.media, 601
android.nfc, 948
android.opengl.GLES20., 704
android.provider, 120
android.providers.Contacts, 132
android.view.animation, 525
com.svox.pico, 856
java.nio, 666
map, 545
nio, 652
OpenGL ES, 38
R.java, 92
pakiety, 407
dokumentacja SDK, 412
lista, 408
nazwa, 408
podpisywanie, 409
specyfikacja, 407
usuwanie, 409
pakiety java.*, 47
para kluczy, 318
parametr
childLayout, 202
from, 202
Intent, 169
odwieania czujnika, 913
requestCode, 169
SRC_QUALITY, 616
to, 202
uri, 137
parser jSON, 342
parser SOAP, 342
parser XML, 342
pary typu MIME, 129
pasek dziaania, 1079, 1080
interakcja z menu, 1091
obszar menu, 1081
obszar paska narzdzi, 1081
obszar przycisku ekranu startowego, 1080
obszar tytuu, 1080
obszar zakadek, 1081
tryb wywietlania listy, 1094, 1096
tryby nawigacji, 1089
pasek zakadek
badanie aktywnoci, 1093
pasujca aktywno, 266
Skorowidz
1171
debug.store, 320
debug_layout_activity.xml, 977
DebugActivity.java, 975
details.xml, 1041
DownloadImageTask.java, 351
DropBox, 85
DropZone.java, 1137
dropzone.xml, 1135
dwikowy z tekstem, 849
eclipse.exe, 53
gestures, 902
GPX, 572
HttpActivity.java, 347, 355, 357
HttpGetDemo.java, 338
IReportBack.java, 497, 973, 1082
JAR, 407
KML, 572
KMZ, 572
layout/lib_main.xml, 418
List_Layout.xml, 529
main.xml, 241, 302, 331, 354, 373, 421,
442, 456, 672
main_layout.xml, 114
main_menu.xml, 422, 442, 456, 669, 672
MainActivity.java, 363, 373, 867
mainmenu.xml, 302
manifest, 445
manifest klienta, 332
menu, 445
menu/lib_main_menu.xml, 418
MP3, 606
MultiViewTestHarnessActivity.java, 672
myappraw.apk, 321
MyLocationDemoActivity.java, 574
NoSearchActivity, 786
NotesList.java, 88
outfile.apk, 323
Palette.java, 1136
palette.xml, 1135
Person.aidl, 388
planets.xml, 216
ProAndroid3_R13_ProceduryObsugi.zip,
441
proguard.cfg, 1018
prompt_layout.xml, 277
R.java, 74, 97, 112, 115, 424
1172 Skorowidz
plik
RawContact.java, 990
release.keystore, 319
scale.xml, 524
sdcard.img, 602
SDK Manager, 54
searchable.xml, 793
SimpleSuggestionProvider.java, 800
SimpleTriangleRenderer.java, 672
StandaloneReceiver.java, 462
strings.xml, 92, 181, 215, 302, 829, 1145
TestAppActivity.java, 420
TestBCRActivity.java, 456
TestContactsDriverActivity.java, 977
TestHandlersDriverActivity.java, 442
TestLibActivity.java, 417
TestListWidgetProvider.java, 1122
TestOpenGLMainDriverActivity.java, 672
TestReceiver.java, 456
TestRemoteViewsFactory.java, 1126
TestRemoteViewsService.java, 1128
TranslateService.java, 402
ukadu graficznego, 94, 444, 458
ukadu graficznego main.xml, 94
Utils.java, 455, 456, 462, 980
web.xml, 68
wewntrzny, 137
XML, 98, 109, 268
XML animacji rotacyjnej, 531
XML preferencji, 302
XML zawierajcy definicje menu, 269
zasobw menu, 458
zawierajcy informacje o widecie, 1129
zdalnego ukadu graficznego, 1109
ZIP, 449, 489
pliki
aplikacji sucej do tumacze, 397
do testowania usug
implementacji dostawcy propozycji, 799
nieskompresowane, 98
parcelowane, 388
programu wywietlajcego list kont, 972
projektu menedera alarmw, 497
projektu TestBCR, 489
projektu testowego, 456
projektu z odbiorc komunikatw, 490
Skorowidz
preferencje, 295
ekran preferencji, 297
kategorie, 311
klasa ListPreference, 296
lista preferencji, 297
magazyn danych, 306
programowe zarzdzanie, 312
przechowywanie stanu aktywnoci, 313
widok CheckBoxPreference, 305
widok EditTextPreference, 307
widok RingtonePreference, 308
zagniedenie elementw
PreferenceScreen, 310
zapisywanie stanu, 313
preferencje aplikacji, 295
preferencje pola wyboru, 305
preferencje RingtonePreference, 309
prefiks vnd, 129
procedura DeferWorkHandler, 433
procedura obsugi, handler, 431, 432
klasy sterownika, 441
menu, 445
proces w Androidzie
Activity, 427
BroadcastReceiver, 427
ContentProvider, 427
Service, 427
procesy, 407
program SQLite Explorer, 965
program testujcy meneder alarmw, 502
projekt bibliotek, 414, 420, 425
identyfikatory wspdzielonych zasobw, 424
kod aktywnoci, 420
manifest, 419, 422
menu, 418, 422
twierdzenia, 414, 417
ukad graficzny, 418, 421
projekt Provider, 48
protok
odbiorcy komunikatw, 468
SIP, inicjalizacji sesji, 597
SOAP, 127
SSL, 37
zarzdzanych okien dialogowych, 283, 287
przeciganie obiektw, 876
kod Java, 876
ukad graficzny, 876
1173
Q
QEMU, 39
QSB, Quick Search Box, 769
R
raporty o bdach aplikacji, 1011
refleksja, 87
reguy przydzielania intencji, 166
rejestracja upowanienia, 126
rejestrator dwiku, 642
rejestrator wideo, 632
aktywno, 632
AndroidManifest.xml, 639
1174 Skorowidz
rejestrator wideo
kod obsugujcy wstrzymywanie, 633
kod przetwarzania, 636
metody zwrotne, 638
rejestrowanie aktualizacji lokacji, 569
rejestrowanie aktywnoci, 156
rejestrowanie dostawcy, 150
rejestrowanie multimediw, 621
analiza procesu rejestracji, 630
AudioRecord, 626
CAMCORDER, 625
MediaRecorder, 622
nieskompresowane dane audio, 630
rejestrowanie rozmowy, 625
VOICE_RECOGNITION, 625
za pomoc intencji, 641
rejestrowanie odbiorcy komunikatw, 456
rejestrowanie widoku dla menu
kontekstowego, 263
rejestry dziennika LogCat, 872
rekord, 136
renderowanie, 707
renderowanie kwadratu, 689
REST, REpresentational State Transfer, 119
RESTful, 41
RFID, Radio Frequency Identification, 939
RISC, Reduced Instruction Set Computer, 39
rodzaje adapterw, 204
ArrayAdapter<T>, 204
CursorAdapter, 204
ResourceCursorAdapter, 204
SimpleAdapter, 204
SimpleCursorAdapter, 204
rodzaje menu, 259
rodzaje zasobw, 99
cigi znakw, 99
kolorowe obiekty rysowane, 100
kolory, 99
obrazy, 100
tablice cigw znakw, 99
wielokrotnoci, 99
wasne pliki XML, 100
wasne, nieskompresowane pliki
dodatkowe, 100
wasne, nieskompresowane zasoby, 100
wymiary, 99
S
schemat danych, 167
SD, Secure Digital, 601
SDK, Software Development Kit, 32
SDP, Session Description Protocol, 598
segment cieki, 135
sekwencja przecigania, 1140
serwis Google Maps, 553
sie
3G, 38
Bluetooth, 38
EDGE, 38
WiFi, 38, 364
silnik Pico, 842
silnik przetwarzania tekstu na mow, 43
silnik renderujcy prostokt, 679
silnik SquareRenderer, 690
silnik TTS, 845, 850, 859
dostpno jzyka, 857
funkcje zaawansowane, 854
metody jzykowe, 857
odtwarzanie ciszy, 856
odtwarzanie ikony akustycznej, 856
SIP, Session Initiation Protocol, 585
skala mapy, 552
skalowanie, 528
sklep, Patrz Android Market
skadnia odniesienia do zasobu, 95
skrt MD5 certyfikatu testowego, 547
skrty dla elementu menu, 272
skrzynka odbiorcza, 592
SMS, Short Messaging Service, 585
foldery, 593
monitorowanie wiadomoci, 589
skrzynka odbiorcza, 592
Skorowidz
1175
1176 Skorowidz
T
tabela contact, 1001
tabela wyszukiwania, 968
tabela zawierajca wyjtki agregacji, 1001
tablica cigw znakw, 101
tablica dodanych elementw menu, 267
tablica flight_sort_options, 301
tagi do symulowania podmenu, 271
technologia ARM, 39
technologia M3G, 652
technologia Ndef, 946
technologia NFC, 939
teksturowane koa, 703
teksturowany kwadrat, 699
teksturowany wielobok, 700
tekstury, 694
glActiveTexture, 697
glBindTexture, 697
glGenTextures, 697
glTexCoordPointer, 697
glTexEnv, 697
glTexParameter, 697
GLUtils.texImage2D, 697
proces obsugi, 695
rysowanie, 698
znormalizowane wsprzdne, 694
terminal
ACTION_NDEF_DISCOVERED, 942
ACTION_TAG_DISCOVERED, 942
ACTION_TECH_DISCOVERED, 942
testowanie
animacji typu alfa, 530
danych kontaktu, 995
dugoterminowych usug, 488
dostawcy BookProvider, 150
kontaktw zbiorczych, 982
nieprzetworzonych kontaktw, 990
odbiorcw komunikatw, 489
pierwszestwa intencji, 512
procedur obsugi, 442
technologii NFC, 950
ustawie alarmw, 500
widetu wywietlajcego list, 1130
TextView, 183
ThreadPolicy, 85
transformacja widoku, 542
trasowanie terminali NFC, 941
tryb
agregacji, 970
debugowania, 82
debugowania USB, 82
MODE_PRIVATE, 314
MODE_WORLD_READABLE, 314
MODE_WORLD_WRITEABLE, 314
portretowy, 1052
rozwijalnego menu, 1104
ruchu ulicznego, 552
usuwania bdw, 82
widoku ulic, 552
wyszukiwania, 771
TTS, Text To Speech, 841
tworzenie
aktywnego folderu, 722
aplikacji Notepad, 70
cyfrowego podpisu, 411
dostawcy widetw, 1122
fragmentu wywietlajcego okna
dialogowe, 1055
instancji widetu, 742
intencji oczekujcej, 495
intencji oczekujcej na komunikat, 1118
intencji oczekujcych, 511
interfejsu uytkownika w pliku XML, 179
interfejsu uytkownika za pomoc kodu, 177
kategorii preferencji, 311
klasy fabrykujcej, 1126
klasy SoundPool, 616
komunikatu, 435
konfiguracji uruchomieniowej, 63
konta Google, 956
listy pakietw, 408
menu, 249
menu za pomoc plikw XML, 268
niestandardowych adapterw, 218
niestandardowych animacji, 1075
obiektu nasuchujcego listy nawigacji, 1095
odbiorcy komunikatw, 455
odniesie do kontrolek, 182
odpowiedzi dla elementw menu, 270
odpowiedzi dla menu kontekstowego, 264
okna alertu, 275
okna dialogowego zachty, 277
powiadomie, 465
pl wyboru, 190
Skorowidz
projektu, 449
projektu bibliotek, 417
prostego odbiorcy, 454
tosamoci w sklepie, 1006
trjktw, 676
urzdzenia AVD, 67, 602
wystpienia fragmentu, 1029
typy agregacji, 1001
typy danych, 167
typy MIME, 77, 128, 130, 265
typy treci, 129
application, 129
audio, 129
example, 129
image, 129
message, 129
model, 129
multipart, 129
text, 129
video, 129
U
Ubuntu, 52
ukad graficzny, layout, 94, 95, 113, 176, 227
animacja, 523
dostosowanie do urzdzenia, 239
optymalizacja, 242
tworzenie interfejsu uytkownika, 240
usuwanie bdw, 242
ukad graficzny
aktywnoci LocalSearchEnabledActivity, 794
aktywnoci SearchActivity, 792
aktywnoci SearchInvokerActivity, 789
aktywnoci TestAlarmsDriverActivity, 502
aktywnoci TestOpenGLMainDriver, 671
aktywnoci wyszukiwania, 828
aplikacji MapViewDemo, 549
aplikacji odtwarzajcej multimedia, 607
aplikacji rejestrujcej, 631
aplikacji tumaczcej, 398
dla aplikacji TouchDemo1, 863
do animacji poklatkowej, 519
do wywoywania klasy AsyncTask, 354
FrameLayout
widok ImageView, 239
1177
1178 Skorowidz
usuga, 59
dugoterminowa, 486, 488
Google Maps, 553
Google Maps JavaScript API, 569
HTTP, 43, 337
IStockQuoteService, 377, 380, 381, 382, 384
LocationManager, 566, 567, 569, 571
lokalna, 367, 369
nietrwaa, 484
notowa giedowych, 377
RemoteViewsService, 1112, 1113
RESTful, 48
ServiceManager, 566
StockQuoteService2, 390
Test60SecBCRService, 474
trwaa, 485
WakefulIntentService, 472
zdalna, 367
zorientowana na pooenie, 43
usugi
definiowanie interfejsu, 376
przekazywanie plikw parcelowanych, 388
przekazywanie typw danych, 385
uruchamianie i zatrzymywanie, 478
utworzenie i zamknicie, 478
usugi obsugujce jzyk AIDL, 368, 376
usugi w Androidzie, 368
ustanawianie menedera alarmw, 496
ustawianie obrazu, 196
usuwanie bdw, 82
usuwanie danych, 138
V
VoIP, Voice over Internet Protocol, 597
W
walidator licencji Android Market, Market
License Validator, 52
waluta klienta, Buyers Currency, 1016
warto startOffset, 529
wtek
narzdzia, 429
stan Dead, 441
stan New thread, 441
stan Not runnable, 441
stan Runnable, 441
wtek gwny
aktywnoci, 428
dostawcy treci, 429
informacje o stanie, 439
odbiorcy komunikatw, 429
opnianie operacji, 432
przetrzymywanie, 432
usugi, 429
wtek pojedynczy, 429
wtek roboczy, 436
testowanie, 442
wzy, 427
wiadomoci e-mail, 593
widoczno menu, 272
widok, view, 39, 58, 176
CheckBoxPreference, 305
contact_entities_view, 971
EditTextPreference, 307
encji kontaktw, 971
GridView, 200
ImageView, 221, 520
Konta i synchronizacja, 954
ListPreference, 313
ListView, 200, 201, 202, 525
MapView, 554
MapView wraz ze znacznikami, 557
niestandardowy - kko, 1140
RemoteViews, 735
RingtonePreference, 308
definiowanie preferencji, 309
interfejs UI, 308
TextView, 95, 202, 263
view_contacts, 971
zdalny, 1106
widet, widget, 176, 736
cykl ycia, 740
definiowanie, 740
definiowanie dostawcy, 747
definiowanie rozmiaru, 748
definiowanie w pliku manifest, 740
definiowanie w pliku XML, 741
implementacja abstrakcyjna modelu, 754
implementacja aktywnoci
konfiguratora, 761
implementacja dostawcy, 751
implementacja modeli, 753
implementacja modelu stanw, 758
Skorowidz
1179
wymiary, 235
Cale, 235
Milimetry, 235
Piksele, 235
Piksele niezalene od gstoci, 235
Piksele niezalene od skali, 235
Punkty, 235
wysoko pojemnika ListView, 209
wysyanie aplikacji, 1018
wysyanie komunikatu, 454
wysyanie powiadomienia, 464
wyszukiwanie
aktywno wyszukiwania, 773
aplikacja odpowiedzialna za ustawienia, 775
dostawcy propozycji, 772
dostp do aktywnoci testowych, 785
globalne, 768
interakcja aktywnoci z przyciskiem, 780, 782
jawne wywoywanie, 787
kod rdowy aktywnoci, 778
kursor propozycji, 772
menu aktywnoci, 784
metadane, 793
pliki aktywnoci, 778
pliki projektu, 777
pliki ukadu graficznego, 778
pole wyszukiwania, 769
propozycje wyszukiwania, 771, 772
propozycje zerowe, 771
SearchInvokerActivity, 789
tryb propozycji zerowych, 771
typy aktywnoci, 777
ukad graficzny aktywnoci, 781
ustawienia, 775, 777
wywoywanie aplikacji, 774
wyszukiwanie globalne, 769
dostawcy propozycji, 774
wyszukiwanie lokalne, 769, 790, 811
aktywno, 795
pole wyszukiwania, 796
wyniki wyszukiwania, 796
wyszukiwanie w Androidzie, 768
wywoanie dostawcy widetu, 1117
wywoanie obiektu Cursor, 132
wywoywanie ekranu startowego, 166
wywoywanie usugi, 391
wzorce identyfikatorw URI, 148
1180 Skorowidz
X
XUL, XML User Interface Language, 39
Z
zabezpieczenia, 317
certyfikat cyfrowy, 318
stosowanie uprawnienie, 325
zabezpieczenia na granicach procesu, 324
zabezpieczenia rodowiska wykonawczego, 324
zakadka
Permissions, 328
Virtual devices, 83
Window/Android SDK and AVD
Manager, 58
zasady postpowania w Android Market, 1006
zasoby, 40, 91
a zmiany konfiguracji, 112
alternatywne, 113
Androida, 98
childLayout, 203
domylne, 113
identyfikatory zasobw, 114
nieskompresowane, 111
obrazw w jzyku XML, 107
plurals, 101
R.drawable.frame_animation, 523
typu Color, 104
typu color w kodzie Java, 105
typu color-drawable, 108
typu color-drawable w kodzie Java, 108
typu color-drawable w kodzie XML, 108
typu dimension, 105
typu dimension w kodzie Java, 106
typu dimension w kodzie XML, 106
typu drawable, 196
typu image, 106
typu image w rodowisku Java, 107
typu layout, 94
typu string, 92, 95, 103
typu string w jzyku XML, 104
typu string w kodzie Java, 103
wielokrotno, 102