You are on page 1of 713

y˜– Š ’š8·

h‘‡•’Œ‡
w•’Š•„’š„‘Œˆ „“ŒŽ„†Œ

|‘ŒŽ„
„š–—œ‡„8†œ†‹
„Ž—œš‘’N†Œ
w•’ˆŽ—˜ „“ŒŽ„†ˆ±
Ž—I•ˆ …A‡8 ‹Œ—„Œ
–“•ˆ‡„Xœ

w•ˆŽ’‘„ –ŒA± „Ž


t„—ˆ•Œ„ kˆ–ŒŠ‘
’Xˆ Œˆ‘Œ9 v“„‘˜ “’A†Œ„
{š’ˆ Xœ†Œˆ ‘Œˆ  —ˆ
ŒˆŒ

w’‡ 8† „…„š –ŒA


–ŒA ‡’ ˜– ˜Š  …Œ…Œ’—ˆŽ„Œ
’Ž„Œ„†œ‘œ†‹ z˜““’•—
h‘‡•’Œ‡„ sŒ…•„•Œˆ–

k„š‘
š‘ n•Œ‰‰Œ—‹–
•Œ‰‰Œ—‹– Æ k
k„™Œ‡
„™Œ‡ n•Œ‰‰
n•Œ‰‰Œ—‹–
Naszym Przyjaciołom i naszej Rodzinie
— dziękujemy Wam bardzo za miłość i wsparcie
O autorach

Autorzy książki Android. Programowanie aplikacji. Rusz głową!

iths
David Griff

Dawn Griffiths

Dawn Griffiths zaczynała jako matematyk David Griffiths zaczął programować w wieku 12 lat,
na jednym z czołowych brytyjskich uniwersytetów, kiedy obejrzał film dokumentalny poświęcony
gdzie ukończyła studia matematyczne z wyróżnieniem. pracom Seymoura Paperta. W wieku 15 lat napisał
Później rozpoczęła karierę w branży produkcji implementację języka LOGO opracowanego przez
oprogramowania i dysponuje już dwudziestoletnim Paperta. Po zakończeniu studiów matematycznych
doświadczeniem w branży IT. na uniwersytecie zaczął pisać kod przeznaczony
dla komputerów oraz artykuły w czasopismach dla
Przed napisaniem książki Android. Programowanie ludzi. Pracował jako instruktor zwinnych metod
aplikacji. Rusz głową! Dawn napisała trzy inne książki programowania, programista, parkingowy, ale nie
z serii Head First (Head First Statistics — polskie w takiej kolejności. Potrafi programować w ponad
wydanie: Head First. Statystyka. Edycja polska, Head dziesięciu językach i pisać prozę tylko w jednym,
First 2D Geometry oraz Head First C — polskie a kiedy nie pisze, ani nie zajmuje się doradztwem,
wydanie: C. Rusz głową!) i brała udział w pracach spędza większość czasu ze swoją uroczą żoną
nad wieloma innymi z tej serii. — i współautorką tej książki — Dawn.
Kiedy nie pracuje nad żadną z książek z serii Rusz Przed napisaniem książki Android. Programowanie
głową!, doskonali się w tai-chi, biega, robi koronki aplikacji. Rusz głową! David napisał trzy inne książki
i gotuje. Uwielbia także podróżować i spędzać czas z tej serii: Head First Rails (polskie wydanie: Head First
ze swoim mężem Davidem. Ruby on Rails. Edycja polska), Head First Programming
oraz Head First C (polskie wydanie: C. Rusz głową!).

Można go śledzić na Twitterze:


http://twitter.com/HeadFirstDroid.

iv
Spis treści

Spis treści (skrócony)


Wprowadzenie xxiii
1 Zaczynamy. Skok na głęboką wodę 1
2 Tworzenie interaktywnych aplikacji. Aplikacje, które coś robią 39
3 Wiele aktywności i intencji. Jakie są Twoje intencje? 73
4 Cykl życia aktywności. Była sobie aktywność 115
5 Interfejs użytkownika. Podziwiaj widoki 163
6 Widoki list i adaptery. Zorganizuj się 227
7 Fragmenty. Zadbaj o modularyzację 269
8 Fragmenty zagnieżdżone. Zadbaj o potomstwo 325
9 Paski akcji. Na skróty 365
10 Szuflady nawigacyjne. Z miejsca na miejsce 397
11 Bazy danych SQLite. Odpal bazę danych 437
12 Kursory i zadania asynchroniczne. Nawiązywanie połączenia z bazą danych 471
13 Usługi. Do usług 541
14 Material Design. W materialistycznym świecie 597
A ART. Środowisko uruchomieniowe Androida 649
B ADB. Android Debug Bridge 653
C Emulator. Emulator Androida 659
D Pozostałości. Dziesięć najważniejszych zagadnień (których nie opisaliśmy) 663

Spis treści (z prawdziwego zdarzenia)

W
Wprowadzenie
Twój mózg jest nastawiony na Androida. Jesteś tu po to, by się czegoś nauczyć, natomiast
Twój mózg robi Ci przysługę, upewniając się, że to, czego się nauczyłeś, szybko wyleci
z pamięci. Twój mózg myśli sobie: „Lepiej zostawić miejsce na coś ważnego, na przykład: których
dzikich zwierząt lepiej unikać albo czy jeżdżenie nago na snowboardzie to dobry pomysł”.
A zatem, w jaki sposób możesz skłonić swój mózg, by myślał, że Twoje życie zależy
od umiejętności pisania aplikacji na Androida?

Dla kogo jest przeznaczona ta książka? xxiv


Wiemy, co sobie myślisz xxv
Wiemy, co sobie myśli Twój mózg xxv
Metapoznanie — myślenie o myśleniu xxvii
Oto co MY zrobiliśmy xxviii
Przeczytaj to xxx
Zespół recenzentów technicznych xxxii
Podziękowania xxxiii

v
Spis treści

Zaczynamy

1
Skok na głęboką wodę
Android błyskawicznie podbił świat.
Każdy chce mieć smartfon lub tablet, a urządzenia z Androidem są niezwykle popularne.
W tej książce nauczymy Cię, jak pisać własne aplikacje, a zaczniemy od pokazania procesu
przygotowania bardzo prostej aplikacji i uruchomienia jej na wirtualnym urządzeniu z Androidem.
W trakcie tych prac poznasz także kilka podstawowych komponentów wszystkich aplikacji
na Androida, takich jak aktywności i układy. Jedyną rzeczą, której będziesz do tego
potrzebować, jest znajomość Javy, choć wcale nie musisz być w niej mistrzem…

2
3
5
6
8
12
SDK
oid
13
dr
An 14
15
Android Studio utworzy pełną strukturę katalogów aplikacji 16
Przydatne pliki projektu 17
Edycja kodu z użyciem edytorów Android Studio 18
Uruchamianie aplikacji w emulatorze Androida 23
Tworzenie wirtualnego urządzenia z Androidem 24
Uruchomienie aplikacji w emulatorze 27
Postępy możesz obserwować w konsoli 28
Jazda próbna 29
Ale co się właściwie stało? 30
Usprawnianie aplikacji 31
Czym jest układ? 32
Plik activity_main.xml zawiera dwa elementy 33
Plik układu zawiera odwołanie do łańcucha, a nie sam łańcuch znaków 34
Zajrzyjmy do pliku strings.xml 35
Weź swoją aplikację na jazdę próbną 37
Twój przybornik do Androida 38

<Layout>

</Layout>
<Układ>
Aktywność
vi Układ
Urządzenie </Układ>
Spis treści

Tworzenie interaktywnych aplikacji

2
Aplikacje, które coś robią
Większość aplikacji musi w jakiś sposób reagować na poczynania
użytkowników.
Z tego rozdziału dowiesz się, co zrobić, aby Twoje aplikacje były nieco bardziej interaktywne.
Przekonasz się, jak zmusić aplikację, by coś zrobiła w odpowiedzi na działania użytkownika,
oraz jak sprawić, by aktywności i układy porozumiewały się ze sobą jak starzy kumple.
Przy okazji pokażemy Ci nieco dokładniej, jak naprawdę działa Android poznasz plik R,
czyli ukryty klejnot, który spaja pozostałe elementy aplikacji.

<Layout> 40
42
</Layout> <resources>
43
Układ </resources> 44
45
strings.xml
48
49
Stosuj zasoby łańcuchowe, a nie łańcuchy podawane w kodzie 50
Zmiana układu i zastosowanie w nim zasobów łańcuchowych 51
Aktywność Weź swoją aplikację na jazdę próbną 52
Dodanie wartości do komponentu Spinner 53
Dodanie do komponentu Spinner odwołania do string-array 54
Jazda próbna komponentu Spinner 54
Musimy zadbać o to, by przycisk coś robił 55
Niech przycisk wywołuje metodę 56
BeerExpert
Jak wygląda kod aktywności? 57
Dodaj do aktywności metodę onClickFindBeer() 58
Metoda onClickFindBeer() musi coś robić 59
Dysponując obiektem View, można odwoływać się do jego metod 60
Aktualizacja kodu aktywności 61
Pierwsza wersja aktywności 63
Jazda próbna — test modyfikacji 65
Tworzenie własnej klasy Javy 66
Dodaj do aktywności wywołanie metody naszej klasy,
aby była wyświetlana FAKTYCZNA porada 67
Kod aktywności, wersja 2 69
Co się dzieje podczas wykonywania tego kodu? 70
Jazda próbna — test aplikacji 71
Twój przybornik do Androida 72

vii
Spis treści

Wiele aktywności i intencji

3
Jakie są Twoje intencje?
Większość aplikacji potrzebuje więcej niż jednej aktywności.
Dotychczas mieliśmy do czynienia z aplikacjami składającymi się tylko z jednej aktywności. Kiedy
jednak sprawy się komplikują, jedna aktywność zwyczajnie nie wystarczy. Dlatego w tym rozdziale
pokażemy Ci, jak tworzyć aplikacje składające się z wielu aktywności i jak nasze aplikacje
mogą porozumiewać się z innymi, wykorzystując w tym celu intencje. Pokażemy także, jak można
używać intencji, by wykraczać poza granice naszych aplikacji, i jak wykorzystywać aktywności
należące do innych aplikacji dostępnych w urządzeniu do wykonywania akcji. To wszystko
zapewni nam znacznie większe możliwości.

74
75
75
Intencja
78
80
83
Do: InnaAktywnosc
84
85
86
87
Metoda putExtra() zapisuje w intencji dodatkowe informacje 88
Aktualizacja kodu aktywności CreateMessageActivity 91
Zastosowanie informacji przekazanych w intencji w klasie
ReceiveMessageActivity 92
Co się dzieje, gdy użytkownik kliknie przycisk Wyślij wiadomość? 93
Jazda próbna aplikacji 94
Jak działają aplikacje na Androida? 95
Co się dzieje podczas działania kodu? 99
Jak Android korzysta z filtrów intencji? 102
Hej, Musisz uruchomić aplikację na PRAWDZIWYM urządzeniu 105
użytkowniku! Każda Jazda próbna aplikacji 107
z tych aktywności może
Zmień kod, aby wyświetlać okno dialogowe 111
wysyłać wiadomości. Której
z nich chcesz użyć? Jazda próbna aplikacji 112
Twój przybornik do Androida 114

CreateMessageActivity

viii Android
Użytkownik
Spis treści

Cykl życia aktywności

4
Była sobie aktywność
Aktywności stanowią podstawę wszystkich aplikacji na Androida.

Wiesz już, jak tworzyć aktywności i jak sprawić, by jedna aktywność uruchomiła drugą, używając
do tego celu intencji. Ale co tak naprawdę dzieje się za kulisami? W tym rozdziale nieco
dokładniej poznamy cykl życia aktywności. Co się dzieje, kiedy aktywność jest tworzona
i usuwana? Jakie metody są wywoływane, gdy aktywność jest wyświetlana i pojawia się na
Aktywność ekranie, a jakie gdy aktywność traci miejsce wprowadzania i jest ukrywana? W jaki sposób
uruchomiona można zapisywać i odtwarzać stan aktywności?

116
118
onCreate()
119
122
123
onStart() Obiekty Handler umożliwiają planowanie wykonania kodu 124
Pełny kod metody runTimer() 125
Kompletny kod aktywności StopwatchActivity 126
onResume() Obrót ekranu zmienia konfigurację urządzenia 132
Od narodzin do śmierci: stany aktywności 133
Cykl życia aktywności: od utworzenia do usunięcia 134
W jaki sposób radzić sobie ze zmianami konfiguracji? 136
Aktywność onRestart()
Co się stanie po uruchomieniu aplikacji? 139
działająca Tworzenie i usuwanie to nie cały cykl życia aktywności 142
Cykl życia aktywności: widzialny czas życia 143
Zaktualizowany kod aktywności StopwatchActivity 147
Co się dzieje podczas działania aplikacji? 148
onPause()
Jazda próbna aplikacji 149
A co się dzieje, jeśli aplikacja jest tylko częściowo widoczna? 150
Cykl życia aktywności: życie na pierwszym planie 151
onStop()
Zatrzymanie stopera w razie wstrzymania aktywności 154
Kompletny kod aktywności 157
Wygodny przewodnik po metodach cyklu życia aktywności 161
onDestroy() Twój przybornik do Androida 162

Aktywność
usunięta
ix
Spis treści

Interfejs użytkownika

5
Podziwiaj widoki
Nie masz innego wyjścia, musisz tworzyć szałowe układy.
Jeśli chcesz pisać aplikacje, których inni będą używać, musisz zadbać o to, by wyglądały one
dokładnie tak, jak sobie tego życzysz. Zagadnienie tworzenia układów potraktowaliśmy
dotychczas bardzo powierzchownie, najwyższy zatem czas, by przyjrzeć mu się dokładniej.
W tym rozdziale pokażemy Ci różne typy układów, które można tworzyć, i zabierzemy Cię na
wycieczkę po najważniejszych komponentach GUI i sposobach ich stosowania. Pod koniec tego
rozdziału przekonasz się, że choć wszystkie te układy i komponenty wyglądają nieco inaczej,
to jednak mają ze sobą więcej wspólnego, niż można by przypuszczać.

Widoki mogą być 165


rozmieszczane
względem układu 168
nadrzędnego… Rozmieszczanie widoków względem innych widoków 170
Atrybuty do rozmieszczania widoków względem innych widoków 171
RelativeLayout — podsumowanie 173
…bądź względem
innych widoków. Układ LinearLayout wyświetla widoki w jednym wierszu lub kolumnie 174
Zmieńmy nieco prosty układ liniowy 176
Dodawanie wagi do widoków 179
Dodawanie wagi do większej liczby widoków 180
Stosowanie atrybutu android:gravity — lista wartości 182
Inne wartości, których można używać w atrybucie
android:layout_gravity 184
Kompletny układ liniowy 185
LinearLayout — podsumowanie 186
Układ GridLayout wyświetla widoki w siatce 189
Dodawanie widoków do układu siatki 190
Utwórzmy nowy układ siatki 191
Wiersz 0: dodajemy widoki do określonych wierszy i kolumn 193
Wiersz 1: tworzymy widok zajmujący komórki kilku kolumn 194
Wiersz 2: tworzymy widok zajmujący komórki kilku kolumn 195
Układ względny
Pełny kod układu siatki 196
<Layout> GridLayout — podsumowanie 197
Układy i komponenty GUI mają wiele wspólnego 201
</Layout>
ViewGroup Zabawy z widokami 205
layout.xml Twój przybornik do Androida 225

Pole
Przycisk tekstowe

x View View
Spis treści

Widoki list i adaptery

6
Zorganizuj się
Chcesz wiedzieć, jaki jest najlepszy sposób na określenie struktury aplikacji?
Znasz już podstawowe elementy konstrukcyjne używane do tworzenia aplikacji, więc teraz nadszedł
czas, żebyś się lepiej zorganizował. W tym rozdziale pokażemy Ci, jak możesz przekształcić
zbiór pomysłów w niesamowitą aplikację. Zobaczysz, że listy danych mogą stać się kluczowym
elementem projektu aplikacji i że łączenie ich może prowadzić do powstania aplikacji łatwej
w użyciu i zapewniającej ogromne możliwości. Przy okazji zapoznasz się z obiektami
nasłuchującymi i adapterami, dzięki którym Twoja aplikacja stanie się bardziej dynamiczna.

228
Ekran

początkowy
229
z listą opcji
230
231
Menu zawierające 232
wszystko, co 233
można u nas Struktura aplikacji dla kafeterii Coffeina 234
zjeść Układ aktywności głównego poziomu składa się z obrazka i listy 238
Kompletny kod układu aktywności głównego poziomu 240
Szczegółowe Zapewnianie reakcji ListView na kliknięcia za pomocą
informacje obiektu nasłuchującego 241
o każdym Kompletny kod aktywności TopLevelActivity 243
napoju Jak utworzyć aktywność listy? 249
Łączenie widoków list z tablicami za pomocą adaptera ArrayAdapter 251
Dodanie adaptera ArrayAdapter do aktywności DrinkCategoryActivity 252
Co się stanie po wykonaniu kodu? 253
Jak obsługiwaliśmy kliknięcia w aktywności TopLevelActivity? 256
Kompletny kod aktywności DrinkCategoryActivity 258
Aktywność szczegółów wyświetla informacje o jednym rekordzie 259
Wypełnienie widoków danymi 261
Kod aktywności DrinkActivity 263
Jazda próbna aplikacji 266
Twój przybornik do Androida 268
Utworzymy adapter ArrayAdapter, To jest
To jest nasz widok listy. nasza tablica.
aby powiązać widok listy z naszą tablicą.

ListView Array Drink.


Adapter drinks

xi
Spis treści

Fragmenty

7
Zadbaj o modularyzację
Wiesz już, jak tworzyć aplikacje, które działają tak samo niezależnie od tego,
na jakim urządzeniu zostały uruchomione…
…ale co zrobić w przypadku, kiedy akurat chcesz, by aplikacja wyglądała i działała inaczej
w zależności od tego, czy zostanie uruchomiona na telefonie, czy na tablecie? W tym rozdziale
pokażemy Ci, co zrobić, aby aplikacja wybierała układ, który najlepiej pasuje do wielkości
ekranu urządzenia. Oprócz tego przedstawimy fragmenty, czyli modularne komponenty
kodu, które mogą być wielokrotnie używane przez różne aktywności.

273
275
276
278
282
283
284
286
290
292
294
295
A zatem fragment 301
będzie zawierał jedną listę. 302
Kiedy chcieliśmy użyć aktywności 303
zawierającej pojedynczą listę,
305
zastosowaliśmy aktywność typu
ListActivity. Zastanawiam się, czy 307
przypadkiem nie istnieje jakiś typ 309
fragmentu stanowiący odpowiednik 315
tej klasy.
319
321
322

xii
Spis treści

Fragmenty zagnieżdżone

8
Zadbaj o potomstwo
Wiesz już, że stosowanie fragmentów w aktywnościach pozwala na
wielokrotne wykorzystywanie kodu i zwiększa elastyczność aplikacji.
W tym rozdziale mamy zamiar pokazać Ci, jak zagnieżdżać fragmenty w innych
fragmentach. Dowiesz się, jak używać menedżera fragmentów podrzędnych,
by poskromić niesforne transakcje. Ponadto dowiesz się, dlaczego tak ważna jest
znajomość różnic między aktywnościami i fragmentami.

326
332
335
Metoda getFragmentManager() tworzy transakcje
na poziomie aktywności 340
Zagnieżdżone fragmenty wymagają zagnieżdżonych transakcji 341
Kompletny kod fragmentu WorkoutDetailFragment 343
Jazda próbna aplikacji 344
Dlaczego kliknięcie przycisku powoduje awarię aplikacji? 345
Przyjrzyjmy się kodowi układu StopwatchFragment 346
Zaimplementuj we fragmencie interfejs OnClickListener 349
Powiązanie obiektu nasłuchującego OnClickListener z przyciskami 351
Kod fragmentu StopwatchFragment 352
Jazda próbna aplikacji 354
Kod fragmentu WorkoutDetailFragment 358
Jazda próbna aplikacji 359
Twój przybornik do Androida 364

Jak widzę, pojawiły się


jakieś transakcje fragmentu.
Muszę je natychmiast
zastosować.
Transakcje
fragmentu

Aktywność Fragment

xiii
Spis treści

Paski akcji

9
Na skróty
Każdy lubi chodzić na skróty.
Z tego rozdziału dowiesz się, jak korzystając z pasków akcji, wzbogacić aplikację o możliwość chodzenia
na skróty. Pokażemy Ci, jak uruchamiać inne aplikacje za pomocą elementów akcji dodawanych do
pasków akcji, jak udostępniać treści innym aplikacjom, używając dostawcy akcji współdzielenia, oraz
jak poruszać się w górę hierarchii aplikacji za pomocą przycisku W górę umieszczonego na pasku
akcji. Dowiesz się też, jak można nadawać tworzonym aplikacjom spójny wygląd i sposób działania,
korzystając z motywów, i poznasz pakiet biblioteki wsparcia systemu Android.

366
tępniania
Właśnie tak wygląda akcja udos 367
jej
wyświetlona na pasku akcji. Po aplikacji,
kliknięciu wyświetlana jest lista 368
ci.
którym możemy udostępnić treś 369
370
371
372
373
374
375
376
377
378
379
382

383
384
386
388
389
391
API 21? 392
Idealnie pasuje. 393
394
395

values-v21
<xml> Name: AppTheme
</xml>
Parent: Theme.Material.Light
Android styles.xml
xiv
Spis treści

Szuflady nawigacyjne

10
Z miejsca na miejsce
Aplikacje są nieporównanie lepsze, gdy można się po nich łatwo poruszać.
W tym rozdziale przedstawimy Ci szufladę nawigacyjną wysuwany panel, który jest
wyświetlany na ekranie po przesunięciu palcem lub kliknięciu ikony umieszczonej na pasku akcji.
Pokażemy Ci, jak można wyświetlać w niej listę odnośników umożliwiających przechodzenie
do kluczowych węzłów aplikacji. Oprócz tego przekonasz się, że przełączanie fragmentów
pozwala łatwo docierać do tych węzłów i je szybko wyświetlać.

398
399
400
401
402
403
404
405
406
407
412
413
414
Stosowanie ActionBarDrawerToggle 417
Modyfikowanie elementów paska akcji w trakcie działania aplikacji 418
Zaktualizowany kod aktywności MainActivity 419
Włączenie możliwości otwierania i zamykania szuflady nawigacyjnej 420
Synchronizacja stanu przycisku ActionBarDrawerToggle 421
Zaktualizowany kod aktywności MainActivity 422
Obsługa zmian konfiguracji 425
Reagowanie na zmiany stosu cofnięć 426
Dodawanie znaczników do fragmentów 427
Kompletny kod aktywności MainActivity 429
Zawartość ekranu jest
wyświetlana w układzie Jazda testowa aplikacji 435
FrameLayout. Chcemy, by Twój przybornik do Androida 436
wypełniała ona cały dostępny
obszar ekranu. W tym przykładzie
jest ona częściowo przesłonięta
przez szufladę nawigacyjną.

xv
Spis treści

Bazy danych SQLite

11
Odpal bazę danych
Jeśli rejestrujesz najlepsze wyniki lub przesyłane komunikaty,
to Twoja aplikacja będzie musiała przechowywać dane.
A w Androidzie dane są zazwyczaj bezpiecznie i trwale przechowywane w bazach danych
SQLite. W tym rozdziale pokażemy Ci, jak utworzyć bazę danych, dodawać do niej tabele,
wypełnić ją wstępnie danymi, a wszystko to za pomocą pomocnika SQLite. Dowiesz się też,
w jaki sposób można bezproblemowo przeprowadzać aktualizacje struktury bazy danych
i jak w razie konieczności wycofania zmian wrócić do jej wcześniejszych wersji.

Znowu w kafeterii Coffeina 438


Jaśnie panie,
Android trwale przechowuje dane, używając baz danych SQLite 439
oto pańska baza
danych. Czy mogę Android udostępnia kilka klas związanych z SQLite 440
jeszcze czymś służyć? Obecna struktura aplikacji kafeterii Coffeina 441
Pomocnik SQLite zarządza Twoją bazą danych 443
Pomocnik SQLite 443
Tworzenie pomocnika SQLite 444
Wnętrze bazy danych SQLite 446
Tabele tworzymy w języku SQL 447
Wstawianie danych za pomocą metody insert() 448
Aktualizacja rekordów za pomocą metody update() 449
onCreate() Określanie wielu warunków 450
Kod klasy CoffeinaDatabaseHelper 451
Co robi kod pomocnika SQLite? 452
Pomocnik SQLite Co zrobić, gdy trzeba będzie zmienić bazę? 455
Bazy danych SQLite mają numer wersji 456
Aktualizacja bazy danych — omówienie 457
DRINK Jak pomocnik SQLite podejmuje decyzje? 459
Aktualizacja bazy w metodzie onUpgrade() 460
Nazwa: Coffeina
Przywracanie starszej wersji bazy za pomocą metody onDowngrade() 461
Wersja: 1
Zaktualizujmy bazę danych 462
Aktualizacja istniejącej bazy danych 465
Zmiana nazwy tabeli 466
Baza danych SQLite
Pełny kod pomocnika SQLite 467
Kod pomocnika SQLite (ciąg dalszy) 468
Co się dzieje podczas działania kodu? 469
Twój przybornik do Androida 470

xvi
Spis treści

Kursory i zadania asynchroniczne

12
Nawiązywanie połączenia z bazą danych
Jak łączysz swoje aplikacje z bazami danych SQLite?
Dotychczas dowiedziałeś się, jak tworzyć bazy danych, używając pomocnika SQLite. Kolejnym
krokiem będzie uzyskanie dostępu do tych baz danych w aktywnościach. Z tego rozdziału dowiesz
się, jak tworzyć kursory, by pobierać dane z bazy danych, jak poruszać się po kursorach oraz
jak pobierać z nich dane. Oprócz tego dowiesz się, jak używać adapterów kursorów, by łączyć
kursory z widokami list. A na koniec przekonasz się, że pisanie wydajnego kodu wielowątkowego
korzystającego z klasy AsyncTask może zagwarantować wysoką wydajność działania aplikacji.

474
onPreExecute 478
479
481
488
doInBackground 489
490
Dodanie ulubionych napojów do aktywności DrinkActivity 508
Kod aktywności DrinkActivity 513
Nowy kod aktywności głównego poziomu 518
onProgressUpdate Zmodyfikowany kod aktywności TopLevelActivity 524
Metoda onPreExecute() 531
Metoda doInBackground() 532
Metoda onProgressUpdate() 533
onPostExecute Metoda onPostExecute() 534
Klasa AsyncTask 535
Kod aktywności DrinkActivity 537
Twój przybornik do Androida 540

Hej, adapterze, Hej, kursorze, potrzebuję


potrzebuję więcej więcej… Kursorze? Stary,
danych. jesteś tam?

Kursor
Widok ListView Adapter CursorAdapter
xvii
Spis treści

Usługi

13
Do usług
Są operacje, które będziemy chcieli realizować niezależnie od tego,
która aplikacja jest widoczna na ekranie.
Na przykład kiedy rozpoczniesz odtwarzanie pliku w aplikacji muzycznej, to najprawdopodobniej
będziesz oczekiwać, że będzie ona kontynuowała odtwarzanie nawet wówczas, gdy przejdziesz
do innej aplikacji. Z tego rozdziału dowiesz się, jak używać usług, by radzić sobie w sytuacjach
takich jak ta. Przy okazji nauczysz się korzystać z kilku wbudowanych usług systemu Android.
Dowiesz się, jak za pomocą usługi powiadomień sprawić, by użytkownik zawsze był dobrze
poinformowany, i jak poznać aktualne położenie geograficzne, korzystając z usługi lokalizacji.
543
545
546
547
554
557
559
561
562
570
573
575
577
578
580
582
586
Tworzenie obiektu ServiceConnection 587
Powiązanie z usługą należy utworzyć podczas uruchamiania aktywności 588

<Layout>
Wyświetlanie przebytego dystansu 589
Kompletny kod aktywności MainActivity 590
</Layout>
Twój przybornik do Androida 595
activity_main.xml
getDistance() Systemowa
usługa
lokalizacyjna

1.11
MainActivity.java OdometerService.java
xviii
Liczba przebytych kilometrów.
Spis treści

Material Design

14
W materialistycznym świecie
W API poziomu 21 Google wprowadził Material Design.
Z tego rozdziału dowiesz się, czym jest Material Design i jak sprawić, by nasze aplikacje do
niego pasowały. Zaczniemy od przedstawienia widoków kart (ang. card views), których możemy
wielokrotnie używać w całej aplikacji, zapewniając jej spójny wygląd i sposób obsługi. Następnie
przedstawimy widok RecyclerView elastycznego przyjaciela widoków list. Przy okazji dowiesz
się także, jak tworzyć własne adaptery i jak całkowicie zmienić wygląd widoku RecyclerView,
używając zaledwie dwóch wierszy kodu.

598
600
603
604
606
607
608
609
610
611
612
613
Kod klasy PizzaMaterialFragment 614
Do rozmieszczania zawartości RecyclerView używa menedżera układu 615
Określanie menedżera układu 616
Kompletny kod klasy PizzaMaterialFragment 617
Zastosowanie fragmentu PizzaMaterialFragment
w aktywności MainActivity 618
Co się stanie po uruchomieniu kodu? 619
Utworzenie aktywności PizzaDetailActivity 627
Co musi robić aktywność PizzaDetailActivity? 628
Aktualizacja pliku AndroidManifest.xml 628
Kod pliku PizzaDetailActivity.java 629
Obsługa kliknięć w widoku RecyclerView 631
Dodanie interfejsu do adaptera 634
Implementacja interfejsu Listener
we fragmencie PizzaMaterialFragment 636
Umieszczenie treści na samym początku 639
Kompletny kod pliku układu fragment_top.xml 644
Kompletny kod klasy TopFragment 645
Twój przybornik do Androida 647

xix
Spis treści

ART

A
Środowisko uruchomieniowe Androida
Aplikacje na Androida muszą działać na urządzeniach wyposażonych
w słabe procesory i bardzo małą pamięć.
Aplikacje pisane w Javie mogą potrzebować sporo pamięci, a ponieważ działają wewnątrz własnej
wirtualnej maszyny Javy (JVM), ich uruchomienie na komputerze o niewielkiej mocy obliczeniowej
może trwać dosyć długo. Android rozwiązuje ten problem, nie używając JVM do uruchamiania
aplikacji. Zamiast JVM używa całkowicie odmiennej wirtualnej maszyny, nazywanej środowiskiem
uruchomieniowym Androida (ang. Android Runtime, w skrócie ART). W tym dodatku zobaczysz,
jak to się dzieje, że ART umożliwia dobre działanie aplikacji napisanych w Javie nawet na małych
komputerach o niewielkiej mocy obliczeniowej.

.java .class classes.dex .apk

ADB

B
Android Debug Bridge
W tej książce skoncentrowaliśmy się na zaspokajaniu wszystkich potrzeb
związanych z pisaniem aplikacji na Androida z wykorzystaniem IDE.
Zdarzają się jednak sytuacje, w których zastosowanie narzędzi obsługiwanych z poziomu wiersza
poleceń jest po prostu przydatne. Mamy tu na myśli na przykład przypadki, gdy Android Studio nie
jest w stanie zauważyć urządzenia z Androidem, choć my wiemy, że ono istnieje. W tym rozdziale
przedstawimy Android Debug Bridge (w skrócie ADB) obsługiwany z poziomu wiersza
poleceń program narzędziowy, którego można używać do komunikacji z emulatorem
lub z rzeczywistymi urządzeniami zaopatrzonymi w Androida.

adb adbd Urządzenie

polecenie adb proces


demona adb

xx Urządzenie
Spis treści

Emulator

C
Emulator Androida
Czy miałeś kiedyś wrażenie, że cały swój czas spędzasz, czekając na emulator?
Nie ma najmniejszych wątpliwości co do tego, że emulator Androida jest bardzo przydatny. Dzięki
niemu możemy się przekonać, jak nasza aplikacja będzie działała na urządzeniach innych niż te,
do których mamy fizyczny dostęp. Niekiedy jednak można odnieść wrażenie, że emulator działa…
wolno. W tym dodatku wyjaśnimy, dlaczego tak się dzieje. Ale to nie wszystko, damy Ci bowiem
także kilka wskazówek, jak przyspieszyć jego działanie.

AVD AVD AVD AVD AVD

Wszystkie wirtualne urządzenia


z Androidem (AVD) są wykonywane Emulator QEMU
przez emulator QEMU.

Pozostałości

D
Dziesięć najważniejszych zagadnień (których nie opisaliśmy)
Nawet po tym wszystkim, co opisaliśmy w tej książce, wciąż pozostaje
wiele innych interesujących zagadnień.
Jest jeszcze kilka dodatkowych spraw, o których musisz się dowiedzieć. Czulibyśmy się nie
w porządku, gdybyśmy je pominęli, a jednocześnie chcieliśmy oddać w Twoje ręce książkę,
którą dasz radę podnieść bez intensywnego treningu na siłowni. Dlatego zanim odłożysz
tę książkę, przeczytaj kilka dodatkowych zagadnień opisanych w tym dodatku.

Bateria już 664


jest prawie 665
rozładowana… jeśli 666
to kogoś interesuje.
667
668
669
669
670
671
672
Android
673

S
674

xxi
Jak korzystać z tej książki

Wprowadzenie
Nie mogę uwierzyć,
że zamieścili to w książce
o programowaniu aplikacji
na Androida!

odpowiemy na palące
W tej części książki orzy umieścili
pytanie: „Dl acz ego aut
e niezwykłe rzeczy?”.
w książce te wszystki

jesteś tutaj  xxiii


Jak korzystać z tej książki

Dla kogo jest przeznaczona ta książka?


Jeśli możesz odpowiedzieć twierdząco na wszystkie poniższe pytania…

1 Czy już umiesz programować w Javie?

2 Czy chcesz być mistrzem w pisaniu aplikacji na Androida, stworzyć


wspaniały program, zarobić fortunę, a na emeryturę przenieść się
na swoją własną wyspę?
No dobrze, być może te plany
są nieco zbyt dalekosiężne.
3 Czy wolisz coś robić i wykorzystywać zdobytą wiedzę czy słuchać Ale przecież od czegoś trzeba
kogoś na niekończących się wykładach? zacząć, prawda?

…to jest to książka dla Ciebie.

Kto raczej nie powinien sięgać po tę książkę?


Jeśli możesz odpowiedzieć twierdząco na któreś z poniższych pytań…

1 Czy szukasz szybkiego wprowadzenia lub podręcznika do pisania


aplikacji na Androida?

2 Czy wolisz, by piętnaście wrzeszczących małp wyrwało Ci paznokcie


z palców u stóp niż nauczyć się czegoś nowego? Czy uważasz, że
książka o programowaniu aplikacji na Androida powinna opisywać
każde możliwe zagadnienie, a przy okazji jej lektura powinna być
śmiertelnie nużąca, i to im bardziej, tym lepiej?

…to ta książka nie jest dla Ciebie.

[Notatka z działu marketingu:


Ta książka jest dla każdego, kto
ma kartę kredytową… Chociaż
w sumie czeki też przyjmujemy
.]

xxiv Wprowadzenie
Wprowadzenie

Wiemy, co sobie myślisz


„Jakim cudem to może być poważną książką o tworzeniu aplikacji na Androida?”

„Po co te wszystkie obrazki?”

„Czy w taki sposób można się czegokolwiek nauczyć?”


Twój mózg myśli, że
właśnie TO jest istotne.
Wiemy, co sobie myśli Twój mózg
Twój mózg pragnie nowości. Zawsze szuka, przegląda i wyczekuje czegoś
niezwykłego. Tak został stworzony i to pomaga Ci przetrwać.

Zatem co Twój mózg robi z tymi wszystkimi rutynowymi, zwyczajnymi, normalnymi


informacjami, jakie do niego docierają? Otóż robi wszystko, co tylko może,
aby nie przeszkadzały mu w jego naprawdę ważnym zadaniu — zapamiętywaniu
rzeczy, które są naprawdę ważne. Twój mózg nie traci czasu i energii na
zapamiętywanie nudnych informacji; one nigdy nie przechodzą przez filtr
„to jest ewidentnie nieważne”.

Skąd Twój mózg wie, co jest istotne? Załóżmy, że jesteś na codziennej


przechadzce i nagle przed Tobą staje tygrys. Co się dzieje w Twej głowie
i w Twoim ciele?
Wspaniale. Zostało
Neurony płoną. Emocje szaleją. Adrenalina napływa falami. jeszcze tylko 680
głupich, nudnych
I właśnie stąd Twój mózg wie, że… i drętwych stron
To musi być ważne! Nie zapominaj o tym!! ózg
Twój m, że
u waża ie warto
Ale wyobraź sobie, że jesteś w domu albo w bibliotece. Jesteś w bezpiecznym n
TEGO tywać.
miejscu — przytulnym i pozbawionym tygrysów. Uczysz się. Przygotowujesz się zapamię
do egzaminu. Albo rozgryzasz jakiś trudny problem techniczny, którego
rozwiązanie, według szefa, powinno zająć Ci tydzień, a najdalej dziesięć dni.

Jest tylko jeden, drobny problem. Twój mózg stara się Ci pomóc.
Próbuje zapewnić, aby te ewidentncie nieważne informacje nie zajęły
cennych zasobów w Twojej głowie. Zasobów, które powinny zostać
wykorzystane na zapamiętanie naprawdę ważnych rzeczy. Takich jak
tygrysy. Takich jak zagrożenie, jakie niesie ze sobą pożar. Takich jak to,
że nie powinieneś był publikować na Facebooku tych zdjęć z imprezy.
Co gorsze, nie ma żadnego sposobu, aby powiedzieć mózgowi:
„Hej, mózgu mój, dziękuję ci bardzo, ale niezależnie od tego, jak nudna
jest tak książka i jak nieznaczne są emocje, których aktualnie doznaję,
to jednak naprawdę chcę zapamiętać wszystkie te informacje”.

jesteś tutaj  xxv


Jak korzystać z tej książki

Wyobrażamy sobie, że Czytelnik tej książki jest uczniem


trzeba to coś poznać,
A zatem czego trzeba, żeby się czegoś nauczyć? W pierwszej kolejności
o wtłoczenie do
a następnie postarać się tego czegoś nie zapomnieć. I nie chodzi tu jedynie
w dziedzin ie przyswa jania informacji,
głowy suchych faktów. Najnowsze badania prowadzone
ą, że uczenie się wymaga czegoś więcej niż tylko
neurobiologii i psychologii nauczania pokazuj
co potrafi pobudz ić nasze mózgi do działani a.
czytania tekstu. My wiemy,

Oto niektóre z głównych założeń niniejszej książki:


i sprawiają, że nauka
Zobrazuj to. Rysunki są znacznie łatwiejsze do zapamiętania niż same słowa
przypom inaniem sobie i przekazywaniem
staje się zdecydowanie bardziej efektywna (badania nad
rysunków poprawi a efektyw ność zapamię tywania o 89%).
informacji dowodzą, że wykorzystanie
ą, że informac je stają się znacznie bardziej zrozumia łe. Wystarc zy umieścić
Poza tym rysunki sprawiaj
, a nie na następne j
słowa bezpośrednio na lub w okolicach rysunku, do którego się odnoszą
w stanie rozwiąza ć problem , którego te słowa
stronie, a prawdopodobieństwo, że osoby uczące się będą
dotyczą, wzrośnie niemal dwukrotnie.
Stosuj konwersacyjny i spersonalizowany styl. Jak wynika z najnows
zych badań, w testach
końcowych studenci uzyskiwali wyniki o 40% lepsze, jeśli treść była przekazy wana w sposób
j osobie i w konwenc ji rozmow y, a nie w sposób formalny . Zamiast robić wykład
bezpośredni, w pierwsze
osoby zbyt poważni e. Kiedy byłbyś
opowiadaj historyjki. Używaj zwyczajnego języka. Nie traktuj swojej
bardziej uważny — podczas rozmowy przy obiedzie czy podczas wykładu ?
neuronów do aktywnego
Zmuś ucznia do głębszego zastanowienia. Innymi słowy: jeśli nie zmusisz
. Czytelnik musi być zmotyw owany, zaangażowany,
wysiłku, w Twojej głowie nie zdarzy się nic wielkiego
rozwiązy waniem problem ów, wyciągan iem wnioskó w i zdobywaniem
zaciekawiony i podekscytowany
wyzwań , zapraszanie do
nowej wiedzy. A osiągnięcie tego wszystkiego jest możliwe poprzez stawianie
zastanow ienia oraz poprzez nakłanianie
rozwiązywania ćwiczeń i zadawanie pytań zmuszających do
obu półkul mózgow ych i kilku zmysłów .
do działań, które wymagają zaangażowania
Zdobądź — i zatrzymaj na dłużej — uwagę Czytelnika. Każdy znalazł
się kiedyś w sytuacji, gdy
zasypiał po przeczyt aniu pierwsze j strony. Mózg zwraca uwagę na
bardzo chciał się czegoś nauczyć, lecz
ące, dziwne, przykuw ające wzrok, nieoczek iwane. Jednak poznawanie nowego
rzeczy niezwykłe, interesuj
nie interesuj ące, Twój mózg
zagadnienia technicznego wcale nie musi być nudne. Jeśli będzie to zagadnie
przyswoi je znacznie szybciej.
Wyzwól emocje. Teraz już wiemy, że zdolność zapamiętywania informac
ji jest w znacznej mierze
tujemy to, na czym nam zależy. Zapamiętujemy
zależna od ich zawartości emocjonalnej. Zapamię
my. Oczywiś cie nie mamy tu na myśli wzrusza jących historii
w sytuacjach, w których coś odczuwa
psie. Chodzi nam o emocje takie jak zaskocze nie, ciekawo ść, radosne podekscytowanie,
o chłopcu i jego
my, gdy znajdziem y rozwiązanie
„o rany…” i uczucie satysfakcji — „jestem wielki!” — jakie odczuwa
za trudne, lub zdamy sobie sprawę, że znamy
zagadki, nauczymy się czegoś, co powszechnie uchodzi
więcej szczegółów technicznych niż Zenek z działu inżynieri i.

xxvi Wprowadzenie
Wprowadzenie

Metapoznanie — myślenie o myśleniu


Jeśli naprawdę chcesz się czegoś nauczyć i jeśli chcesz się tego nauczyć szybciej i dokładniej,
to zwracaj uwagę na to, jak koncentrujesz uwagę. Myśl o tym, jak myślisz. Dowiedz się, Zastanawiam się,
jak przyswajasz wiedzę. jak zmusić mózg
do zapamiętania
Większość z nas w okresie dorastania nie uczestniczyła w zajęciach z metapoznania albo teorii tych informacji…
nauczania. Oczekiwano od nas, że będziemy się uczyć, ale nie uczono nas, jak mamy to robić.
Zakładamy jednak, że jeśli trzymasz w ręku tę książkę, to chcesz nauczyć się programować
aplikacje na Androida. I prawdopodobnie nie chcesz na to tracić zbyt wiele czasu. Jeśli chcesz
wykorzystać to, co przeczytałeś w tej książce, musisz to zapamiętać. A oprócz tego musisz to
zrozumieć. Aby w możliwie jak największym stopniu wykorzystać zarówno tę, jak i dowolną inną
książkę lub jakikolwiek inny sposób uczenia się, musisz wziąć odpowiedzialność za swój mózg.
Myśl o tym, czego się uczysz.
Sztuczka polega na tym, aby przekonać mózg, że poznawany materiał jest
Naprawdę Ważny. Kluczowy dla Twojego dobrego samopoczucia. Tak ważny
jak tygrys stojący naprzeciw Ciebie. W przeciwnym razie będziesz prowadzić
nieustającą wojnę z własnym mózgiem, który ze wszystkich sił będzie się starał,
aby nowe informacje nie zostały utrwalone.

A zatem jak ZMUSIĆ mózg, aby potraktował programowanie


jak głodnego tygrysa?
Można to zrobić w sposób powolny i męczący lub szybki i bardziej efektywny.
Powolny sposób polega na wielokrotnym powtarzaniu. Oczywiście wiesz, że jesteś w stanie
nauczyć się i zapamiętać nawet najnudniejsze zagadnienie, mozolnie je wkuwając. Po odpowiedniej
ilości powtórzeń Twój mózg stwierdzi: „Zdaje się, że to nie jest dla niego szczególnie ważne, lecz
w kółko to czyta i powtarza, więc przypuszczam, że jakąś wartość to jednak musi mieć”.
Szybszy sposób polega na zrobieniu czegoś, co zwiększy aktywność mózgu, a zwłaszcza jeśli
czynność ta wyzwoli kilka różnych typów aktywności. Wszystkie zagadnienia, o jakich pisaliśmy na
poprzedniej stronie, są kluczowymi elementami rozwiązania i udowodniono, że wszystkie potrafią
pomóc w zmuszeniu mózgu do tego, aby pracował na Twoją korzyść. Na przykład badania
dowodzą, że umieszczenie słów na opisywanych rysunkach (a nie w innych miejscach tekstu na
stronie, na przykład w nagłówku lub wewnątrz akapitu) sprawia, iż mózg stara się zrozumieć
relację pomiędzy słowami a rysunkiem, a to z kolei zwiększa aktywność neuronów. Większa
aktywność neuronów to większa szansa, że mózg uzna informacje za warte zainteresowania i,
ewentualnie, zapamiętania.
Prezentowanie informacji w formie konwersacji pomaga, ponieważ ludzie zdają się wykazywać
większe zainteresowanie w sytuacjach, gdy uważają, że biorą udział w rozmowie, bo oczekuje
się od nich, iż będą śledzić jej przebieg i brać w niej czynny udział. Zadziwiające jest to, iż
mózg zdaje się nie zważać na to, że rozmowa jest prowadzona z książką! Natomiast jeśli sposób
przedstawiania informacji jest formalny i suchy, mózg postrzega to tak samo jak w sytuacji,
gdy uczestniczysz w wykładzie na sali pełnej sennych studentów. Nie ma potrzeby wykazywania
jakiejkolwiek aktywności.
Ale rysunki i rozmowa to dopiero początek…

jesteś tutaj  xxvii


Jak korzystać z tej książki

Oto co MY zrobiliśmy
Użyliśmy rysunków, ponieważ Twój mózg zwraca większą uwagę na obrazy niż na tekst. Jeśli chodzi
o mózg, to faktycznie jeden obraz jest wart tysiąc słów. W sytuacjach, gdy mieliśmy do czynienia zarówno
z tekstem, jak i z rysunekiem, umieściliśmy tekst na rysunku, mózg bowiem działa bardziej efektywnie,
kiedy tekst jest na czymś, co opisuje, niż kiedy jest umieszczony w innym miejscu i stanowi część
większego fragmentu tekstu.

Zastosowaliśmy powtórzenia, wielokrotnie podając tę samą informację na różne sposoby i przy


wykorzystaniu różnych środków przekazu oraz odwołując się do różnych zmysłów. Wszystko to po to,
aby zwiększyć szansę, że informacja zostanie zakodowana w większej ilości obszarów Twojego mózgu.

Użyliśmy pomysłów i rysunków w nieoczekiwany sposób, ponieważ Twój mózg pragnie nowości, a poza
tym staraliśmy się zawrzeć w nich chociaż trochę emocji, gdyż mózg jest skonstruowany tak, że zwraca
uwagę na biochemię związaną z emocjami. Prawdopodobieństwo zapamiętania informacji jest większe,
jeśli sprawia ona, że coś czujemy, nawet jeśli to uczucie nie jest niczym więcej jak lekkim rozbawieniem,
zaskoczeniem lub zainteresowaniem.

Użyliśmy bezpośrednich zwrotów i przekazaliśmy treści w stylu konwersacyjnym, gdyż mózg zwraca
większą uwagę, jeśli sądzi, że prowadzisz rozmowę, niż wtedy, kiedy jesteś jedynie biernym słuchaczem
prezentacji. Mózg działa w ten sposób nawet wówczas, gdy czytasz zapis rozmowy.

Zamieściliśmy w książce wiele ćwiczeń, ponieważ mózg uczy się i pamięta więcej, gdy coś robi, niż gdy
o czymś czyta. Poza tym podane ćwiczenia stanowią wyzwania, choć nie są przesadnie trudne, bo właśnie
takie preferuje większość osób.

Zastosowaliśmy wiele stylów nauczania, gdyż Ty możesz preferować instrukcje opisujące krok po kroku
sposób postępowania, ktoś inny może woleć analizowanie zagadnienia opisanego ogólnie, a jeszcze inna
osoba może chcieć przejrzeć przykładowy fragment kodu. Niezależnie jednak od ulubionego sposobu
nauki każdy skorzysta na tym, że te same informacje będą przedstawiane kilkukrotnie na różne sposoby.

Podaliśmy informacje przeznaczone dla obu półkul Twojego mózgu, gdyż im bardziej mózg będzie
zaangażowany, tym większe będzie prawdopodobieństwo nauczenia się i zapamiętania podawanych
informacji i tym dłużej możesz koncentrować się na nauce. Ponieważ angażowanie jednej półkuli mózgu
często oznacza, że druga może odpocząć, zatem będziesz mógł uczyć się bardziej produktywnie przez
dłuższy okres czasu.

Dodatkowo zamieściliśmy opowiadania i ćwiczenia prezentujące więcej niż jeden punkt widzenia,
ponieważ mózg uczy się łatwiej, gdy jest zmuszony do przetwarzania i podawania własnej opinii.

Postawiliśmy przed Tobą wyzwania, zarówno poprzez podawanie ćwiczeń, jak i stawianie pytań, na które
nie zawsze można odpowiedzieć w prosty sposób, a to dlatego, że mózg uczy się i pamięta, gdy musi
popracować nad czymś. Pomyśl sam: nie można zdobyć dobrej kondycji, obserwując ćwiczenia w telewizji.
Ale dołożyliśmy wszelkich starań, aby zapewnić, że gdy pracujesz, to robisz dokładnie to, co trzeba.
Aby ani jeden dendryt nie musiał przetwarzać trudnego przykładu ani analizować tekstu zbyt lapidarnego
lub napisanego niezrozumiałym żargonem.

Przedstawiliśmy ludzi — w opowiadaniach, w przykładach, na rysunkach itd. — bo Ty też jesteś


człowiekiem. Dlatego Twój mózg zwraca bardziej uwagę na ludzi niż na rzeczy.
xxviii Wprowadzenie
Wprowadzenie

Oto, co możesz zrobić TY,


aby zmusić swój mózg do posłuszeństwa
Wytnij
te porady A zatem zrobiliśmy, co było w naszej mocy. Reszta zależy od Ciebie. Możesz zacząć
i przyklej
na lodówce. od poniższych porad. Posłuchaj swojego mózgu i określ, które sprawdzają się w Twoim
przypadku, a które nie dają pozytywnych rezultatów. Próbuj nowych rzeczy.

1 Zwolnij — im więcej rozumiesz, tym mniej musisz 6 Pij wodę, dużo wody
zapamiętać Mózg pracuje najlepiej, gdy dostarcza mu się dużo płynów.
Nie ograniczaj się jedynie do czytania. Przerwij na chwilę Odwodnienie (do którego może dojść nawet, zanim
lekturę i pomyśl. Kiedy znajdziesz w tekście pytanie, nie poczujesz pragnienie) obniża zdolność percepcji.
zaglądaj od razu na stronę z odpowiedzią. Wyobraź sobie,
że ktoś faktycznie zadaje Ci pytanie. Im bardziej zmusisz 7 Posłuchaj swojego mózgu
swój mózg do myślenia, tym większa będzie szansa, że się Uważaj, kiedy Twój mózg staje się przeciążony. Jeśli
czegoś nauczysz i coś zapamiętasz. spostrzeżesz, że zaczynasz czytać pobieżnie i zapominać,
o czym przeczytałeś przed chwilą, to najwyższy czas, żeby
2 Rób ćwiczenia, notuj sobie zrobić przerwę. Po przekroczeniu pewnego punktu
Zamieściliśmy je w książce, ale gdybyśmy zrobili je nie będziesz się uczył szybciej, „wciskając” do głowy
za Ciebie, to niczym nie różniłoby się to od sytuacji, więcej informacji, co gorsze, może to zaszkodzić całemu
w której ktoś za Ciebie wykonywałby ćwiczenia fizyczne. procesowi nauki.
I nie ograniczaj się jedynie do czytania ćwiczeń. Używaj
ołówka. Można znaleźć wiele dowodów na to, że fizyczna 8 Poczuj coś!
aktywność podczas nauki może poprawić jej wyniki. Twój mózg musi wiedzieć, że to, czego się uczysz, jest
ważne. Z zaangażowaniem śledź zamieszczane w tekście
3 Czytaj fragmenty oznaczone jako „Nie istnieją opowiadania. Nadawaj własne tytuły zdjęciom. Zalewanie
głupie pytania” się łzami ze śmiechu po przeczytaniu głupiego dowcipu
Chodzi tu o wszystkie fragmenty umieszczone z boku i tak jest lepsze od braku jakiejkolwiek reakcji.
tekstu. Nie są to fragmenty opcjonalne — stanowią one
część podstawowego tekstu książki! Nie pomijaj ich. 9 Pisz jak najwięcej kodu
Istnieje tylko jeden sposób, by nauczyć się programowania
4 Niech lektura tej książki będzie ostatnią rzeczą, jaką aplikacji na Androida: pisanie kodu, a im będzie go
robisz przed pójściem spać — a przynajmniej ostatnią więcej, tym lepiej. I właśnie to będziesz robić podczas
rzeczą stanowiącą wyzwanie intelektualne lektury tej książki. Pisanie programów jest umiejętnością,
Pewne elementy procesu uczenia się (a w szczególności a jedynym sposobem jej nabycia jest ciągła praktyka.
przenoszenie informacji do pamięci długoterminowej) Mamy zamiar dać Ci wiele okazji do tego: w każdym
następują po odłożeniu książki. Mózg potrzebuje trochę rozdziale zamieściliśmy ćwiczenia stawiające przed Tobą
czasu dla siebie i musi dodatkowo przetworzyć dostarczone problemy, które możesz rozwiązać. Nie pomijaj ich —
informacje. Jeśli podczas tego koniecznego na wykonanie podczas rozwiązywania ćwiczeń możesz się bardzo wiele
dodatkowego przetwarzania czasu zmusisz go do innej nauczyć. Zamieściliśmy także rozwiązania wszystkich
działalności, to część z przyswojonych informacji może ćwiczeń — śmiało zerknij na rozwiązanie, jeśli utkniesz!
zostać utracona. (Łatwo jest utknąć na jakiejś drobnostce). Staraj się
jednak rozwiązać problem, zanim zajrzysz do rozwiązania.
5 Mów o zdobywanych informacjach — głośno I koniecznie, zanim przejdziesz do dalszej części książki,
Mówienie aktywuje odmienne obszary mózgu. Jeśli postaraj się uruchomić programy, nad którymi pracujesz.
próbujesz coś zrozumieć lub zwiększyć szansę na
zapamiętanie informacji na dłużej, powtarzaj to na głos.
Jeszcze lepiej — staraj się to na głos komuś wytłumaczyć.
W ten sposób nauczysz się szybciej, a oprócz tego będziesz
mógł odkryć kwestie, o których nie wiedziałeś podczas
czytania tekstu książki. jesteś tutaj  xxix
Jak korzystać z tej książki

Przeczytaj to
Ta książka jest doznaniem poznawczym, a nie podręcznikiem. Celowo usunęliśmy z niej
wszystko, co mogłoby Ci przeszkadzać w uczeniu się i poznawaniu materiału zamieszczonego
w danym miejscu książki. W przypadku pierwszej lektury tej książki należy zacząć od samego
początku, gdyż jej dalsze fragmenty bazują na wiedzy, którą musisz zdobyć wcześniej.

Zakładamy, że nowością jest dla Ciebie Android, ale nie Java

Aplikacje na Androida będziemy pisać, używając kombinacji kodu pisanego w Javie


i kodu XML. Zakładamy, że miałeś już wcześniej kontakt z językiem Java. Jeśli jeszcze
nie napisałeś w ogóle żadnego programu w Javie, to prawdopodobnie powinieneś sięgnąć
po książkę Java. Rusz głową! Wydanie II.

Zaczynamy od napisania aplikacji w rozdziale 1.

Możesz nam wierzyć lub nie, ale nawet jeśli nigdy wcześniej nie zdarzyło Ci się napisać
żadnej aplikacji na Androida, to możesz wskoczyć na głęboką wodę i zacząć pisać własne
aplikacje. Przy okazji poznasz trochę Android Studio — oficjalne IDE do pisania aplikacji
na Androida.

Przykłady zostały zaprojektowane pod kątem nauki

Podczas lektury niniejszej książki napiszesz kilka różnych aplikacji. Niektóre z nich są
bardzo małe, co pozwoli Ci skoncentrować się na konkretnym aspekcie programowania na
Androida. Inne z kolei są większe, dzięki czemu przekonasz się, jak ich różne komponenty
pasują do siebie i ze sobą współpracują. W książce nie dokończymy wszystkich fragmentów
każdej z tych aplikacji, możesz to jednak zrobić samodzielnie. To wszystko zalicza się do
doznania poznawczego. Kody źródłowe aplikacji przedstawionych w tej książce można
pobrać z serwera FTP wydawnictwa Helion: ftp://ftp.helion.pl/przyklady/andrrg.zip

Aktywności NIE są opcjonalne

Ćwiczenia i aktywności nie są jedynie dodatkami, są one elementem treści tej książki.
Niektóre z nich mają wspomóc Twoją pamięć, inne — ułatwić Ci zrozumienie, a jeszcze inne
— pomóc w wykorzystaniu nabytej wiedzy i umiejętności. Nie pomijaj tych ćwiczeń!

xxx Wprowadzenie
Wprowadzenie

Powtórzenia są celowe i ważne

Jedną z cech, która wyróżnia książki z serii Rusz głową!, jest to, że nam naprawdę zależy, żebyś
wszystko zrozumiał. Chcemy także, byś kończąc lekturę tej książki, pamiętał wszystko, co w niej
przeczytałeś. W przypadku większości książek informacyjnych i encyklopedycznych przyswojenie
i zapamiętanie informacji nie jest celem, ale w tej książce chodzi o naukę, dlatego znajdziesz
w niej wiele pojęć, które pojawiają się kilka razy.

Do ćwiczeń z cyklu „Wysil szare komórki” nie podaliśmy odpowiedzi

Do niektórych ćwiczeń w ogóle nie można podać jednej dobrej odpowiedzi, w innych przypadkach
to doświadczenie, które zdobywasz, rozwiązując te ćwiczenia, ma dać Ci możliwość określenia, czy
i kiedy podana odpowiedź będzie poprawna. W niektórych ćwiczeniach z tej serii znajdziesz także
podpowiedzi, które ułatwią Ci znalezienie rozwiązania.

jesteś tutaj  xxxi


Zespół recenzentów

Zespół recenzentów technicznych

Edward

Recenzenci techniczni książki

Edward Yue Shung Wong pasjonował się programowaniem od czasu, gdy w 2006
roku napisał pierwszy wiersz kodu w języku Haskell. Obecnie pracuje w sercu
londyńskiego City nad sterowanym zdarzeniami przetwarzaniem danych handlowych.
Edward z radością dzieli się swoją pasją programistyczną z londyńską społecznością
programistów Javy w ramach London Java Community i Software Craftsmanship
Community. Jeśli akurat przebywa z dala od klawiatury, to można go znaleźć na
boisku do piłki nożnej, gdzie czuje się w swoim żywiole, lub obejrzeć na YouTubie
(@arkangelofkaos), jak gra w gry komputerowe.

Tony Williams jest programistą używającym Javy i piszącym aplikacje na Androida.

xxxii Wprowadzenie
Wprowadzenie

Podziękowania
Dla naszej redaktorki

Chcieliśmy bardzo podziękować naszej redaktorce, Meghan Blanchette,


za pracę nad serią Head First. Jej opinie i spostrzeżenia były bezcenne.
Bardzo doceniamy te wszystkie sytuacje, w których zwracała nam uwagę,
że choć pisane przez nas słowa mają odpowiednie litery, to jednak są Meghan Blanchette
zapisane w niewłaściwej kolejności.

Bardzo dziękujemy także Bertowi Batesowi za nauczenie nas, jak odrzucić


stary zestaw reguł, oraz za wpuszczenie nas do swojego mózgu. Ta książka
stała się nieporównanie lepsza dzięki jego reakcjom i opiniom.

Dla zespołu O’Reilly

Wielkie podziękowania składamy Mike’owi Hendricksonowi za wiarę, jaką


w nas pokładał, i za wybranie nas do napisania tej książki; Courtney Nash
za pomoc na początkowych etapach pracy nad tą książką oraz zespołowi
wstępnego wydania za pracę nad udostępnieniem wstępnej wersji niniejszej
książki. I w końcu chcieliśmy także podziękować Melanie Yarbrough
i Jasmine Kwityn oraz całej reszcie zespołu produkcyjnego za doskonałe
kierowanie pracami nad książką w tracie całego procesu wydawniczego
oraz za wspaniałą i ciężką pracę za kulisami.

Dla rodziny, przyjaciół i kolegów

Pisanie książki z serii Head First jest jak jazda kolejką górską, a prace nad
tą akurat książką nie były pod tym względem wyjątkiem. Ta książka w ogóle
nie ujrzałaby światła dziennego, gdyby nie uprzejmość i wsparcie naszych
przyjaciół i rodziny. Specjalne podziękowania przesyłamy: Andy’emu P,
Steve’owi, Jacqui, Angeli, Paulowi B, Mamie, Tacie, Carlowi, Robowi
i Lorraine.

Dla pozostałych osób

Nasz zespół recenzentów technicznych wykonał doskonałą robotę, utrzymując


nas w ryzach i zapewniając, żeby zagadnienia będące przedmiotem tej książki
były opisane dokładnie i prawidłowo. Jesteśmy także niezmiernie wdzięczni
wszystkim osobom, które przekazywały nam swoje uwagi po ukazaniu się
pierwszych wydań niniejszej książki. Uważamy, że dzięki Wam ta książka
stała się dużo, dużo lepsza.

I w końcu chcieliśmy podziękować Kathy Sierze i Bertowi Batesowi


za stworzenie tej niesamowitej serii książek.

jesteś tutaj  xxxiii


xxxiv Wprowadzenie
1. Zaczynamy

Skok na głęboką wodę

Android błyskawicznie podbił świat.


Każdy chce mieć smartfon lub tablet, a urządzenia z Androidem są niezwykle popularne.
W tej książce nauczymy Cię, jak pisać własne aplikacje, a zaczniemy od pokazania procesu
przygotowania bardzo prostej aplikacji i uruchomienia jej na wirtualnym urządzeniu z Androidem.
W trakcie tych prac poznasz także kilka podstawowych komponentów wszystkich aplikacji
na Androida, takich jak aktywności i układy. Jedyną rzeczą, której będziesz do tego
potrzebować, jest znajomość Javy, choć wcale nie musisz być w niej mistrzem…

to jest nowy rozdział  1


Android — przegląd

Witamy w Androidowie Nasze aplikacje na Androida


będziemy tworzyć, używając
Android jest najpopularniejszą na świecie mobilną platformą
połączenia Javy i XML-a.
systemową. Według ostatnich szacunków na świecie jest obecnie
używanych ponad miliard urządzeń z tym systemem, przy czym ich W trakcie prac wszystko
liczba błyskawicznie rośnie. dokładnie wyjaśnimy, jednak
Android jest kompleksową platformą o otwartym kodzie źródłowym, aby w pełni skorzystać z tej
bazującą na Linuksie i wspieraną przez Google’a. Stanowi on potężną
książki, musisz dysponować
platformę programistyczną, zawierającą absolutnie wszystko, czego
potrzeba do tworzenia wspaniałych aplikacji z użyciem języków Java w miarę dobrą znajomością
i XML. Co więcej, Android pozwala na rozpowszechnianie tych języka Java.
aplikacji i uruchamianie ich na bardzo wielu różnych urządzeniach
— telefonach, tabletach itd. jak mają
Układy informują,
ególne
wyglądać poszcz
A zatem z czego składa się typowa aplikacja na Androida? ekra ny ap lik ac ji.

Układy określają postać poszczególnych ekranów


Typowa aplikacja na Androida składa się z jednego lub kilku ekranów.
Postać każdego z tych ekranów definiowana jest przy użyciu układu.
Te układy zazwyczaj są tworzone w języku XML i mogą zawierać
komponenty graficznego interfejsu użytkownika (w skrócie GUI,
od angielskich słów graphical user interface), takie jak przyciski,
pola tekstowe czy etykiety.

Kod napisany w Javie definiuje, co robi aplikacja


Jednak układy określają jedynie wygląd aplikacji. To, co aplikacja
będzie robić, określamy natomiast, pisząc kod w języku Java. Specjalne
klasy Javy, nazywane aktywnościami, określają, które układy należy
wybierać oraz jak aplikacja ma reagować na czynności wykonywane
przez użytkownika. Na przykład jeśli układ zawiera przycisk,
Aktywności
to w aktywności musimy napisać kod, który określi, co aplikacja definiują,
ma zrobić po naciśnięciu tego przycisku. co aplikacja
ma robić.

Czasami konieczne są także dodatkowe zasoby


Oprócz kodu Javy oraz układów aplikacje na Androida często będą
potrzebowały także zasobów dodatkowych, takich jak obrazki lub dane.
A zatem do tworzonych aplikacji można dołączać takie dodatkowe pliki. Zasoby mogą
zawierać na
Innymi słowy: aplikacje na Androida są w zasadzie grupami plików przykład pliki
umieszczonych w odpowiednich katalogach. Podczas budowania dźwiękowe
i graficzne.
aplikacji wszystkie te pliki zostają ze sobą połączone i wspólnie
tworzą aplikację, którą można uruchomić na urządzeniu.

2 Rozdział 1.
Zaczynamy

Platforma Android w szczegółach Nie przejmuj się, jeśli masz


wrażenie, że to bardzo dużo
Spokojnie jak na początek.
Platforma Android składa się z kilku różnych komponentów.
Tworzą ją standardowe aplikacje, takie jak Kontakty, interfejsy Pokazujemy Ci tutaj poglądowy
programistyczne, API, pomagające nam w kontrolowaniu wyglądu schemat wszystkiego, co składa się na platformę
i działania aplikacji, jak również bardzo wiele dodatkowych plików systemu Android. Jego poszczególne komponenty
i bibliotek. Poniższy schemat pokazuje, w jaki sposób tworzą one będziemy opisywać bardziej szczegółowo w dalszej
jedną spójną platformę systemową. części książki, kiedy pojawi się taka konieczność.

Android jest wyposażony


w zestaw podstawowych Aplikacje
aplikacji, takich jak
Kontakty, Kalendarz i Mapy, Aparat Kontakty Telefon Internet ...
oraz w przeglądarkę WWW.
Pisząc własne aplikacje, Framework aplikacji
mamy dostęp do tych
samych podstawowych Menedżer Menedżer Dostawcy System
API, które są używane aktywności okien treści widoków
przez podstawowe Środowisko
aplikacje. Używając tych Menedżer Menedżer Menedżer Menedżer Menedżer uruchomieniowe
API, kontrolujemy wygląd pakietów telefonii zasobów lokalizacji powiadomień Androida jest
aplikacji i jej działanie. wyposażone
Biblioteki w zestaw
Środowisko wykonawcze podstawowych
Poniżej frameworku
Surface Media Android bibliotek,
SQLite
Manager Framework Podstawowe implementujących
aplikacji ukryty jest
biblioteki przeważającą
zestaw bibliotek C i C++.
OpenGL | ES FreeType WebKit część języka
Dostęp do nich zapewniają
programowania Java.
API należące do
Każda aplikacja
frameworku aplikacji.
SGL SSL libc na Androida jest
wykonywana we
Poniżej wszystkich
własnym procesie.
pozostałych warstw
platformy Android Jądro systemu Linux
umieszczone jest jądro
Sterownik
systemu Linux. To ono Sterownik ekranu Sterownik aparatu Binder (IPC)
pamięci flash
odpowiada za obsługę
sterowników oraz
Sterownik Sterownik Sterownik Zarządzanie
podstawowych usług, klawiszy WiFi audio energią
takich jak zabezpieczenia
i zarządzanie pamięcią.

Na szczęście okazuje się, że te wszystkie potężne biblioteki Androida są udostępniane


przez interfejsy programowania aplikacji — API — wchodzące w skład frameworku
do tworzenia aplikacji i to właśnie z tych API będziemy korzystać, pisząc własne
programy. Zatem wszystkim, czego potrzebujesz, by zacząć, jest znajomość języka
programowania Java i pomysł na wspaniałą aplikację.

jesteś tutaj  3
Czynności

Oto, co mamy zamiar zrobić


A zatem, bez niepotrzebnego przeciągania, spróbujmy utworzyć
pierwszą, prostą aplikację na Androida. W tym celu musimy
wykonać kilka czynności:

1 Przygotować środowisko programistyczne.


W pierwszej kolejności musimy zainstalować Android
Studio — środowisko programistyczne zawierające
wszystkie narzędzia niezbędne do pisania aplikacji
na Androida.

2 Napisać i zbudować prostą aplikację.


Używając Android Studio, przygotujemy prostą
aplikację, która wyświetla na ekranie jakiś tekst.

3 Uruchomić aplikację w emulatorze.


Skorzystamy z wbudowanego emulatora,
by uruchomić aplikację i przekonać się, jak działa.

4 Zmodyfikować aplikację.
I na koniec wprowadzimy kilka zmian w aplikacji
stworzonej w kroku 2.

Nie istnieją
głupie pytania
P: Czy wszystkie aplikacja na P: W jakim stopniu muszę znać P: Czy muszę znać biblioteki Swing
Androida są pisane w Javie? Javę, by pisać aplikacje na Androida? i AWT?

O: Aplikacje na Androida można pisać O: Powinieneś dobrze znać Javę SE. Jeśli O: Android nie używa ani biblioteki
także w innych językach, lecz prawda nie czujesz się pewnie, to radzimy sięgnąć Swing, ani biblioteki AWT, dlatego nie
jest taka, że przeważająca większość najpierw po książkę Java. Rusz głową! przejmuj się, jeśli nie masz doświadczeń
programistów używa Javy. napisaną przez Kathy Sierrę i Berta Batesa. w pisaniu w Javie aplikacji o graficznym
interfejsie użytkownika.

4 Rozdział 1.
Zaczynamy
Jesteś tutaj.

¨
Środowisko programistyczne Przygotowanie środowiska
¨ Stworzenie aplikacji
¨ Uruchomienie aplikacji
Java jest najpopularniejszym językiem używanym do pisania aplikacji na Androida. ¨ Modyfikacja aplikacji
Jednak urządzenia działające pod tym systemem nie wykonują plików .class ani .jar.
Zamiast tego, aby poprawić szybkość i wydajność działania, urządzenia te korzystają
z własnego, zoptymalizowanego formatu zapisu skompilowanego kodu. A to oznacza,
że do pisania aplikacji na Androida nie można używać zwyczajnych środowisk do
pisania kodu w Javie — konieczne są bowiem specjalne narzędzia do konwersji
skompilowanego kodu do formatu używanego przez platformę Android, do
zainstalowania ich na urządzeniu oraz debugowania kodu po uruchomieniu aplikacji.

Wszystkie te narzędzia wchodzą w skład Android SDK. Spójrzmy zatem, co on zawiera.

Android SDK
Android SDK (Software Development Kit) to zestaw narzędzi i bibliotek niezbędnych
do pisania aplikacji na Androida. W jego skład wchodzą:

Platforma SDK
Istnieje jedna taka platforma Dokumentacja
dla każdej wersji Androida. By móc korzystać
z najnowszej dokumentacji
bez połączenia z internetem.
Narzędzia SDK
Narzędzia do debugowania Wsparcie dla Androida
i testowania oraz inne przydatne Dodatkowe API, które nie
programy.
SDK wchodzą w skład standardowej
id
ro
platformy.
d
An
Aplikacje przykładowe Płatności Google Play
Jeśli do zrozumienia niektórych API Pozwalają na zintegrowanie
potrzebujesz kodu praktycznych z aplikacją mechanizmów
To tylko kilka do rozliczeń finansowych.
przykładów, to mogą Ci w tym najważniejszych
pomóc aplikacje przykładowe. elementów.

Android Studio to specjalna wersja IntelliJ IDEA


IntelliJ IDEA jest jednym z najpopularniejszych zintegrowanych środowisk
programistycznych (w skrócie: IDE) do pisania programów w Javie. Android Studio
jest wersją tego IDE, która zawiera Android SDK i dodatkowe narzędzia graficzne
wspomagające tworzenie aplikacji na Androida.

Oprócz edytora oraz dostępu do narzędzi i bibliotek wchodzących w skład Android


SDK Android Studio udostępnia także szablony, które mogą bardzo pomóc w tworzeniu
nowych aplikacji i klas i znacząco ułatwiają wykonywanie takich czynności jak pakowanie
i uruchamianie aplikacji.
jesteś tutaj  5
Instalacja

¨
Zainstaluj Javę Przygotowanie środowiska
¨ Stworzenie aplikacji
¨ Uruchomienie aplikacji
Android Studio jest środowiskiem programistycznym służącym do pisania ¨ Modyfikacja aplikacji
aplikacji w języku Java, musisz się zatem upewnić, czy na swoim komputerze masz
zainstalowaną odpowiednią wersję Javy.

Zacznij od sprawdzenia wymagań systemowych Android Studio, aby dowiedzieć


się, której wersji Java Development Kit (JDK) i Java Runtime Environment (JRE)
będziesz potrzebować. Informacje o tych wymaganiach można znaleźć na stronie:
witrynach firm u
Adresy stron w
og le od czasu do czas
http://developer.android.com/sdk/index.html#Requirements Oracle i Go śli zatem te
się zmieniają. Je
prawidłowo, to
Kiedy już się dowiesz, jakich wersji JDK i JRE będziesz potrzebować, nie będą działać trochę poszukać.
będziesz musiał
pobierz je ze strony:

http://www.oracle.com/technetwork/java/javase/downloads/index.html

Jeśli ten adres URL ulegnie


Następnie zainstaluj Android Studio zmianie, to poszukaj hasła
„Android Studio” w witrynie
Po pobraniu i zainstalowaniu Javy musisz pobrać Android Studio. developer.android.com.
Znajdziesz je na tej stronie:

https://developer.android.com/sdk/installing/index.html?pkg=studio
W tej książce nie zamieszczamy instrukcji
Na tej samej stronie zostały także opublikowane informacje dotyczące instalacji instalacji Android Studio, gdyż mogłoby
Android Studio. Postępuj zgodnie z nimi, aby zainstalować IDE na swoim się okazać, że bardzo szybko staną się one
nieaktualne. Postępuj zgodnie z opisem
komputerze. Kiedy to już zrobisz, uruchom Android Studio i postępuj zgodnie instalacji zamieszczonym na stronie,
z instrukcjami, by zainstalować najnowsze narzędzia SDK i biblioteki. a na pewno bez problemu się uda.

Kiedy wszystko będzie już gotowe, zobaczysz ekran powitalny Android Studio.
Teraz będziesz już gotów, by rozpocząć tworzenie swojej pierwszej aplikacji
na Androida.

To jest ekran
powitalny
Android Studio.
Prezentuje on
zestaw opcji
odpowiadający
czynnościom,
jakie można
wykonać.

6 Rozdział 1.
Zaczynamy

Nie istnieją
głupie pytania

P: Napisaliście, że do pisania aplikacji P: A czy można pisać aplikacje na P: Większość aplikacji jest
na Androida będziemy używać Android Android bez używania żadnego IDE? budowanych przy użyciu gradle?
Studio. Czy to konieczne? Napisaliście chyba wcześniej,
O: Tak, można, lecz wymaga to znacznie że bardzo wielu programistów
O: Precyzyjnie rzecz ujmując, Android więcej pracy. Większość aplikacji na używa Android Studio?
Studio nie jest niezbędne do pisania aplikacji Androida jest obecnie przygotowywanych
na Androida. Będziesz potrzebować przy użyciu programu do budowania O: Android Studio udostępnia graficzny
dowolnego narzędzia umożliwiającego o nazwie gradle. Projekty gradle można interfejs do obsługi gradle, jak również wiele
pisanie i kompilację kodu Javy oraz paru tworzyć i budować, używając dowolnego innych narzędzi służących do tworzenia
innych specjalistycznych narzędzi służących edytora tekstów i wiersza poleceń. układów, odczytu dzienników oraz
do konwersji skompilowanego kodu do debugowania.
postaci, w której urządzenia z Androidem P: Narzędzie do budowania?
będą go w stanie wykonywać. Czy gradle to coś takiego jak ANT?

P: Czy zatem mogę używać mojego O: Owszem, lecz gradle ma nieporównanie


ulubionego IDE? większe możliwości. Gradle, podobnie jak
ANT, potrafi kompilować i wdrażać kod,
O: Android Studio jest oficjalnym IDE ale oprócz tego korzysta także z Maven do
służącym do pisania aplikacji na Androida, pobierania wszystkich dodatkowych bibliotek
jednak popularne jest także Eclipse. Więcej używanych w kodzie. Gradle używa Groovy
informacji na ten temat można znaleźć jako języka skryptowego, co oznacza,
na stronie https://developer.android.com/ że całkiem łatwo można tworzyć bardzo
tools/sdk/eclipse-adt.html. złożone skrypty do budowy projektów.

Stwórzmy prostą aplikację


Skoro już przygotowałeś własne środowisko programistyczne,
jesteś gotowy do stworzenia pierwszej aplikacji na Androida.
Oto, jak ona będzie wyglądać:

Ta aplikacja jest
To jest nazwa bardzo prosta, ale jak
aplikacji. na Twoją pierwszą
aplikację na Androida
w zupełności
wystarczy.

To jest przykładowy
napis, który Android
Studio samo dodało
do aplikacji.

jesteś tutaj  7
Utworzenie projektu Ten etap prac już zakończyłeś,
więc oznaczyliśmy go ptaszkiem.

Stwórzmy prostą aplikację ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Tworząc nową aplikację, zawsze trzeba utworzyć dla niej nowy projekt.
¨ Modyfikacja aplikacji
Upewnij się, czy Android Studio jest uruchomione, a następnie
wykonaj następujące czynności:

1. Utworzenie nowego projektu


Ekran powitalny Android Studio udostępnia kilka opcji pozwalających
na wykonywanie różnych czynności. Ponieważ chcemy utworzyć nowy
projekt, kliknij opcję Start a new Android Studio project.

Kliknij tę opcję, aby


rozpocząć tworzenie
nowego projektu
Android Studio.

Tu będą wyświetlane wszystkie


projekty, które stworzyłeś
już wcześniej. Ponieważ ten
projekt jest pierwszy, lista
jest jeszcze pusta.

8 Rozdział 1.
Zaczynamy

Tworzenie aplikacji (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
2. Konfiguracja projektu
Teraz musisz skonfigurować aplikację i podać jej nazwę, nazwę
firmowej domeny oraz lokalizację plików aplikacji na dysku.
Nazwa pakietu już
Android Studio używa nazwy firmowej domeny i nazwy aplikacji do na zawsze zostanie
wygenerowania nazwy pakietu aplikacji. Na przykład jeśli nadasz Obejrzyj to! taka sama.
aplikacji nazwę Moja pierwsza apka, a nazwą firmowej domeny będzie
hfad.com, to nazwą pakietu użytą przez Android Studio będzie Stanowi ona unikalny identyfikator
com.hfad.mojapierwszaapka. aplikacji i jest używana do
zarządzania jej wieloma wersjami.
Wpisz zatem Moja pierwsza Apka jako nazwę aplikacji (w polu
Application name) i hfad.com jako nazwę domeny (w polu Company
Domain), a potem zaakceptuj sugerowaną lokalizację projektu.
Następnie kliknij przycisk Next.

Nazwa aplikacji jest wyświetlana


w sklepie Google Play i w wielu
innych miejscach.

Kreator utworzył
nazwę pakietu na
podstawie nazwy
aplikacji i nazwy
firmowej domeny.

przechowywane w tym katalogu.


Wszystkie pliki tego projektu będą

jesteś tutaj  9
Poziom API

Tworzenie aplikacji (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
3. Określenie poziomu API ¨ Modyfikacja aplikacji

Teraz musisz określić, którego poziomu API platformy Android będzie używać
tworzona aplikacja. Numer poziomu API rośnie wraz z każdą nową wersją
Androida. Jeśli nie chcesz, by aplikacja działała wyłącznie na najnowszych
urządzeniach, to wybierz jeden z wcześniejszych API.
Na następnej stronie znajdziesz .
W tym przykładzie wybierzemy API poziomu 15, co oznacza, że aplikacja będzie więcej informacji o poziomach API
w stanie działać na przeważającej większości urządzeń. Co więcej, utworzymy
wyłącznie aplikację w wersji przeznaczonej na telefony i tablety, dlatego nie
zaznaczaj żadnych innych pól wyboru widocznych w tym oknie kreatora.

Kiedy już wybierzesz odpowiednie opcje, kliknij przycisk Next.

Minimalna wymagana
wersja SDK to najniższa
wersja obsługiwana
przez aplikację. Twoja
aplikacja będzie działać
na urządzeniach z API
wybranego lub wyższego
poziomu, natomiast na
urządzeniach z API
niższego poziomu nie uda
się uruchomić aplikacji.

10 Rozdział 1.
Zaczynamy

Wersje Androida pod lupą


Prawdopodobnie słyszałeś sporo słodkich rzeczy w kontekście Androida. Chodzi o takie terminy
jak Ice Cream Sandwich, Jelly Bean, KitKat czy też Lollipop1. O co chodzi z tymi słodyczami?

Wszystkie wersje Androida mają swój numer i nazwę kodową. Numer wersji dokładnie określa numer
platformy Android (na przykład 5.0), natomiast nazwa kodowa to nieco bardziej „przyjazna” nazwa, która może
obejmować kilka kolejnych wersji Androida (tak jest na przykład w przypadku wersji Jelly Bean). Poziom API
odnosi się do wersji API używanych przez aplikacje. Na przykład Androidowi 5.0 odpowiada poziom API 21.

Version Codename API level


1.0 1
1.1 2
1.5 Cupcake 3
1.6 Donut 4 Niemal nikt już nie
używa tych wersji
2.0 Eclair 5 API.
2.01 Eclair 6
2.1 Eclair 7
2.2.x Froyo 8
2.3 - 2.3.2 Gingerbread 9
2.3.2 - 2.3.7 Gingerbread 10
3.0 Honeycomb 11
3.1 Honeycomb 12
3.2 Honeycomb 13
4.0 - 4.0.2 Ice Cream Sandwich 14
4.0.3-4.0.4 Ice Cream Sandwich 15
4.1 Jelly Bean 16
4.2 Jelly Bean 17
4.3 Jelly Bean 18 Większość urządzeń
używa obecnie tych
4.4 KitKat 19 wersji API.
4.4 KitKat (z rozszerzeniem dla 20
urządzeń ubieralnych)
5.0 Lollipop 21

Pisząc aplikacje na Andorida, naprawdę trzeba poważnie zastanowić się nad tym, z jakimi wersjami systemu
nasza aplikacja ma być zgodna. Jeśli określimy, że będzie ona zgodna tylko z najnowszą wersją SDK, to może się
okazać, że nie będzie w stanie działać na wielu urządzeniach. Udziały procentowe urządzeń z poszczególnymi
wersjami Androida można znaleźć na stronie https://developer.android.com/about/dashboards/index.html.

1
Wszystkie te nazwy odpowiadają nazwom słodyczy: ice cream sandwich to lodowa kanapka, jelly bean to żelki w kształcie
fasolek, a lollipop to lizak — przyp. tłum.

jesteś tutaj  11
15 000 metrów

Aktywności z wysokości 15 tysięcy metrów ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Kolejną rzeczą, którą musisz zrobić, jest dodanie do projektu aktywności.
¨ Modyfikacja aplikacji
Każda aplikacja na Androida jest zestawem ekranów, a każdy z ekranów składa się
z aktywności i układu.

Aktywność (ang. activity) jest pojedynczą, precyzyjnie zdefiniowaną czynnością, Układy definiują,
którą użytkownik może wykonywać. Można zatem utworzyć aktywność pozwalającą
na redagowanie e-maila, robienie zdjęcia czy też odnajdywanie kontaktu. Aktywności
jak będzie
są zazwyczaj skojarzone z jednym ekranem, a ich kod jest pisany w Javie. wyglądał interfejs
Układ (ang. layout) opisuje wygląd ekranu. Układy są tworzone w formie zwyczajnych
plików XML i informują system o tym, w jaki sposób na ekranie są rozmieszane
użytkownika.
poszczególne elementy interfejsu użytkownika.

Przyjrzyjmy się nieco dokładniej, jak aktywności i układy współpracują ze sobą, Aktywności
by utworzyć interfejs użytkownika aplikacji:
definiują akcje.
1 Urządzenie uruchamia
1
aplikację i tworzy obiekt
aktywności.
<Układ>
2
2 Obiekt aktywności
określa układ. </Układ>
<Układ>
Aktywność
3 Aktywność nakazuje
Urządzenie </Układ> Układ
urządzeniu wyświetlenie 3
układu na ekranie.

4 Użytkownik prowadzi
interakcje z układem
wyświetlonym na ekranie.

5 Aktywność odpowiada na
te interakcje, wykonując 4
odpowiedni kod aplikacji. 5

6 Aktywność aktualizuje
treści w układzie…

7 …które użytkownik widzi 6 Aktywność


na ekranie. 7 Urządzenie
Użytkownik

Skoro już wiesz nieco więcej o aktywnościach i układach,


możemy wykonać kilka ostatnich kroków kreatora
i wygenerować prostą aktywność i układ.

12 Rozdział 1.
Zaczynamy

Tworzenie aplikacji (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
4. Utworzenie aktywności ¨ Modyfikacja aplikacji

Kolejne okno kreatora prezentuje grupę szablonów, których można używać


do tworzenia aktywności i układów. Musisz wybrać jeden z nich. Ponieważ
mamy zamiar utworzyć aplikację składającą się z prostej aktywności i układu,
wybierz szablon Blank Activity (pusta aktywność) i kliknij przycisk Next.

Dostępnych jest
kilkanaście innyc także
rodzajów aktywn h

które można wybr ci,
tym razem upew ać, ale
czy został zaznacnij się,
szablon Blank Ac zony
tivity.

jesteś tutaj  13
Konfiguracja aktywności

Tworzenie aplikacji (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
5. Skonfigurowanie aktywności ¨ Modyfikacja aplikacji

Teraz zostaniesz poproszony o podanie nazwy ekranu aktywności i układu.


Dodatkowo musisz także określić tytuł ekranu i podać nazwę zasobu menu.
Jako nazwę aktywności (w polu Activity Name) wpisz MainActivity,
natomiast układowi nadaj (w polu Layout Name) nazwę activity_main.
Aktywność jest klasą Javy, a układ — plikiem XML. Oznacza to, że
podane informacje spowodują utworzenie pliku źródłowego Javy o nazwie
MainActivity.java i pliku XML o nazwie activity_main.xml.

Kiedy klikniesz przycisk Finish, Android Studio wygeneruje aplikację.

Jako nazwę aktywności


podaj „MainActivity”,
a jako nazwę układu
„activity_main”,
w pozostałych polach
zaś zostaw domyślne
wartości.

14 Rozdział 1.
Zaczynamy

Właśnie utworzyłeś swoją pierwszą aplikację ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
na Androida ¨
¨
Uruchomienie aplikacji
Modyfikacja aplikacji

A co się właściwie stało?

 Kreator Android Studio utworzył projekt aplikacji i skonfigurował ją zgodnie


z wybranymi opcjami.
Określiłeś, z którą wersją Androida ma być zgodna tworzona aplikacja, a kreator utworzył
wszystkie pliki i katalogi, których będzie potrzebowała prosta, działająca aplikacja.

 Kreator wygenerował także prostą aktywność i układ, zawierające kody


z wybranego szablonu.
Kod wchodzący w skład szablonu obejmuje kod XML układu i kod aktywności napisany
w Javie. Ich działanie ogranicza się do wyświetlenia na ekranie prostego tekstu „Hello
world!”. Oczywiście ten kod możesz teraz zmienić.

Po zakończeniu tworzenia projektu — podaniu wymaganych informacji we wszystkich oknach


dialogowych kreatora — Android Studio automatycznie wyświetli projekt.
To jest projekt
wyświetlony
Oto, jak będzie wyglądał nasz nowo utworzony projekt (nie przejmuj się, jeśli na razie wygląda na w Android Studio.
bardzo skomplikowany — jego poszczególne elementy wyjaśnimy na kilku kolejnych stronach):

jesteś tutaj  15
Struktura katalogów

Android Studio utworzy pełną strukturę ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
katalogów aplikacji ¨
¨
Uruchomienie aplikacji
Modyfikacja aplikacji

Aplikacja na Androida jest w rzeczywistości grupą prawidłowych plików rozmieszczonych


w określonej strukturze katalogów. Android Studio przygotowuje dla nas te wszystkie
pliki i katalogi podczas tworzenia nowej aplikacji. Najprostszym sposobem przejrzenia
tej struktury katalogów jest skorzystanie z eksploratora plików umieszczonego w lewej
kolumnie Android Studio.

Eksplorator plików prezentuje wszystkie aktualnie otwarte projekty. Aby rozwinąć lub
zwinąć katalog, wystarczy kliknąć strzałkę wyświetloną z lewej strony ikony katalogu.

Tutaj wybierz
Struktura katalogów projektu
opcję Project,
aby wyświetlić zawiera pliki różnych typów
pliki i katalogi
wchodzące Jeśli przejrzysz zawartość projektu, to zauważysz,
w skład projektu. że kreator utworzył wiele katalogów, a w nich pliki
wielu różnych typów:
To jest
nazwa  Pliki źródłowe Java i XML
projektu. To wygenerowane przez kreator pliki aktywności
i układów.

 Pliki Javy wygenerowane przez Androida


To dodatkowe pliki źródłowe Javy wygenerowane
Klikając
te strzałki, automatycznie przez Android Studio; nie musisz
możesz się przejmować ich zawartością ani zmieniać jej
rozwijać ręcznie.
i zwijać
katalogi.
 Pliki zasobów
Zaliczają się do nich domyślne pliki graficzne ikon,
Wszystkie
te pliki pliki stylów oraz pliki z wartościami łańcuchowymi
i katalogi używanymi przez aplikację.
wchodzą
w skład
projektu.  Biblioteki Androida
W kreatorze określana jest minimalna wersja
SDK, z którą będzie zgodna aplikacja. Android
Studio zadba, by w projekcie znalazły się biblioteki
odpowiednie dla wybranej wersji SDK.

 Pliki konfiguracyjne
Pliki konfiguracyjne informują Androida
o zawartości aplikacji oraz o to tym, jak powinna
ona działać.

Przyjrzymy się teraz nieco dokładniej wybranym,


16 Rozdział 1. kluczowym plikom i katalogom Androidowa.
Zaczynamy

Przydatne pliki projektu ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Projekty Android Studio używają narzędzia do budowy projektów gradle do kompilacji
¨ Modyfikacja aplikacji
i wdrażania aplikacji. Projekty gradle mają określoną, standardową strukturę. Poniżej
przedstawiliśmy kilka kluczowych plików i katalogów, których będziemy używać:

Katalog build/ zawiera pliki


Mojapierwszaapka utworzone dla nas przez Android
Studio. Zazwyczaj nie musimy
Nazwa katalogu samodzielnie modyfikować
głównego odpowiada app
zawartości tych plików i katalogów.
nazwie projektu.
build
Każdy projekt aplikacji na
Androida musi zawierać plik
generated
o nazwie R.java, który jest
tworzony automatycznie
source i umieszczany w automatycznie
wygenerowanych katalogach.
Android używa go do
r
Katalog src/ zawiera kody zarządzania zasobami aplikacji.
src
źródłowe, które tworzymy
i edytujemy. debug
main
com.hfad.mojapierwszaapka
Katalog java/ zawiera tworzone
przez nas kody źródłowe aplikacji. java
To właśnie tu są umieszczone R.java
wszystkie aktywności aplikacji. com.hfad.mojapierwszaapka
Plik MainActivity.java definiuje
Zasoby aplikacji można znaleźć aktywność. Aktywność informuje
w katalogu res/. Katalog MainActivity.java system, w jaki sposób aplikacja
layout/ zawiera pliki układów, ma prowadzić interakcję
a katalog values/ pliki zasobów res z użytkownikiem.
z wartościami, takimi jak łańcuchy
znaków. Istnieją także inne typy Plik activity_main.xml definiuje
layout układ. Układ informuje system,
zasobów. <xml>
</xml> jak ma wyglądać aplikacja.
activity_main.xml
Każda aplikacja na Androida w swoim
katalogu głównym musi zawierać plik Plik strings.xml zawiera pary
o nazwie AndroidManifest.xml. To tak values identyfikator – łańcuch. W tym pliku
<xml>
zwany plik manifestu, zawierający </xml>
przechowywane są takie łańcuchy
kluczowe informacje dotyczące znaków jak nazwa aplikacji i wszelkie
strings.xml inne domyślne wartości tekstowe.
aplikacji, takie jak komponenty,
z których się ona składa, wymagane <xml> Inne pliki, takie jak pliki aktywności
biblioteki i inne deklaracje.
</xml>
lub układów, mogą odczytywać
AndroidManifest.xml zapisane w nim łańcuchy znaków.

jesteś tutaj  17
Edytory

Edycja kodu z użyciem edytorów Android Studio ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Do przeglądania i edytowania plików w Android Studio służą edytory. Wystarczy
¨ Modyfikacja aplikacji
dwukrotnie kliknąć plik, z którym chcemy pracować, a Android Studio natychmiast
wyświetli jego zawartość w środkowej, największej części okna.

Edytor kodu
Większość plików jest
wyświetlana w edytorze kodu.
Edytor kodu przypomina
zwyczajny edytor tekstów,
lecz dysponuje kilkoma
dodatkowymi możliwościami,
takimi jak kolorowanie
kodu i sprawdzanie jego
poprawności syntaktycznej.

Dwukrotnie kliknij plik, …a jego zawartość


który Cię interesuje… zostanie wyświetlona
w panelu edytora.

Edytor projektu
W przypadku edycji
układu mamy do
dyspozycji dodatkową
opcję. Zamiast
edytować bezpośrednio
kod XML możemy
skorzystać z edytora
projektu. Pozwala on To różne sposoby
przeciągać do układu prezentacji
zawartości tego
graficzne komponenty samego pliku:
interfejsu użytkownika pierwszy widok
Te przyciski przedstawia jego
i rozmieszczać je pozwalają kod, a drugi
w wybrany sposób. Edytor wybrać edytor, pokazuje wygląd
którego chcemy układu.
kodu i edytor projektu używać.
zapewniają dwa różne
sposoby prezentacji tego
samego pliku, zatem
w każdej chwili można się
pomiędzy nimi przełączać.

18 Rozdział 1.
?
  Zaczynamy

DO CZEGO SŁUŻĘ?

Poniżej przedstawiamy kod źródłowy pliku układu wygenerowanego przez


Android Studio. Wiemy, że jeszcze nigdy nie widziałeś takiego pliku na oczy,
ale przekonajmy się, czy potrafisz dopasować opisy umieszczone u dołu strony
z odpowiednimi wierszami kodu. Aby Ci ułatwić, podaliśmy jedną odpowiedź.

activity_main.xml

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:paddingBottom=”16dp”
tools:context=”.MainActivity”>

<TextView
android:text=”@string/hello_world”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />

</RelativeLayout>

Dodaje wypełnienie do Wyświetla zawartość zasobu


marginesów ekranu. łańcuchowego o nazwie
hello_world.

Dodaje do układu komponent


GUI o nazwie TextView, który
służy do wyświetlania tekstów. Sprawia, że szerokość i wysokość
układu będą odpowiadały
szerokości i wysokości ekranu
Sprawia, że tekst będzie urządzenia.
dostosowywany do wielkości
komponentu zarówno w poziomie,
jak i w pionie.

jesteś tutaj  19
?
Rozwiązanie  


DO CZEGO SŁUŻĘ?
ROZWIĄZANIE
Poniżej przedstawiamy kod źródłowy pliku układu wygenerowanego przez
Android Studio. Wiemy, że jeszcze nigdy nie widziałeś takiego pliku na oczy,
ale przekonajmy się, czy potrafisz dopasować opisy umieszczone u dołu strony
z odpowiednimi wierszami kodu. Aby Ci ułatwić, podaliśmy jedną odpowiedź.

activity_main.xml

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:paddingBottom=”16dp”
tools:context=”.MainActivity”>

<TextView
android:text=”@string/hello_world”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />

</RelativeLayout>

Dodaje wypełnienie Wyświetla zawartość zasobu


do marginesów ekranu. łańcuchowego o nazwie
hello_world.

Dodaje do układu komponent


GUI o nazwie TextView, który
służy do wyświetlania tekstów. Sprawia, że szerokość i wysokość
układu będą odpowiadały
szerokości i wysokości ekranu
Sprawia, że tekst będzie urządzenia.
dostosowywany do wielkości
komponentu zarówno w poziomie,
jak i w pionie.

20 Rozdział 1.
?
  Zaczynamy

DO CZEGO SŁUŻĘ?

A teraz przekonajmy się, czy będziesz w stanie podobnie dopasować opisy do


kodu aktywności. To jest kod przykładowy, a nie kod wygenerowany przez
Android Studio podczas tworzenia projektu. A zatem dopasuj opisy do
odpowiednich wierszy kodu.

MainActivity.java

package com.hfad.mojapierwszaapka;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

To jest nazwa pakietu. Implementacja metody onCreate()


zdefiniowanej w klasie Activity.
Ta metoda jest wywoływana
w momencie pierwszego tworzenia
To są klasy Androida aktywności.
używane przez naszą klasę
MainActivity.

MainActivity rozszerza klasę


android.app.Activity.
Określa, którego układu
należy użyć.

jesteś tutaj  21
?
Kolejne rozwiązanie  


DO CZEGO SŁUŻĘ?
ROZWIĄZANIE
A teraz przekonajmy się, czy będziesz w stanie podobnie dopasować opisy do
kodu aktywności. To jest kod przykładowy, a nie kod wygenerowany przez
Android Studio podczas tworzenia projektu. A zatem dopasuj opisy do
odpowiednich wierszy kodu.

MainActivity.java

package com.hfad.mojapierwszaapka;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

To jest nazwa pakietu. Implementacja metody onCreate()


zdefiniowanej w klasie Activity.
Ta metoda jest wywoływana
w momencie pierwszego tworzenia
To są klasy Androida aktywności.
używane przez naszą klasę
MainActivity.

MainActivity rozszerza klasę


android.app.Activity.
Określa, którego układu
należy użyć.

22 Rozdział 1.
Zaczynamy

Uruchamianie aplikacji w emulatorze Androida ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Dotychczas przekonałeś się jedynie, jak wygenerowana aplikacja będzie ¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
wyglądała w Android Studio, i dowiedziałeś nieco, z czego się ona składa.
Ale zapewne tym, co naprawdę chciałbyś zobaczyć, jest ta aplikacja
w działaniu, prawda?

Jeśli chodzi o uruchamianie aplikacji, to mamy do wyboru kilka opcji.


Pierwszą z nich jest uruchomienie aplikacji na fizycznym urządzeniu.
Ale co zrobić, jeśli nie będziemy mieli takiego pod ręką albo jeśli będziemy
Emulator Androida pozwala
chcieli sprawdzić działanie aplikacji na urządzeniu konkretnego typu, wykonywać aplikacje na
którego nie posiadamy? wirtualnych urządzeniach
W takich przypadkach można skorzystać z alternatywnego rozwiązania, z Androidem (AVD). Takie
jakim jest uruchomienie aplikacji na emulatorze Androida wchodzącym
w skład Android SDK. Emulator umożliwia przygotowanie jednego lub kilku
wirtualne urządzenie działa
wirtualnych urządzeń z Androidem (określanych skrótowo jako AVD), tak samo jak urządzenie
a następnie uruchamianie aplikacji w emulatorze w taki sposób, jak gdyby fizyczne. Można utworzyć
działały na fizycznym urządzeniu.
wiele urządzeń wirtualnych,
Jak zatem wygląda emulator? emulujących różne typy
Rysunek zamieszczony obok przedstawia AVD uruchomione w emulatorze urządzeń.
Androida. Jak widać, emulator wygląda jak telefon działający na komputerze.

Emulator jest aplikacją, która odtwarza Kiedy już skonfigurujesz


AVD, możesz uruchomić
konkretne środowisko sprzętowe urządzenia na nim aplikację
z Androidem: zaczynając od jego procesora i zobaczyć, jak działa.
i pamięci, a kończąc na układzie dźwiękowym Android Studio samo
uruchamia emulator.
i ekranie. Emulator Androida bazuje
na istniejącym już emulatorze o nazwie
QEMU, podobnym do wielu innych aplikacji
obsługujących maszyny wirtualne, z którymi
być może już się zetknąłeś, takimi jak
VirtualBox lub VMWare.

Dokładny wygląd i zachowanie urządzenia


wirtualnego zależy od jego konfiguracji.
To przedstawione obok ma symulować
telefon Nexus 4, dlatego będzie wyglądało
i działało tak jak Nexus 4 uruchomiony Podobnie jak
normalny telefon,
na komputerze. także wirtualne
urządzenie trzeba
A zatem przygotuj teraz własne urządzenie odblokować, zanim
wirtualne, żeby zobaczyć swoją aplikację zaczniemy go
używać — wystarczy
działającą w emulatorze. kliknąć ikonę kłódki
i przeciągnąć ją
w górę.

jesteś tutaj  23
Tworzenie AVD

Tworzenie wirtualnego urządzenia z Androidem ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Utworzenie wirtualnego urządzenia z Androidem w Android Studio wymaga


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
wykonania kilku czynności. Spróbujemy teraz utworzyć AVD symulujące
telefon Nexus 4 obsługujący API poziomu 21. Dzięki temu przekonasz się, jak
Twoja nowa aplikacja wygląda i działa na urządzeniach tego typu. Czynności
wykonywane w kreatorze są niemal takie same, niezależnie od typu tworzonego
urządzenia.

Otwórz Android Virtual Device Manager


Program AVD Manager umożliwia tworzenie nowych
urządzeń wirtualnych oraz przeglądanie i edytowanie już
istniejących. Aby go otworzyć, należy wybrać z menu Android
Studio opcję Tools/AVD Manager.

Jeśli wcześniej nie utworzyłeś żadnego AVD, to na ekranie


zostanie wyświetlone okno z sugestią, abyś to zrobił.

Kliknij przycisk Create a virtual


device, aby utworzyć nowe AVD.

Wybierz komponenty sprzętowe


W następnym oknie dialogowym kreatora znajdzie się prośba
o wybranie definicji urządzenia. Ta definicja określa typ
urządzenia, które
tworzone AVD będzie
emulować. Możesz
wybierać spośród wielu
rodzajów telefonów,
tabletów, urządzeń
ubieralnych oraz
przystawek telewizyjnych.

My chcielibyśmy się
przekonać, jak nasza
nowa aplikacja wygląda
na telefonie Nexus 4.
A zatem najpierw na
liście Category zaznacz Kiedy wybierzesz urządzenie,
tutaj zostaną wyświetlone
opcję Phone, a potem szczegółowe informacje o nim.
z listy obok wybierz
opcję Nexus 4. Następnie
kliknij przycisk Next.

24 Rozdział 1.
Zaczynamy

Tworzenie AVD (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Wybierz obraz systemu ¨ Modyfikacja aplikacji

Kolejną czynnością, jaką musisz wykonać, jest wybór obrazu systemu. Obraz systemu
udostępnia zainstalowaną wersję systemu operacyjnego Android. W ten sposób możesz
określić, która wersja systemu ma działać na tworzonym urządzeniu wirtualnym i jaki
ma być typ jego procesora (ARM lub x86).

Musisz wybrać obraz systemu odpowiadający poziomowi API, który będzie zgodny
z tworzoną aplikacją. Na przykład jeśli tworząc aplikację, określiłeś, że minimalnym
poziomem API jest poziom 15, to tworząc AVD, wybierz obraz systemu obsługujący
co najmniej API poziomu 15. W tym przykładzie wybierzemy obraz systemu dla API
poziomu 21. A zatem wybierz opcję Lollipop 21 armeabi-v7a z wartością Android 5.0.1
lub Default w kolumnie Target. Kiedy to zrobisz, kliknij przycisk Next.

Jeśli te obrazy
systemów
nie będą
zainstalowane,
to AVD Manager
umożliwi ich
pobranie.

Na następnej stronie będziemy kontynuować konfigurowanie AVD.

jesteś tutaj  25
Sprawdzenie konfiguracji

Tworzenie AVD (ciąg dalszy) ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji
¨ Uruchomienie aplikacji
Sprawdź konfigurację AVD ¨ Modyfikacja aplikacji

W następnym oknie dialogowym kreatora zostaniesz poproszony o zweryfikowanie


konfiguracji AVD. To okno zawiera podsumowanie wszystkich wybranych wcześniej
opcji oraz daje możliwość ich zmiany. Zaakceptuj opcje i kliknij przycisk Finish.

To są wszystkie
opcje, które wybrałeś
na kilku poprzednich
stronach.

Teraz AVD Manager utworzy urządzenie wirtualne, a kiedy skończy, wyświetli je na liście.
Możesz już zamknąć okno AVD Managera.

Twój wirtualny Nexus 4 został utworzony.

26 Rozdział 1.
Zaczynamy

Uruchomienie aplikacji w emulatorze ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Skoro już przygotowałeś swoje urządzenie wirtualne, możesz spróbować


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
uruchomić na nim aplikację. W tym celu wystarczy wybrać opcję Run ‘app’
z menu Run. Kiedy zostaniesz poproszony o wybranie urządzenia, upewnij
się, czy przycisk opcji Launch emulator jest zaznaczony i czy z listy poniżej
został wybrany utworzony wcześniej wirtualny Nexus 4.

A teraz, cierpliwie czekając na uruchomienie urządzenia, przyjrzymy się


nieco dokładniej, co dzieje się po wybraniu opcji Run.

Skompilowanie, spakowanie, wdrożenie i wykonanie


Wybranie opcji Run nie powoduje jedynie uruchomienia aplikacji — oprócz
tego wykonywane są wszystkie inne niezbędne czynności przygotowawcze:

To jest utworzone przed chwilą


urządzenie wirtualne.

Biblioteki Zasoby

Plik APK to pakiet


2
1
aplikacji na Androida.
APK
W zasadzie jest to
Plik Javy Kod bajtowy Plik APK zwyczajny plik JAR
4
3 5 lub ZIP zawierający
Run
aplikację.

Emulator

Emulator

1 Pliki źródłowe Javy zostają skompilowane 4 Po uruchomieniu emulatora i wybranego


do postaci kodów bajtowych. urządzenia wirtualnego zostanie na nie
wgrany plik APK aplikacji, po czym aplikacja
2 Zostaje utworzony pakiet aplikacji, zostanie zainstalowana w systemie.
czyli plik APK.
Plik APK zawiera skompilowane pliki Javy 5 AVD uruchomi główną aktywność aplikacji.
oraz wszystkie biblioteki i zasoby niezbędne W tym momencie aplikacja zostanie wyświetlona
do uruchomienia aplikacji. na ekranie AVD i będzie gotowa do testowania.

3 Jeśli emulator jeszcze nie działa, to zostanie


uruchomiony, a na nim zostanie uruchomione
wybrane urządzenie wirtualne.
jesteś tutaj  27
Prosimy o cierpliwość

Postępy możesz obserwować w konsoli ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Uruchomienie emulatora i urządzenia wirtualnego czasami może zająć całkiem


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
sporo czasu — często jest to nawet kilka minut. Na szczęście okazuje się, że postępy
wykonywanych czynności można obserwować w konsoli Android Studio. Konsola
udostępnia szczegółowy dziennik wszystkich czynności wykonywanych przez narzędzie Sugerujemy, żebyś w trakcie
oczekiwania na uruchomienie emulatora
gradle, a jeśli podczas tych operacji wystąpią jakieś błędy, to zostaną one wyraźnie znalazł sobie jakieś inne zajęcie,
wyróżnione. na przykład możesz szydełkować
lub ugotować sobie szybki obiad.
Panel konsoli jest wyświetlany u dołu okna Android Studio.

Poniżej zamieściliśmy zawartość konsoli wyświetloną podczas wykonanej Android Studio uruchamia emulator,
a w nim AVD symulujące Nexusa 4
przez nas próby wykonania aplikacji: — to, które przed chwilą utworzyłeś.

Waiting for device.


/Applications/adt-bundle-mac/sdk/tools/emulator -avd Nexus_4_API_21 -netspeed full -netdelay none
Device connected: emulator-5554
Device Nexus_4_API_21 [emulator-5554] is online, waiting for processes to start up..
Device is ready: Nexus_4_API_21 [emulator-5554]
Urządzenie wirtualne zostało
Target device: Nexus_4_API_21 [emulator-5554] uruchomione i działa.
Uploading file
local path: /Users/Piotrek/Documents/Helion/ksiazki/Head First Android Development/kody_robocze/MojaPierwszaApka/app/build/
outputs/apk/app-debug.apk
remote path: /data/local/tmp/com.hfad.mojapierwszaapka
Installing com.hfad.mojapierwszaapka
DEVICE SHELL COMMAND: pm install -r ”/data/local/tmp/com.hfad.mojapierwszaapka”
pkg: /data/local/tmp/com.hfad.mojapierwszaapka APK.
Trwa wgrywanie i instalacja pliku
Success
Launching application: com.hfad.myfirstapp/com.hfad.mojapierwszaapka.MainActivity.
DEVICE SHELL COMMAND: am start -n ”com.hfad.mojapierwszaapka /com.hfad.mojapierwszaapka.MainActivity” -a
android.intent.action.MAIN -c android.intent.category.LAUNCHER
I w końcu zostaje uruchomiona
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] aplikacja i wykonana jej aktywność
cmp=com.hfad.mojapierwszaapka/.MainActivity }
główna; ta aktywność została
wygenerowana przez kreator.

28 Rozdział 1.
Zaczynamy

¨ Przygotowanie środowiska
¨ Stworzenie aplikacji
Jazda próbna ¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
Sprawdźmy zatem, co widać na ekranie podczas uruchamiania aplikacji.

W pierwszej kolejności w osobnym oknie jest wyświetlany emulator.


Następnie, przez dłuższą chwilę, emulator wczytuje AVD, a chwilkę
po jego uruchomieniu wyświetlany jest ekran blokady urządzenia. …a to jest zablokowane
urządzenie wirtualne

wygląda ono i działa
tak samo
Najpierw zostaje jak prawdziwy telefon
Nexus 4.
uruchomiony
emulator…

Kiedy odblokujesz ekran AVD, przesuwając


ikonę kłódki ku górze, zostanie na nim
wyświetlona utworzona aplikacja. Jej nazwa
będzie widoczna na pasku w górnej części To jest tytuł
aplikacji.
ekranu, a poniżej, w głównej części ekranu,
będzie widoczny domyślny napis
„Hello world!”. To jest
przykładowy
tekst
zastosowany
przez kreator.

Android Studio, bez zawracania


nam tym głowy, wygenerowało
tekst „Hello world!”.
A to jest
aplikacja
działająca
w AVD.

jesteś tutaj  29
Co się stało?

Ale co się właściwie stało? ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Przyjrzyjmy się dokładnie wszystkiemu,


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
co się dzieje podczas uruchamiania aplikacji:

1 Android Studio uruchamia emulator,


wczytuje AVD i instaluje aplikację.
1
2
2 Po uruchomieniu aplikacji zostaje
utworzony obiekt aktywności, której <Layout>
kod pochodzi z pliku MainActivity.java. 3

3 Aktywność informuje, że używa </Layout>


układu activity_main.xml. <Layout>
Aktywność
Urządzenie
</Layout>
Układ
4
4 Aktywność nakazuje systemowi
wyświetlenie układu na ekranie.
W efekcie zostaje wyświetlony tekst W naszym przypadku
jest to urządzenie
„Hello world!”. wirtualne.

Nie istnieją
głupie pytania

P: Wspominaliście, że podczas tworzenia pliku APK kody P: To wszystko wygląda na bardzo skomplikowane. Nie
źródłowe Javy zostają skompilowane do postaci kodów można po prostu użyć zwyczajnej wirtualnej maszyny Javy?
bajtowych i dodane do pliku APK. Przypuszczam,
że chodziło Wam o to, że zostają one skompilowane O: Środowisko ART jest w stanie konwertować kody DEX na kod
do kodów bajtowych Javy, prawda? maszynowy, wykonywany bezpośrednio przez procesor urządzenia
z Androidem. To sprawia, że aplikacja działa znacznie szybciej
O: Owszem, zostają, ale to nie wszystko. Na Androidzie sytuacja i zużywa znacznie mniej baterii.
wygląda nieco inaczej.
Kluczowa różnica polega na tym, że na Androidzie kod nie jest P : Czy wirtualna maszyna Javy naprawdę powoduje
wykonywany w standardowej wirtualnej maszynie Javy Java aż tak duże narzuty?
VM. Zamiast tego działa on w środowisku uruchomieniowym
Androida (ART), a na starszych urządzeniach w poprzedniku
O: Tak. Ponieważ na Androidzie każda aplikacja działa
w odrębnym procesie. Oznacza to, że gdyby była używana
ART, środowisku uruchomieniowym noszącym nazwę Dalvik.
standardowa wirtualna maszyna Javy, to urządzenia z Androidem
Oznacza to, że piszemy kod w Javie, kompilujemy go do postaci
musiałyby mieć znacznie więcej pamięci.
plików .class, używając kompilatora Javy, a następnie pliki klasowe
zostają zapisane w pliku w formacie DEX, który zajmuje mniej
miejsca i jest bardziej wydajny od zwyczajnych kodów bajtowych.
P: Czy za każdym razem, gdy tworzę nową aplikację,
muszę także tworzyć nowe AVD?
Środowisko uruchomieniowe wykonuje te kody DEX. Więcej
informacji na ten temat można znaleźć w Dodatku A. O: Nie. Po utworzeniu AVD możesz go używać do uruchamiania
wszystkich tworzonych aplikacji. Czasami można jednak utworzyć
wiele urządzeń wirtualnych, aby testować aplikacje w różnych
środowiskach. Na przykład można utworzyć wirtualny tablet, aby
przekonać się, jak aplikacja będzie wyglądać i działać na większych
urządzeniach.

30 Rozdział 1.
Zaczynamy

Usprawnianie aplikacji ¨
¨
Przygotowanie środowiska
Stworzenie aplikacji

Podczas lektury kilku ostatnich stron stworzyłeś prostą aplikację na


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
Androida i przekonałeś się, jak ona wygląda i działa w emulatorze.
Teraz zajmiemy się nieznacznym usprawnieniem tej aplikacji.

Obecnie aplikacja wyświetla tekst „Hello world!” umieszczony


w niej przez kreator. Mamy zamiar zmienić go na coś nieco bardziej
interesującego. A co należy w tym celu zrobić? Aby odpowiedzieć
na to pytanie, musimy się trochę cofnąć i przyjrzeć się konstrukcji
aplikacji.
Chcesz zmienić tekst
wyświetlany przez aplikację
na coś innego niż „Hello world!”.
Aplikacja składa się z jednej aktywności
i jednego układu
Podczas tworzenia aplikacji określiliśmy, w jaki sposób ma być
skonfigurowana, a kreator Android Studio zrobił całą resztę
— przede wszystkim utworzył prostą aktywność i domyślny układ.

Nasza aktywność
określa, co
aplikacja robi
Aktywność kontroluje działanie aplikacji i w jaki sposób
prowadzi interakcję
Android Studio utworzyło aktywność o nazwie z użytkownikiem.
MainActivity.java. Ta aktywność określa, co aplikacja
robi i jak ma reagować na poczynania użytkownika. MainActivity.java

Układ określa wygląd aplikacji


Aktywność MainActivity.java określa, że używa układu <Layout> Nasz układ
activity_main.xml, który także został wygenerowany przez określa, jak
aplikacja
Android Studio. Ten układ określa, jak wygląda aplikacja. wygląda.
</Layout>

activity_main.xml
Teraz chcielibyśmy zmienić wygląd aplikacji, a konkretnie — treść
tekstu wyświetlanego na ekranie. Oznacza to, że będziemy musieli
zmodyfikować komponent odpowiadający za wygląd aplikacji.
Musimy zatem nieco dokładniej przyjrzeć się układowi.

jesteś tutaj  31
Układ

Czym jest układ? ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Naszym celem jest zmiana przykładowego tekstu „Hello world!”


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
zastosowanego przez Android Studio. Zacznijmy zatem od pliku
activity_main.xml. Jeśli ten plik jeszcze nie jest otwarty w edytorze,
to otwórz go teraz — odszukaj plik w katalogu app/src/main/res/
Edytor projektu.
layout i dwukrotnie go kliknij.

Edytor projektu
W Android Studio pliki układów można
przeglądać i edytować na dwa sposoby:
używając edytora kodu lub edytora projektu.

Jeśli skorzystamy z tej drugiej opcji,


przekonamy się, że zgodnie z oczekiwaniami
A to jest
w układzie zostanie wyświetlony tekst „Hello przykładowy
world!”. A jak wygląda źródłowy kod XML tekst.
tego pliku układu?

Aby się przekonać, wystarczy przełączyć się


do edytora kodu.

Aby przełączyć się do


edytora projektu, wystarczy
kliknąć kartę „Design”.

Edytor kodu
W przypadku wybrania edytora kodu
Edytor kodu
jest w nim wyświetlana zawartość pliku
activity_main.xml. Przyjrzymy się jej
teraz nieco dokładniej.

Aby wyświetlić edytor kodu,


wystarczy kliknąć kartę „Text”
wyświetloną u dołu okna IDE.
32 Rozdział 1.
Zaczynamy

Plik activity_main.xml zawiera dwa elementy ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Poniżej przedstawiamy kod umieszczony w pliku activity_main.xml


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
wygenerowanym dla nas przez Android Studio.

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools” To jest pełna ścieżka
android:layout_width=”match_parent” dostępu do pliku
activity_main.xml.
android:layout_height=”match_parent” Mojapierwszaapka
... Tutaj Android Studio
ay nt
t>.

umieściło nieco więcej


eL me
ou

tools:context=”.MainActivity” > kodu XML, ale na razie app/src/main


tiv ele

nie musisz zaprzątać


ela t

sobie nim głowy.


<R jes

res
To

<TextView
android:text=”@string/hello_world” To jest element <TextView>
zagnieżdżony wewnątrz layout
android:layout_width=”wrap_content” elementu <RelativeLayout>. <xml>
</xml>
android:layout_height=”wrap_content” /> activity_main.xml
</RelativeLayout>

Powyższy kod składa się z dwóch elementów. Android Studio czasami wyświetla
Pierwszy to <RelativeLayout>. Informuje on system, wartość odwołania zamiast
że położenie elementów układu ma być określane w sposób Obejrzyj to! rzeczywistego kodu.
względny. Elementu <RelativeLayout> można użyć
na przykład do wyśrodkowania elementów w układzie, Na przykład zamiast faktycznego kodu ”@string/
wyświetlenia ich przy dolnej krawędzi ekranu bądź określenia hello_world” Adroid Studio może wyświetlić
położenia jednego elementu względem innego. tekst ”Hello world!”. Każde takie podstawienie
powinno zostać wyróżnione w edytorze kodu
Drugim elementem naszego układu jest <TextView>. podświetleniem, a jego kliknięcie lub umieszczenie
Służy on do wyświetlania tekstu na ekranie. Jak widać, na nim wskaźnika myszy powinno spowodować
element <TextView> został umieszczony wewnątrz elementu pokazanie faktycznego kodu.
<RelativeLayout>, a w naszym przypadku służy on do
wyświetlania przykładowego tekstu „Hello world!”.

Kluczowym elementem kodu w elemencie <TextView> jest


jego pierwszy wiersz. Co można o nim powiedzieć?

<TextView
Element android:text=”@string/hello_world” Co możesz
TextView powiedzieć o tym
opisuje tekst
android:layout_width=”wrap_content” wierszu kodu?
wyświetlany android:layout_height=”wrap_content” />
w układzie.

jesteś tutaj  33
Łańcuchy znaków

Plik układu zawiera odwołanie do łańcucha,


a nie sam łańcuch znaków Wartości łańcuchowe
należy umieszczać
Kluczowym fragmentem elementu <TextView> jest jego pierwszy wiersz:
w pliku strings.xml, a nie
android:text=”@string/hello_world”
podawać na stałe w kodzie
Fragment android:text oznacza, że jest to właściwość text elementu aplikacji. strings.xml to
<TextView>; innymi słowy określa on tekst, który powinien zostać wyświetlony
w układzie. Ale dlaczego ma on postać ”@string/hello_world”, a nie ”Hello plik zasobów służący do
world!”? Jakie jest faktyczne znaczenie atrybutu android:text i jego wartości przechowywania par nazwa
zastosowanej w powyższym przykładzie?
– wartość łańcuchowa.
Zacznijmy od jego początku: @string. To sposób pozwalający poinformować
system, że ma odszukać tekst w pliku zasobów wartości łańcuchowych. W naszym
Układy i aktywności mogą
przypadku Android Studio automatycznie utworzyło taki plik zasobów, a nosi on pobierać i używać tych
nazwę strings.xml i jest umieszczony w katalogu app/src/main/res/values.
wartości łańcuchowych,
Druga część, hello_world, informuje, że należy odszukać wartość zasobu posługując się ich nazwami.
o nazwie hello_world. A zatem cały fragment @string/hello_world oznacza:
„odszukaj zasób łańcuchowy o nazwie hello_world i użyj skojarzonej z nim
wartości”.
…z zasobu łańcuchowego
o nazwie hello_world.
Wyświetl tekst…
android:text="@string/hello_world" />

To trochę skomplikowane.
Dlaczego w pliku activity_main.xml nie
można po prostu podać wyświetlanego
tekstu? Czy tak nie byłoby prościej?

Jest jeden kluczowy powód takiego rozwiązania: lokalizacja.

Załóżmy, że napisałeś aplikację i stała się ona wielkim hitem w lokalnym, krajowym sklepie
Google Play. Ale przecież nie chcesz się ograniczać tylko do jednego kraju lub języka
— chcesz zapewne, by Twoja aplikacja była dostępna globalnie, w wielu językach.

Umieszczenie używanych łańcuchów znaków w pliku strings.xml sprawia, że radzenie sobie z takimi
zagadnieniami staje się znacznie prostsze. Zamiast zmieniać podane na stałe wartości w wielu
miejscach kodu i w plikach układu wystarczy zastąpić plik strings.xml jego wielojęzyczną wersją.

Oprócz tego stosowanie pliku strings.xml jako centralnego źródła wartości tekstowych znacząco
ułatwia wprowadzanie globalnych zmian w tekstach używanych w aplikacji. Jeśli szef zażąda
skorygowania tekstów wyświetlanych w aplikacji ze względu na zmianę nazwy firmy, wystarczy
wprowadzić poprawki w pliku strings.xml.

34 Rozdział 1.
Zaczynamy

Zajrzyjmy do pliku strings.xml ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Android Studio automatycznie utworzyło dla nas plik z zasobami łańcuchowymi,


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
o nazwie strings.xml, sprawdźmy zatem, czy faktycznie zawiera on zasób o nazwie
hello_world. Skorzystaj z eksploratora plików, by odnaleźć katalog app/src/main/
res/values, i kliknij go dwukrotnie, aby go otworzyć.
To jest pełna ścieżka
dostępu do pliku
Oto, jak wygląda zawartość pliku strings.xml: Mojapierwszaapka strings.xml.

<?xml version=”1.0” encoding=”utf-8”?> app/src/main


<resources>
res
<string name=”app_name”>Moja pierwsza apka</string>
<string name=”hello_world”>Hello world!</string> values
Plik strings.xml zawiera
<xml>
<string name=”action_settings”>Settings</string>
łańcuch znaków o nazwie
</xml>

</resources> hello_world i wartości strings.xml


„Hello world!”.

Jak widać, w pliku znajduje się wiersz kodu wyglądający dokładnie jak to, czego szukamy.
Opisuje on zasób łańcuchowy o nazwie hello_world i wartości ”Hello world!”:

<string name=”hello_world”>Hello world!</string>

Zmodyfikuj plik strings.xml, aby zmienić tekst


Zmieńmy zatem przykładowy tekst wyświetlany w aplikacji. Jeśli jeszcze tego
nie zrobiłeś, to odszukaj teraz w Android Studio plik strings.xml i go otwórz.

Poniżej przedstawiliśmy kod stanowiący zawartość tego pliku. Musisz odszukać


w nim łańcuch o nazwie ”hello_world” i zmienić skojarzoną z nim wartość
z ”Hello world!” na ”Siemka stary!”:

<?xml version=”1.0” encoding=”utf-8”?>


<resources> Zmień podaną tu wartość
y!”.
z „Hello world!” na „Siemka star
<string name=”app_name”>Moja pierwsza apka</string>
<string name=”hello_world”>Hello world!Siemka stary!</string>
<string name=”action_settings”>Settings</string>
</resources>

Po wprowadzeniu zmian w pliku zapisz je, wybierając z menu głównego


opcję File/Save All.

jesteś tutaj  35
Pod lupą

Pliki zasobów łańcuchowych pod lupą


Plik strings.xml jest domyślnym plikiem zasobów używanym do przechowywania
par nazwa – wartość, do których następnie można się odwoływać w innych
miejscach aplikacji. Oto jego format:
Element <string> określa, że
para nazwa wartość jest
Element <?xml version=”1.0” encoding=”utf-8”?> łańcuchem znaków.
<resources> <resources>
informuje, że
zawartością <string name=”app_name”>Moja pierwsza apka</string>
pliku są zasoby. <string name=”hello_world”>Hello world!</string>
<string name=”action_settings”>Settings</string>
</resources>

Istnieją dwa czynniki, dzięki którym Android jest w stanie rozpoznać,


że plik strings.xml jest plikiem zasobów łańcuchowych:

 Został on zapisany w katalogu app/src/main/res/values.


Pliki XML umieszczone w tym katalogu zawierają proste wartości,
takie jak łańcuchy znaków lub kolory.

 Plik zawiera element <resources>, który z kolei zawiera jeden


lub więcej elementów <string>.
Sam format, w jakim została zapisana zawartość pliku, wskazuje, że zostały
w nim podane łańcuchy znaków. Element <resources> informuje system, że plik
zawiera zasoby, a element <string> — że tymi zasobami są łańcuchy znaków.

To wszystko oznacza, że zasobów łańcuchowych wcale nie musimy przechowywać w pliku


o nazwie strings.xml — ten plik może nosić dowolną inną nazwę, można także używane
łańcuchy znaków zapisać w kilku różnych plikach.

Każda para nazwa – wartość ma następującą postać:

<string name=”nazwa_łańcucha”>wartość_łańcucha</string>

gdzie nazwa_łańcucha jest identyfikatorem danego łańcucha znaków, a wartość_łańcucha


określa sam łańcuch.

W pliku układu można odwołać się do konkretnego łańcucha znaków, używając zapisu
o postaci:

”@string/nazwa_łańcucha” To jest nazwa łańcucha, którego


wartość chcemy zwrócić.
Fragment „string” informuje system,
że ma odszukać zasób łańcuchowy
o podanej nazwie.

36 Rozdział 1.
Zaczynamy

Weź swoją aplikację na jazdę próbną ¨


¨
Przygotowanie środowiska
Stworzenie aplikacji

Po wprowadzeniu zmian opisanych na poprzednich stronach spróbuj


¨ Uruchomienie aplikacji
¨ Modyfikacja aplikacji
ponownie uruchomić aplikację w emulatorze, wybierając w tym celu
opcję Run/Run ‘app’ z menu głównego. Powinieneś zauważyć, że aplikacja
wyświetla teraz tekst „Siemka stary!”, a nie „Hello world!”.

To jest
zaktualizowana
wersja aplikacji
uruchomiona
w emulatorze.

Przykładowy tekst ma teraz


postać „Siemka stary!”,
a nie „Hello world!”.

Nie istnieją
głupie pytania
P: Czy umieszczanie wartości łańcuchowych w plikach P: A skąd aplikacja będzie wiedziała, którego z nich użyć?
zasobów, takich jak strings.xml, jest absolutnie
konieczne? O: Umieść swój domyślny plik z polskimi zasobami łańcuchowymi
w standardowym miejscu katalogu app/src/main/res/values.
O: Nie ma takiego obowiązku, jednak Android wyświetla A plik z tekstami angielskimi w katalogu app/src/main/res/values-
komunikat ostrzegawczy, jeśli wartości tekstowe zostaną podane en. Jeśli w urządzeniu został wybrany język angielski, to Android
na stałe w kodzie. Początkowo można sądzić, że to sporo zastosuje pliki zasobów z katalogu app/src/main/res/values-en.
zachodu, lecz takie rozwiązanie znacznie ułatwia inne prace, takie Jeśli w urządzeniu został wybrany jakikolwiek inny język, system
jak lokalizowanie aplikacji. Poza tym łatwiej jest już od samego użyje plików zasobów z katalogu app/src/main/res/values.
początku używać zasobów łańcuchowych niż dodawać je później.
P: Kod układu, który wygenerowało moje Android
P: W jaki sposób umieszczenie łańcuchów znaków Studio, wygląda nieco inaczej niż ten przedstawiony
w osobnym pliku może pomóc w lokalizacji aplikacji? w książce. Czy mam się tym martwić?

O: Załóżmy, że chcesz, by Twoja aplikacja domyślnie wyświetlała O: Kod XML generowanego układu może się nieco różnić
tekstu w języku polskim i w języku angielskim, jeśli w telefonie w zależności od używanej wersji Android Studio. Nie musisz się
został wybrany inny język niż polski. W takim przypadku zamiast jednak tym przejmować, gdyż zaczynając od zaraz i tak będziesz
na stałe umieszczać teksty w różnych językach w aplikacji, mógłbyś się uczył tworzyć własne układy, dlatego i tak zastąpisz układ
utworzyć jeden plik z tekstami polskimi i drugi z angielskimi. wygenerowany przez Android Studio.

jesteś tutaj  37
Przybornik

Twój przybornik do Androida


Rozdział 1.

Opanowałeś już rozdział 1. i dodałeś j


do swojego przybornika z narzędziami Pełny kod przykładowe
lika cji pre zen tow ane j
ap
podstawowe pojęcia i informacje mo żes z
w tym rozdziale
związane z tworzeniem aplikacji na Androida. pobrać z ser we ra FT P
wydawnictwa Helion:
ftp://ftp.helion.pl/
przyklady/andrrg.zip

CELNE SPOSTRZEŻENIA
 Kolejne wersje Androida są identyfikowane na  Plik AndroidManifest.xml zawiera informacje
podstawie numeru wersji, poziomu API oraz o samej aplikacji. Jest on przechowywany
nazwy kodowej. w katalogu app/src/main.
 Android Studio jest specjalną wersją InelliJ IDEA,  AVD to skrót od angielskich słów Android
dostosowaną do współpracy z Android Software Virtual Device, oznaczający wirtualne urządzenie
Development Kit (Android SDK) i z systemem z Androidem. Takie wirtualne urządzenia są
budowy gradle. wykonywane w emulatorze Androida i udają
urządzenia fizyczne.
 Typowa aplikacja na Androida składa się
z aktywności, układów oraz plików zasobów.  Plik APK to plik pakietu aplikacji. Przypomina on
plik JAR, przy czym zawiera aplikację na Androida
 Układy opisują, jak aplikacja ma wyglądać. Są one
jej kody bajtowe, biblioteki i zasoby. Instalacja
przechowywane w katalogu app/src/main/res/
aplikacji na urządzeniu z Androidem polega na
layout.
zainstalowaniu pliku APK.
 Aktywności opisują, co aplikacja robi i w jaki  Aplikacje na Androida działają w niezależnych
sposób prowadzi interakcję z użytkownikiem.
procesach wykonywanych przez środowisko
Pisane przez nas aktywności są przechowywane
uruchomieniowe Androida (ART).
w katalogu app/src/main/java.
 RelativeLayout określa rozmieszczenie
 Plik strings.xml zawiera pary nazwa – wartość.
elementów w układzie w sposób względny.
Pozwala on zapewnić separację pomiędzy
wartościami tekstowymi oraz układami  Element TextView służy do wyświetlania
i aktywnościami, jak również ułatwia tekstów.
lokalizowanie aplikacji.

38 Rozdział 1.
2. Tworzenie interaktywnych aplikacji

Aplikacje, które coś robią


Zastanawiam się,
co się stanie, jeśli
nacisnę przycisk
z napisem „katapulta”…

Większość aplikacji musi w jakiś sposób reagować na poczynania użytkowników.


Z tego rozdziału dowiesz się, co zrobić, aby Twoje aplikacje były nieco bardziej interaktywne.
Przekonasz się, jak zmusić aplikację, by coś zrobiła w odpowiedzi na działania użytkownika, oraz
jak sprawić, by aktywności i układy porozumiewały się ze sobą jak starzy kumple. Przy okazji
pokażemy Ci nieco dokładniej, jak naprawdę działa Android poznasz plik R, czyli ukryty
klejnot, który spaja pozostałe elementy aplikacji.

to jest nowy rozdział  39


Doradca piwny

W tym rozdziale napiszemy aplikację


Doradca piwny Wybierz ulubiony
rodzaj piwa, kliknij
przycisk…
Z poprzedniego rozdziału wiesz, jak można utworzyć prostą
aplikację, używając do tego celu kreatora New Project
udostępnianego przez Android Studio, oraz jak zmienić tekst
wyświetlany w układzie. Zazwyczaj jednak, tworząc aplikacje
na Androida, będziemy chcieli, aby coś robiły.
…a aplikacja
W tym rozdziale pokażemy Ci, jak napisać aplikację, z którą wyświetli listę
sugerowanych
użytkownik będzie mógł prowadzić interakcję, a konkretnie rzecz gatunków piw.
biorąc, będzie to aplikacja o nazwie Doradca Piwny. Pozwoli ona
użytkownikowi wybrać ulubiony rodzaj piwa, kliknąć przycisk
i otrzymać listę smacznych piw, które warto spróbować.

Oto struktura aplikacji:

1 Wygląd aplikacji zostanie określony przez układ.


Tak będzie wyglądał
Układ będą tworzyć trzy komponenty graficznego interfejsu układ naszej aplikacji.
użytkownika:
• komponent typu Spinner, czyli rozwijana lista
pozwalająca użytkownikowi wybrać ulubiony rodzaj piwa; <Layout>
• przycisk, którego kliknięcie spowoduje zwrócenie listy 1
2
piw wybranego rodzaju; </Layout> <resources>

• pole tekstowe wyświetlające dobrane gatunki piwa.


Układ </resources>

2 Plik strings.xml zawiera wszystkie zasoby łańcuchowe strings.xml


używane przez układ, na przykład etykietę przycisku
wyświetlanego na ekranie.

3 Aktywność określa, w jaki sposób aplikacja powinna 3


prowadzić interakcję z użytkownikiem.
Aktywność pobierze rodzaj piwa wybrany przez użytkownika Aktywność
i użyje go do wyświetlenia listy piw, którymi użytkownik
mógłby być zainteresowany. Czynności te będą wykonywane
przy użyciu niestandardowej, napisanej przez nas klasy Javy.

4 Napisana przez nas klasa Javy zawiera logikę


działania aplikacji. 4
Ta klasa będzie zawierała metodę pobierającą rodzaj
piwa (przekazywany jako parametr) i zwracającą listę piw
Nasza klasa Javy
tego rodzaju. Aktywność będzie wywoływać tę metodę,
przekazywać do niej wybrany rodzaj piwa i wyświetlać
zwrócone wyniki.

40 Rozdział 2.
Tworzenie interaktywnych aplikacji

A oto, co musisz zrobić


A zatem bierzmy się do pracy i stwórzmy naszego doradcę piwnego.
W tym celu musisz wykonać kilka czynności (w dalszej części rozdziału
będziemy je odznaczać na liście rzeczy to zrobienia):

1 Utworzyć projekt.
Tworzymy zupełnie nową aplikację, musisz więc utworzyć
nowy projekt. Podobnie jak w poprzednim rozdziale, będziesz
potrzebować prostego układu i aktywności.

2 Zaktualizować układ.
Po wygenerowaniu aplikacji musisz zaktualizować jej układ
i dodać do niego wszystkie niezbędne komponenty GUI.

3 Powiązać układ z aktywnością.


Układ określa jedynie wizualne elementy aplikacji. Aby aplikacja <Layout>

była nieco bardziej inteligentna, trzeba powiązać układ z kodem


</Layout>
Javy w aktywności.

4 Napisać logikę działania aplikacji.


Dodasz do aplikacji własną klasę Javy i posłużysz się nią,
by mieć pewność, że będzie wyświetlać odpowiednie gatunki piwa,
dostosowane do opcji wybranej przez użytkownika.

jesteś tutaj  41
Utworzenie projektu

¨  Utworzenie projektu
Utworzenie projektu ¨  Aktualizacja układu
¨  Połączenie aktywności
Zabierzmy się zatem za pisanie nowej aplikacji (czynności, jakie należy w tym celu ¨  Implementacja logiki
wykonać, są bardzo podobne do tych, które wykonaliśmy w poprzednim rozdziale):
 Otwórz Android Studio i na ekranie powitalnym kliknij opcję Start a new Android Studio project.
Spowoduje to uruchomienie kreatora przedstawionego w rozdziale 1.

 Kiedy zostaniesz poproszony o podanie nazwy aplikacji, wpisz Doradca piwny, co automatycznie
spowoduje zmianę nazwy pakietu na com.hfad.doradcapiwny.

 Chcemy, by nasza aplikacja działała na większości telefonów i tabletów, dlatego na liście Minimum
SDK wybierz API poziomu 15 i upewnij się, że będzie zaznaczone pole wyboru Phone and Tablet.
Oznacza to, że każdy telefon lub tablet, na którym będziemy chcieli uruchomić aplikację, będzie
musiał mieć zainstalowane API przynajmniej poziomu 15. Kryterium to spełnia przeważająca
większość aktualnie używanych urządzeń z Androidem.

 Jako domyślną aktywność wybierz szablon Blank Activity (pusta aktywność). Nadaj jej nazwę
FindBeerActivity, a plik układu nazwij activity_find_beer. Zaakceptuj domyślne wartości w polach
Title i Menu Resource Name, gdyż nie będziemy ich na razie potrzebować.

wszystkie te
Kreator przeprowadzi Cię przez im rozdziale.
czynności, podo bnie jak w popr zedn
j nazw ę Dora dca piwny,
Tworzonej aplikacji nada m API
określ, że jej minimalnym poziome y aktywności,
będzie 15, a następnie podaj nazw ity_find_beer.
activ
FindBeerActivity, i pliku układu,

42 Rozdział 2.
Tworzenie interaktywnych aplikacji

Utworzyliśmy domyślną aktywność i układ


DoradcaPiwny
Kiedy klikniesz przycisk Finish, Android Studio utworzy nowy projekt zawierający
aktywność zdefiniowaną w pliku FindBeerActivity.java oraz plik układu o nazwie
app/src/main
activity_find_beer.xml. Zacznijmy od wprowadzenia zmian w pliku układu.
W tym celu wyświetl zawartość katalogu aap/src/main/res/layout i otwórz plik
res
activity_find_beer.xml.

Podobnie jak w poprzednim rozdziale, także teraz kreator utworzył domyślny layout
układ, który zawiera element <TextView> prezentujący tekst „Hello world!”. <xml>
</xml>
Poniżej przedstawiliśmy kod tego układu:
activity_find_beer.xml

Kod XML układu


<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”16dp” Te wszystkie elementy
odnoszą się do układu
android:paddingRight=”16dp” jako całości. Określają
jego szerokość i wysokość
android:paddingTop=”16dp” oraz wypełnienia jego
android:paddingBottom=”16dp” marginesów.
tools:context=”.FindBeerActivity”>

<TextView
android:text=”@string/hello_world”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />

</RelativeLayout> Element <TextView> widoczny


w kodzie XML został wyświetlony
Edytor projektu w edytorze projektu.

jesteś tutaj  43
Edytor projektu

¨  Utworzenie projektu
Dodawanie komponentów w edytorze projektu ¨  Aktualizacja układu
¨  Połączenie aktywności
Komponenty graficznego interfejsu użytkownika można dodawać do układów na dwa ¨  Implementacja logiki
sposoby: bezpośrednio w kodzie XML bądź w edytorze projektu. Zacznijmy od dodania
przycisku w edytorze projektu.

Z lewej strony edytora projektu jest wyświetlona paleta zawierająca komponenty GUI, które
można przeciągać do układu. Jeśli przyjrzysz się sekcji Widgets, zauważysz w niej na pewno
komponent przycisku — Button. Kliknij go, a następnie przeciągnij do edytora projektu.

To jest paleta
komponentów.

W naszej
wersji aplikacji
A to jest przycisk umieściliśmy
— komponent Button. przycisk
Przeciągnij go do układu. pod tekstem
„Hello world!”.

Zmiany wprowadzane w edytorze projektu są odzwierciedlane w kodzie XML


Przeciąganie komponentów w opisany powyżej sposób jest bardzo wygodną metodą aktualizowania
układów. Jeśli teraz przełączysz się do edytora projektu, to zauważysz, że dodanie przycisku
spowodowało wprowadzenie zmian w kodzie pliku układu: Postać kodu, który edytor projektu
... doda do pliku układu, będzie zależeć
od miejsca, w którym umieścimy
<TextView przycisk. Jest całkiem prawdopodobne,
android:text=”@string/hello_world” że kod XML Twojego układu będzie
wyglądał nieco inaczej niż ten
android:layout_width=”wrap_content” przedstawiony tutaj, ale nie przejmuj
android:layout_height=”wrap_content” się tym, bo niebawem to zmienimy.
android:id=”@+id/textView” />
Teraz do elementu Text
View
W pliku pojawił się <Button został dodany identyfikator.
nowy element Button; android:layout_width=”wrap_content”
opisuje on przycisk, android:layout_height=”wrap_content”
który dodałeś do układu.
Przyjrzymy się mu nieco android:text=”New Button”
dokładniej na kilku android:id=”@+id/button”
następnych stronach
książki. android:layout_below=”@+id/textView”
android:layout_alignLeft=”@+id/textView” />
...
44 Rozdział 2.
Tworzenie interaktywnych aplikacji

Plik activity_find_beer.xml zawiera nowy przycisk


Edytor dodał do pliku activity_find_beer.xml nowy element Button:

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”New Button”
android:id=”@+id/button”
android:layout_below=”@+id/textView”
android:layout_alignLeft=”@+id/textView” />

W Androidowie termin „przycisk” oznacza standardowe przyciski, które użytkownik


może kliknąć, aby wykonać pewną akcję. Każdy przycisk zawiera właściwości służące
do kontrolowania jego położenia, wielkości i wyglądu oraz określa metodę aktywności,
którą należy wykonać. Te wszystkie właściwości nie są unikalne wyłącznie dla przycisków
— dysponują nimi także inne komponenty GUI, choćby takie jak TextView.

Przyciski i widoki tekstowe są klasami pochodnymi klasy View


Istnieje pewien ważny powód, który sprawia, że przyciski i widoki tekstowe,
czyli komponenty TextView, mają wiele wspólnych właściwości — oba te Klasa View zawiera wiele różnych
rodzaje komponentów dziedziczą po tej samej klasie: View. Więcej informacji metod. Przyjrzymy się im dokładniej
w dalszej części książki.
na jej temat znajdziesz w dalszej części książki, a na razie przedstawiliśmy
poniżej kilka jej najczęściej używanych właściwości.
android.view.View
android:id setId(int)
Ta właściwość określa nazwę identyfikującą dany komponent. Właściwość ID ...
umożliwia kontrolę działania komponentu z poziomu kodu, a także określanie
jego położenia w układzie: TextView jest
typu View…
android:id=”@+id/button”

android:text android.widget.TextView
setText(CharSequence,
Ta właściwość określa, jaki tekst powinien zostać wyświetlony w komponencie. TextView.BufferType)
W przypadku komponentu Button będzie to tekst wyświetlany na przycisku:
...
android:text=”New button”

android:layout_width, android:layout_height …a Button jest typu


TextView, co oznacza,
Te właściwości określają podstawową szerokość i wysokość komponentu. że jest także typu View.
Wartość ”wrap_content” oznacza, że wielkość komponentu powinna być
na tyle duża, by można było wyświetlić w nim jego zawartość. android.widget.Button
android:layout_width=”wrap_content” ...
android:layout_height=”wrap_content”
jesteś tutaj  45
Kod układu

¨  Utworzenie projektu
Dokładniejszy przegląd kodu układu ¨  Aktualizacja układu
¨  Połączenie aktywności
Przyjrzyjmy się nieco dokładniej kodowi układu i podzielmy go na poszczególne elementy, ¨  Implementacja logiki
abyś mógł lepiej zrozumieć, jak on działa (nie przejmuj się, jeśli Twój kod wygląda nieco
inaczej, po prostu przeanalizuj wraz z nami kod podany poniżej):

To jest element <RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”


RelativeLayout.
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:paddingBottom=”16dp”
tools:context=”.FindBeerActivity”>

To jest <TextView
widok
tekstowy. android:text=”@string/hello_world” DoradcaPiwny
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” app/src/main
android:id=”@+id/textView” />
res
To jest <Button
przycisk.
android:layout_width=”wrap_content” layout
android:layout_height=”wrap_content” <xml>
</xml>
android:text=”New Button”
activity_find_beer.xml
android:id=”@+id/button”
android:layout_below=”@+id/textView”
android:layout_alignLeft=”@+id/textView” />

</RelativeLayout> Ten znacznik zamyka element Rela


tiveLayout.

Element RelativeLayout
ślania
Pierwszym elementem w kodzie układu jest <RelativeLayout>. Element Istnieją także inne sposoby okre
a kom pone ntów graf iczn ego
położeni
ten informuje system, że położenie poszczególnych komponentów GUI interfejsu użytkownika. Poznasz
je
ma być wyznaczane względem innych komponentów. Można go zatem w dalszej części książki.
użyć na przykład, aby wyświetlić jeden komponent z lewej strony innego
bądź aby wyrównać komponenty w określony sposób.

W tym przykładzie przycisk jest umieszczony bezpośrednio poniżej


widoku tekstowego, a zatem położenie przycisku jest określane względem
tego widoku.

46 Rozdział 2.
Tworzenie interaktywnych aplikacji

Element TextView
Pierwszym elementem umieszczonym wewnątrz <RelativeLayout> Zastosowanie układu
jest <TextView>:
... względnego oznacza,
<TextView że położenie
android:text=”@string/hello_world”
android:layout_width=”wrap_content” komponentów GUI
android:layout_height=”wrap_content”
będzie określane
android:id=”@+id/textView” />
... względem innych
Jak widać, w kodzie tego elementu nie zostały podane żadne właściwości elementów układu.
określające, w którym miejscu układu powinien on zostać wyświetlony, dlatego
domyślnie Android wyświetli go w lewym górnym rogu ekranu. Zwróć też
uwagę, że komponent ten ma identyfikator o wartości textView. Przekonasz się,
dlaczego to takie ważne, gdy przyjrzymy się następnemu elementowi.

Element Button
Ostatnim elementem umieszczonym w układzie <RelativeLayout>
jest przycisk — element <Button>: Widok tekstowy jest domyślnie
wyświetlany w lewym górnym
... rogu ekranu.

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”New Button”
android:id=”@+id/button”
android:layout_below=”@+id/textView”
android:layout_alignLeft=”@+id/textView” />
...

Dodając przycisk do układu, umieściliśmy go w taki sposób, by był widoczny Przycisk ma zostać wyświetlony
poniżej widoku tekstowego,
poniżej tekstu, a jego lewa krawędź znajdowała się bezpośrednio poniżej lewej a ich lewe krawędzie mają być
krawędzi widoku tekstowego. Oznacza to, że położenie przycisku zostało wyrównane.
określone względem położenia widoku tekstowego i ten fakt jest wyraźnie
odzwierciedlony w kodzie XML układu:

android:layout_below=”@+id/textView”
android:layout_alignLeft=”@+id/textView”

Istnieje kilka możliwych sposobów zapisu kodu XML układu, pozwalających


uzyskać ten sam efekt wizualny. Na przykład powyższy kod XML określa, że
przycisk jest umieszczony poniżej widoku tekstowego. Ale identyczny efekt można
uzyskać, stwierdzając, że widok tekstowy ma być umieszczony powyżej przycisku.
jesteś tutaj  47
Aktualizacja układu

DoradcaPiwny
Zmiany w kodzie XML układu…
app/src/main
Przekonałeś się już, że zmiany wprowadzane w widoku projektu są
natychmiast uwzględniane w kodzie XML układu. To samo dotyczy
res
odwrotnej sytuacji: wszelkie zmiany wprowadzane w kodzie XML
układu są pokazywane w widoku projektu.
layout
Sprawdźmy, czy faktycznie tak się dzieje. Zastąp zawartość swojego <xml>
</xml>
pliku activity_find_beer.xml kodem przedstawionym poniżej:
activity_find_beer.xml

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
tools:context=”.FindBeerActivity” >

Spinner to
stosowana <Spinner
w Androidzie android:id=”@+id/color” Ten element wyświetla
nazwa na rozwijaną w układzie komponent
android:layout_width=”wrap_content” Spinner, czyli rozwijaną
listę konkretnych
wartości. Pozwala android:layout_height=”wrap_content” listę wartości.
on wybrać jedną android:layout_alignParentTop=”true”
z dostępnych opcji.
android:layout_centerHorizontal=”true”
android:layout_marginTop=”37dp” />

<Button
android:id=”@+id/find_beer”
Umieść przycisk android:layout_width=”wrap_content”
poniżej rozwijanej android:layout_height=”wrap_content”
listy i wyrównaj go android:layout_alignLeft=”@+id/color”
do lewej krawędzi
listy. android:layout_below=”@+id/color”
android:text=”Button” />

<TextView
android:id=”@+id/brands”
android:layout_width=”wrap_content”
Umieść widok android:layout_height=”wrap_content”
Zrób to sam!
tekstowy poniżej
przycisku android:layout_alignLeft=”@+id/find_beer”
i wyrównaj go android:layout_below=”@+id/find_beer”
do lewej krawędzi
przycisku. android:layout_marginTop=”18dp”
android:text=”TextView” /> Zastąp zawartość swojego 
pliku activity_find_beer.xml 
</RelativeLayout>
przedstawionym tu kodem XML.

48 Rozdział 2.
Tworzenie interaktywnych aplikacji

…są uwzględniane w edytorze projektu ¨  Utworzenie projektu


¨  Aktualizacja układu
¨  Połączenie aktywności
Kiedy już wprowadzisz modyfikacje w kodzie XML układu, przełącz się do
¨  Implementacja logiki
widoku projektu. Zamiast układu zawierającego tekst i przycisk zobaczysz
tekst wyświetlony poniżej przycisku.

Natomiast powyżej przycisku zostanie wyświetlony spinner. Spinner to


stosowana w terminologii Androida nazwa określająca rozwijaną listę
wartości. Dotknięcie tego komponentu powoduje rozwinięcie listy,
tak by można było wybrać jeden z jej elementów. Komponent Spinner
tworzy rozwijaną listę
wartości. Pozwala on
wybrać jedną wartość
To jest rozwijana lista,
z której użytkownik będzie
mógł wybrać ulubiony
ze zbioru określonych
rodzaj piwa.
wartości.
Użytkownik kliknie
przycisk… Komponenty GUI, takie
…a tu zostanie wyświetlona jak przyciski, widoki
lista dobranych dla niego
gatunków piwa. tekstowe oraz listy
rozwijane, mają bardzo
podobne atrybuty,
gdyż wszystkie są typu
View. Wszystkie te
komponenty dziedziczą
bowiem po wspólnej
klasie View.

Pokazaliśmy Ci, jak można dodawać komponenty GUI do układu, i to


zarówno przy użyciu graficznego edytora projektu, jak i bezpośrednio
w kodzie XML. Ogólnie rzecz biorąc, zamierzone efekty łatwiej można
uzyskać, modyfikując kod XML układu niż wybierając opcje w edytorze
projektu. Dzieje się tak dlatego, że wprowadzając zmiany w kodzie XML,
mamy bardziej bezpośrednią kontrolę nad układem — innymi słowy nie
jesteśmy uzależnieni od Android Studio.

jesteś tutaj  49
Dodanie łańcuchów znaków

¨  Utworzenie projektu
Stosuj zasoby łańcuchowe, ¨  Aktualizacja układu
a nie łańcuchy podawane w kodzie ¨  Połączenie aktywności
¨  Implementacja logiki

Jest jeszcze jedna rzecz, którą musimy zmienić przed próbą uruchomienia
tej aplikacji. Aktualnie zarówno tekst umieszczony na przycisku, jak i tekst
prezentowany w widoku tekstowym są podane na stałe, bezpośrednio
we właściwości text. Jak już wspominaliśmy w rozdziale 1., takie teksty
warto zamienić na zasoby łańcuchowe, definiowane w pliku strings.xml.
Choć nie jest to właściwie obowiązkowe, to jednak takie postępowanie jest
dobrym nawykiem, który warto sobie wyrobić. Stosowanie plików zasobów
łańcuchowych do gromadzenia statycznych tekstów ułatwia późniejsze
tworzenie innych wersji językowych aplikacji, a jeśli kiedyś pojawi się
konieczność wprowadzenia zmian w wyświetlanych tekstach, będzie to
można zrobić w jednym miejscu.

Otwórz zatem plik app/src/main/res/values/strings.xml. Po przełączeniu się


do widoku XML jego zawartość powinna wyglądać podobnie do kodu
zamieszczonego poniżej:

<?xml version=”1.0” encoding=”utf-8”?>


To są łańcuchy znaków utworzon
<resources> dla nas przez Android Studio.
e

<string name=”app_name”>Doradca Piwny</string>


<string name=”hello_world”>Hello world!</string>
<string name=”action_settings”>Settings</string>

</resources>
DoradcaPiwny

W pierwszej kolejności usuń zasób ”hello_world”, gdyż nie będzie


app/src/main
nam już potrzebny. Następnie dodaj nowy zasób łańcuchowy o nazwie
”find_beer” i wartości ”Odszukaj piwo!”. Potem dodaj nowy zasób
o nazwie ”brands”, ale na razie nie określaj jeszcze jego wartości. res

Nowy kod pliku powinien wyglądać tak: values


<xml>
... </xml>

<string name=”app_name”>Doradca piwny</string> strings.xml

<string name=”hello_world”>Hello world!</string>


<string name=”action_settings”>Settings</string> Musisz usunąć zasób
<string name=”find_beer”>Odszukaj piwo!</string> hello_world oraz dodać
dwa nowe zasoby:
<string name=”brands”></string> find_beer i brands.
...

50 Rozdział 2.
Tworzenie interaktywnych aplikacji

Zmiana układu i zastosowanie w nim zasobów łańcuchowych


Teraz zajmiemy się zmianą przycisku i widoku tekstowego oraz zastosowaniem
w nich zasobów łańcuchowych utworzonych na poprzedniej stronie.

Otwórz zatem plik activity_find_beer.xml i wprowadź w nim następujące zmiany:

 Zmień wiersz android:text=”Button” na android:text=”@string/find_beer”.

 Zmień wiersz android:text=”TextView” na android:text=”@string/brands”.

...
<Spinner
android:id=”@+id/color” DoradcaPiwny

android:layout_width=”wrap_content”
app/src/main
android:layout_height=”wrap_content”
android:layout_alignParentTop=”true”
res
android:layout_centerHorizontal=”true”
android:layout_marginTop=”37dp” /> layout
<xml>
</xml>

<Button activity_find_beer.xml
android:id=”@+id/find_beer”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/color”
android:layout_below=”@+id/color” Dzięki temu na przycisku
zostanie wyświetlona
android:text=”@string/find_beer” /> zawartość zasobu
łańcuchowego find_beer.

<TextView
android:id=”@+id/brands”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/find_beer”
android:layout_below=”@+id/find_beer”
Dzięki temu w widoku tekstowym zostanie
android:layout_marginTop=”18dp” wyświetlona zawartość zasobu brands.
Choć obecnie jest on pusty, to jednak
android:text=”@string/brands” /> jeśli w przyszłości podamy w nim jakiś inny
łańcuch znaków, zostanie on wyświetlony.
...

jesteś tutaj  51
Jazda próbna

¨  Utworzenie projektu
Weź swoją aplikację na jazdę próbną ¨  Aktualizacja układu
¨  Połączenie aktywności
Wciąż mamy jeszcze sporo do zrobienia w naszej aplikacji, sprawdźmy ¨  Implementacja logiki
jednak, jak ona wygląda w swojej aktualnej postaci. Zapisz wszystkie
wprowadzone zmiany, a następnie wybierz opcję Run ‘app’ z menu Run.
Kiedy zostaniesz o to poproszony, wybierz opcję uruchomienia emulatora.

Poczekaj cierpliwie na uruchomienie aplikacji, w końcu pojawi się


w emulatorze. To jest rozwijana
lista, choć
Spróbuj dotknąć rozwijaną listę. Choć obecnie nie będzie to takie na razie jest
oczywiste, dotknięcie tego komponentu powoduje wyświetlenie jeszcze pusta.
rozwijanej listy wartości — na razie jednak jest ona pusta.
Przycisk znajduje się
Oto, co udało się nam już zrobić poniżej rozwijanej listy
i jest wyrównany do jej
Poniżej zamieściliśmy krótkie podsumowanie wszystkiego, co udało się lewej krawędzi.
nam do tej pory zrobić:

1 Stworzyliśmy układ określający wygląd aplikacji.


Nasz układ zawiera rozwijaną listę, przycisk oraz widok Nie istnieją
głupie pytania
tekstowy.
P: Po uruchomieniu aplikacji układ 
2 Plik strings.xml zawiera używane przez aplikację wygląda nieco inaczej niż w edytorze 
zasoby łańcuchowe. projektu. Dlaczego tak się dzieje?
Dodaliśmy do niego etykietę przycisku i pusty łańcuch
znaków określający tymczasowo rodzaje piwa. O: Edytor projektu robi, co tylko może, by
w jak najlepszy sposób przedstawić postać
układu, niemniej jednak ma kilka ograniczeń.
3 Aktywność określa, jak aplikacja powinna prowadzić Na przykład kod XML naszego układu określa,
interakcję z użytkownikiem.
że rozwijana lista powinna być wyśrodkowana
Android Studio utworzyło dla nas prostą aktywność, w poziomie, co wcale nie musi być widoczne
na razie jednak jeszcze nic z nią nie zrobiliśmy. w edytorze projektu.
W praktyce zawsze najlepiej jest pracować
bezpośrednio na kodzie XML. W ten sposób
<Layout> znacznie łatwiej można się zorientować, co się
1
</Layout> 2 <resources>
dzieje, a oprócz tego mamy znacznie większą
kontrolę nad układem.
Układ
P:  A czy w układzie nie było jeszcze 
</resources>

strings.xml widoku tekstowego?

O: Cały czas jest, tylko aktualnie nie zawiera


żadnego tekstu, więc nie można go zauważyć.
Pojawi się w dalszej części rozdziału, kiedy
3
wyświetlimy w nim jakiś tekst.

Aktywność

52 Rozdział 2.
Tworzenie interaktywnych aplikacji

¨  Utworzenie projektu
Dodanie wartości do komponentu Spinner ¨  Aktualizacja układu
¨  Połączenie aktywności
Obecnie nasz układ zawiera rozwijaną listę, lecz jeszcze jest ona pusta. Za każdym ¨  Implementacja logiki
razem, gdy używamy komponentu tego typu, musimy zadbać o to, by zawierał on
listę wartości, gdyż tylko w takim przypadku użytkownik będzie mógł coś wybrać.
Zasoby to
Listę wartości do komponentu Spinner można przekazać mniej więcej w taki różnego rodzaju
sam sposób, w jaki określa się tekst wyświetlany w widoku tekstowym: przy użyciu
zasobów. Dotychczas używaliśmy pliku strings.xml wyłącznie do definiowania używane przez
pojedynczych wartości łańcuchowych. Tym razem musimy natomiast określić aplikację dane
tablicę takich łańcuchów i odwołać się do niej w komponencie.
i pliki, które
Tworzenie zasobu tablicowego nie są kodem.
przypomina dodawanie łańcuchów znaków
Jak już wiemy, zasób łańcuchowy można dodać do pliku strings.xml,
używając następującego kodu XML:

<string name=”nazwa_łańcucha”>wartość_łańcucha</string>

W kodzie tym nazwa_łańcucha jest identyfikatorem, a wartość_łańcucha jest


wartością zasobu.

Aby dodać tablicę łańcuchów, należy użyć kodu o następującej postaci:


<string-array name=”nazwa_tablicy_łańcuchów”> To jest nazwa tablicy.
<item>łańcuch_wartość1</item>
<item>łańcuch_wartość2</item> To są wartości umieszczone w tabli
Można dodawać dowolną ich liczb cy.
<item>łańcuch_wartość3</item> ę.
...
</string-array>
W powyższym kodzie nazwa_tablicy_łańcuchów jest nazwą tablicy, a łańcuch_
wartość1, łańcuch_wartość2 oraz łańcuch_wartość3 to poszczególne
łańcuchy znaków zapisane w tej tablicy.

Dodajmy zatem zasób string-array do naszej aplikacji. Otwórz plik strings.xml


i dodaj do niego poniższy, wyróżniony fragment kodu:
DoradcaPiwny
...
<string name=”brands”></string> app/src/main
<string-array name=”beer_colors”>
<item>jasne</item> res
<item>bursztynowe</item>
<item>brązowe</item> Dodaj ten element string-array do values
<item>ciemne</item> pliku strings.xml. Definiuje on tablicę <xml>
łańcuchów znaków o nazwie beer_colors, </xml>
</string-array> która zawiera następujące wartości: strings.xml
... jasne, bursztynowe, brązowe i ciemne.

jesteś tutaj  53
Jazda próbna

Dodanie do komponentu Spinner odwołania do string-array


W kodzie układu można odwołać się do zasobu string-array w bardzo podobny
sposób jak do zasobów łańcuchowych. W tym przypadku zamiast zapisu:

”@string/nazwa_łańcucha”

musimy użyć zapisu: Użyj zapisu @string, aby odwołać się do


zasobu łańcuchowego, i zapisu @array,
by odwołać się do tablicy.
”@array/nazwa_tablicy”

gdzie nazwa_tablicy jest nazwą zasobu tablicowego.

Użyjmy zatem naszego nowego zasobu w układzie. Przejdź do pliku DoradcaPiwny


activity_layout_beer.xml i dodaj do elementu Spinner atrybut entries:
... app/src/main
<Spinner
... res
android:layout_marginTop=”37dp”
android:entries=”@array/beer_colors” /> layout
<xml>
... </xml>
Ten zapis oznacza: „elementy
listy w komponencie Spinner activity_find_beer.xml
pochodzą z tablicy beer_colors”.

Jazda próbna komponentu Spinner


¨  Utworzenie projektu
Przekonajmy się zatem, jakie efekty wywołały te wszystkie zmiany w wyglądzie ¨  Aktualizacja układu
naszej aplikacji. Zapisz wszystkie zmodyfikowane pliki i uruchom aplikację. ¨  Połączenie aktywności
Uzyskane wyniki powinny przypominać te pokazane na poniższych rysunkach. ¨  Implementacja logiki

Domyślnie
w komponencie
wyświetlana Kliknij Kiedy
jest pierwsza komponent, klikniesz
wartość. aby rozwinąć wartość,
listę wartości. zostanie
ona
wybrana.

54 Rozdział 2.
Tworzenie interaktywnych aplikacji

¨  Utworzenie projektu
Musimy zadbać o to, by przycisk coś robił ¨  Aktualizacja układu
¨  Połączenie aktywności
Jak na razie dodaliśmy do układu komponenty GUI i wypełniliśmy rozwijaną listę, używając ¨  Implementacja logiki
tablicy łańcuchów znaków. Kolejnym zadaniem, którym musimy się zająć, jest zapewnienie,
by po kliknięciu przycisku aplikacja odpowiednio zareagowała na wartość wybraną z listy.
Chcemy, by nasza aplikacja działała mniej więcej w sposób opisany poniżej:

1 Użytkownik wybiera z rozwijanej listy 4 Metoda getBrands() odnajduje


rodzaj piwa. pasujące gatunki piwa
Użytkownik klika przycisk, by odszukać odpowiadające przekazanemu
pasujące gatunki piwa. rodzajowi, po czym zwraca je do
aktywności jako listę ArrayList
zawierającą łańcuchy znaków.
2 Układ określa, którą metodę aktywności
należy wywołać po kliknięciu przycisku.
5 Aktywność pobiera odwołanie do
widoku tekstowego umieszczonego
3 Metoda zdefiniowana w aktywności w układzie i wyświetla w nim
pobiera wartość wybraną z listy zwróconą listę gatunków piwa.
określającą ulubiony rodzaj piwa Ta lista jest wyświetlana na ekranie
i przekazuje ją do metody getBrands() urządzenia.
napisanej przez nas klasy o nazwie
BeerExpert.

2
1
<Layout>

</Layout> 3
5
Układ Aktywność
Urządzenie
getBrands("bursztynowe")
"Jack Amber"
"Red Moose"
4

BeerExpert

W pierwszej kolejności zadbajmy o to, by kliknięcie przycisku


powodowało wywołanie jakiejś metody aktywności.

jesteś tutaj  55
Atrybut onClick

Niech przycisk wywołuje metodę ¨  Utworzenie projektu


¨  Aktualizacja układu
Można przypuszczać, że zawsze dodając do układu jakiś przycisk, będziesz chcieć,
¨  Połączenie aktywności
¨  Implementacja logiki
by jego kliknięcie powodowało wykonanie jakiejś operacji. W tym celu przycisk
musi wywoływać metodę zdefiniowaną w aktywności.

Aby kliknięcie przycisku powodowało wywołanie metody aktywności, konieczne


jest wprowadzenie zmian w dwóch plikach:

 Musimy zmienić plik układu activity_find_beer.xml.


Określimy w nim, która metoda klasy aktywności ma zostać wywołana po kliknięciu przycisku.

 Musimy zmienić plik aktywności FindBeerActivity.java.


W tym pliku zdefiniujemy wywoływaną metodę.

Zacznijmy od modyfikacji układu.

Użyj onClick, by określić metodę wywoływaną przez przycisk


Poinstruowanie systemu, którą metodę należy wywołać po kliknięciu przycisku, wymaga
tylko jednego wiersza kodu XML. Musimy w tym celu dodać do elementu <Button>
atrybut android:onClick i podać w nim nazwę wywoływanej metody:
To oznacza: „kiedy komponent zostanie kliknięty, wywołaj
android:onClick=”nazwa_metody” zdefiniowaną w aktywności metodę nazwa_metody”.

Spróbujmy to zrobić. Przejdź do pliku układu, activity_find_beer.xml, i dodaj do elementu


<Button> nowy wiersz kodu XML, który określi, że kliknięcie przycisku ma spowodować
wywołanie metody onClickFindBeer():

...
<Button
DoradcaPiwny
android:id=”@+id/find_beer”
android:layout_width=”wrap_content” app/src/main
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/color” res
android:layout_below=”@+id/color”
android:text=”@string/find_beer” layout
<xml>
android:onClick=”onClickFindBeer” /> </xml>

... Kiedy przycisk


wywołaj zdef zostanie kliknięty, activity_find_beer.xml
in
metodę onClic iowaną w aktywności
Implementacj kFindBeer().
Kiedy już wprowadzisz te zmiany, zapisz plik. się na kilku
ą tej metody
kolejnych stro zajmiemy
nach.
Skoro układ już wie, którą metodę aktywności ma wywołać,
nadszedł czas, by ją zaimplementować. Przyjrzyjmy się zatem
aktywności.
56 Rozdział 2.
Tworzenie interaktywnych aplikacji

Jak wygląda kod aktywności?


Kiedy po raz pierwszy tworzyliśmy projekt aplikacji, kazaliśmy kreatorowi
wygenerować prostą aktywność o nazwie FindBeerActivity. Jej kod został
DoradcaPiwny
zapisany w pliku FindBeerActivity.java. Otwórz go teraz — wyświetl zawartość
katalogu app/src/main/java i dwukrotnie kliknij ikonę pliku.
app/src/main
Po wyświetleniu pliku przekonasz się, że Android Studio wygenerowało całkiem sporo
kodu napisanego w Javie. Zamiast analizować go teraz krok po kroku, zastąpimy java
go w całości kodem przedstawionym poniżej. Takie rozwiązanie wynika z faktu, że
przeważająca część kodu aktywności wygenerowanego przez Android Studio jest nam com.hfad.doradcapiwny
niepotrzebna, a chcemy skupić się na podstawach samego programowania aplikacji na
Androida, a nie na rozwiązaniach specyficznych dla konkretnego IDE. A zatem usuń cały
kod zapisany w pliku FindBeerActivity.java i zastąp go kodem przedstawionym poniżej: FIndBeerActivity.java

package com.hfad.doradcapiwny;

import android.os.Bundle; Klasa rozszerza klasę Activity,


czyli jedną z klas systemu Android.
import android.app.Activity;

jest
public class FindBeerActivity extends Activity { To jest metoda onCreate(), która go
wywoływana w momencie pierwsze
tworzenia aktywności.
@Override
protected void onCreate(Bundle savedInstanceState) { Metoda setContentView() informuje
super.onCreate(savedInstanceState); system o tym, którego układu używa
dana aktywność. W tym przypadku
setContentView(R.layout.activity_find_beer); jest to układ activity_find_beer.
}
}

Powyższy kod to wszystko, czego potrzeba do utworzenia prostej aktywności. Jak widać,
aktywność jest klasą, która dziedziczy po klasie android.app.Activity i implementuje
metodę onCreate().

Wszystkie aktywności muszą dziedziczyć po klasie Activity. Klasa ta definiuje grupę


metod, które przekształcają zwyczajne klasy Javy w pełnowartościowe aktywności Androida.

Oprócz tego wszystkie aktywności muszą implementować metodę onCreate(). Metoda ta


jest wywoływana w momencie tworzenia obiektu aktywności i służy do wykonania prostych Zrób to sam!
operacji konfiguracyjnych, takich jak określenie układu skojarzonego z daną aktywnością.
Ta konkretna czynność jest wykonywana poprzez wywołanie metody setContentView().
Jej wywołanie użyte w powyższym kodzie, setContentView(R.layout.activity_find_
beer), informuje system, że aktywność używa układu activity_find_beer.
Zastąp kod w swoim pliku 
Na poprzedniej stronie dodaliśmy do przycisku atrybut onClick i przypisali mu wartość FindBeerActivity.java 
onClickFindBeer. Musimy zatem dodać tę metodę do aktywności, aby można ją było kodem przedstawionym 
wywołać w momencie kliknięcia przycisku. Dzięki temu aktywność będzie w stanie
na tej stronie.
zareagować, gdy użytkownik dotknie przycisku wyświetlonego w interfejsie aplikacji.
jesteś tutaj  57
onClickFindBeer()

Dodaj do aktywności metodę onClickFindBeer() ¨  Utworzenie projektu


¨  Aktualizacja układu
Metoda onClickFindBeer() musi mieć ściśle określoną sygnaturę, gdyż
¨  Połączenie aktywności
¨  Implementacja logiki
w przeciwnym razie nie zostanie wywołana po kliknięciu przycisku wyświetlonego
w układzie. Metoda musi wyglądać dokładnie tak jak pokazaliśmy poniżej:

public void onClickFindBeer(View view) {


}
Metoda musi być Metoda musi zwracać Metoda musi mieć jeden
publiczna. wartość typu void. parametr typu View.

Jeśli metoda nie będzie miała powyższej postaci, to nie zostanie wywołana, Jeśli chcesz, aby
gdy użytkownik dotknie przycisku. Stanie się tak dlatego, że za kulisami
Android będzie poszukiwał publicznej metody zwracającej wartość typu metoda odpowiadała
void, której nazwa odpowiada wartości podanej w kodzie XML układu.
na kliknięcia przycisku,
Z pozoru można przypuszczać, że parametr View tej metody jest musi być metodą
niepotrzebny, niemniej jednak jego istnienie jest uzasadnione. Otóż
parametr ten odwołuje się do komponentu GUI, który doprowadził do publiczną, zwracać
wywołania metody (w naszym przypadku komponentem tym jest przycisk).
Jak już wspominaliśmy, elementy graficznego interfejsu użytkownika, takie
wartość typu void
jak przyciski lub widoki tekstowe, są obiektami klasy View. i mieć jeden parametr
Zmodyfikujmy zatem kod naszej aktywności. Dodaj do niej metodę typu View.
onClickFindBeer():

Używamy tej klasy,


więc musimy ją ...
zaimportować. import android.view.View;
DoradcaPiwny

public class FindBeerActivity extends Activity {


app/src/main
...
Dodaj metodę // Metoda wywoływana, gdy użytkownik kliknie przycisk
onClickFindBeer java
do klasy public void onClickFindBeer(View view) {
FindBeerActivity.java.
} com.hfad.doradcapiwny
}

FIndBeerActivity.java
<Layout>
onClickFindBeer()
</Layout>

FindBeerActivity.java
activity_find_beer.xml

58 Rozdział 2.
Tworzenie interaktywnych aplikacji

Metoda onClickFindBeer() musi coś robić ¨  Utworzenie projektu


¨  Aktualizacja układu
Skoro dodaliśmy do aktywności metodę onClickFindBerr(), kolejnym zadaniem, ¨  Połączenie aktywności
które przed nami stoi, jest sprawienie, by metoda ta coś robiła. Naszym celem jest ¨  Implementacja logiki
wyświetlenie w aplikacji listy różnych gatunków piwa, które odpowiadają rodzajowi
wybranemu przez użytkownika.

Aby to zrobić, w pierwszej kolejności musimy pobrać referencje do dwóch


komponentów GUI: rozwijanej listy i widoku tekstowego. Dzięki temu będziemy
mogli zarówno pobrać wartość wybraną przez użytkownika na rozwijanej liście,
jak i wyświetlić tekst w widoku tekstowym.

Zastosuj metodę findViewById(),


by pobrać referencję do widoku
Referencje do dwóch interesujących nas komponentów GUI można pobrać przy
użyciu metody findViewById(). Metoda ta pobiera jako parametr identyfikator
komponentu GUI i zwraca obiekt klasy View. Ten zwrócony obiekt należy następnie
rzutować do odpowiedniego typu komponentu (na przykład TextView lub Button).

Poniżej pokazaliśmy, w jaki sposób możemy użyć metody findViewById(), żeby


pobrać referencję widoku tekstowego o identyfikatorze brands: Interesuje nas widok
o identyfikatorze brands.
Przyjrzyj się dokładnie, w jaki sposób określiliśmy identyfikator widoku tekstowego.

TextView brands = (TextView) findViewById(R.id.brands);

brands to widok tekstowy, więc musisz


rzutować zwrócony wynik do tego typu.
R jest specjalną klasą
Zamiast przekazywać jego nazwę przekazaliśmy identyfikator o postaci Javy umożliwiającą
R.id.brands. Ale co to oznacza? I czym jest R?
R.java to specjalny plik Javy generowany przez narzędzia Android SDK za każdym
pobieranie odwołań
razem, gdy jest budowana aplikacja. Plik ten jest zapisywany w katalogu app/build/ do różnych zasobów
generated/source/r/debug projektu i należy do tego samego pakietu co aplikacja.
Android używa go do zarządzania wszystkimi zasobami używanymi w aplikacji, aplikacji.
a jego zawartość pozwala nam między innymi na odwoływanie się do komponentów
GUI z poziomu kodu aktywności.
Jeśli wyświetlisz kod pliku R.java, to przekonasz się, że zawiera on grupę klas Plik R.java jest
generowany
Spokojnie
wewnętrznych — po jednej klasie dla każdego typu zasobów. W każdej z tych klas
znajdują się pola odpowiadające wszystkim zasobom danego typu. Na przykład automatycznie.
klasa R zawiera klasę wewnętrzną o nazwie id, która z kolei definiuje wartość
Sami nie musimy
static final brands. Poniższe wywołanie:
niczego zmieniać w pliku R.java,
(TextView) findViewById(R.id.brands); warto jednak wiedzieć o jego
istnieniu.
używa tej wartości, by odwołać się do komponentu brands.

jesteś tutaj  59
Metody klasy View

Dysponując obiektem View, ¨  Utworzenie projektu


¨  Aktualizacja układu
można odwoływać się do jego metod ¨  Połączenie aktywności
¨  Implementacja logiki
Metoda findViewById() zwraca obiekt reprezentujący komponent GUI. Oznacza
to, że można używać metod udostępnianych przez daną klasę Javy do pobierania
i ustawiania właściwości komponentu GUI. Przyjrzyjmy się temu nieco dokładniej.

Określanie tekstu wyświetlanego w komponencie TextView


Jak wiadomo, referencję do widoku tekstowego można pobrać, używając
następującego kodu:

TextView brands = (TextView) findViewById(R.id.brands);

Wykonanie tego kodu powoduje utworzenie obiektu TextView o nazwie brands;


jednocześnie zyskujemy możliwość wywoływania metod tego obiektu.

Załóżmy, że chcesz wyświetlić w widoku tekstowym tekst „Utelka iwa”.


Moglibyśmy to zrobić w następujący sposób:

brands.setText(”Utelka iwa”); Ustawiamy tekst w komponencie


TextView na „Utelka iwa”.

Pobranie wartości wybranej w komponencie Spinner


Referencję do listy rozwijanej można pobrać w taki sam sposób jak do widoku
tekstowego. Także w tym przypadku posłużymy się metodą findViewById(),
ale zwrócony wynik rzutujemy na typ Spinner:

Spinner color = (Spinner) findViewById(R.id.color);

W ten sposób uzyskamy obiekt typu Spinner i będziemy mogli korzystać z jego metod.
Poniższy przykład pokazuje, jak można pobrać aktualnie wybraną wartość komponentu
i skonwertować ją na łańcuch znaków:

String.valueOf(color.getSelectedItem()) To wywołanie pobiera z rozwijanej listy


aktualnie wybraną wartość i konwertuje
ją na łańcuch znaków.
Wywołanie o postaci:

color.getSelectedItem()

zwraca w rzeczywistości ogólny obiekt Javy. Wynika to z faktu, że zawartością


rozwijanych list mogą być nie tylko łańcuchy znaków, lecz także na przykład obrazki.
W naszym przypadku wiemy, że są to łańcuchy znaków, więc możemy wywołać metodę
String.valueOf(), aby skonwertować Object na String.

60 Rozdział 2.
Tworzenie interaktywnych aplikacji

Aktualizacja kodu aktywności


Teraz już wiesz dostatecznie dużo, by napisać kod metody onClickFindBeer().
Zamiast pisać ten kod w jednym wierszu zacznijmy od odczytania wartości wybranej
na liście rozwijanej, a następnie wyświetlmy ją w widoku tekstowym.

Magnesiki aktywności
Ktoś napisał nową wersję metody onClickFindBeer(), którą możesz dodać
do swojej aktywności, używając do tego celu magnesików przyczepionych
na lodówce. Niestety tajemnicza kuchenna trąba powietrzna oderwała kilka
magnesików. Czy potrafisz przyczepić je z powrotem we właściwych miejscach?

Kod musi pobierać rodzaj piwa wybrany na liście rozwijanej, a następnie


wyświetlić go w widoku tekstowym.

// Metoda wywoływana, gdy użytkownik kliknie przycisk


public void onClickFindBeer(. .......... view) {

// Pobiera referencję komponentu TextView


........ brands = .......... .............. (. ...............);

// Pobiera referencję komponentu Spinner


Spinner ............. = ............ .................(................);

// Pobiera wartość wybraną w komponencie Spinner


String ............ = String.valueOf(color. ..................);

// Wyświetla wybraną wartość


brands. ................(beerType);
}

setText
findViewById R.id.color
TextView color Nie musisz
używać
R.view.brands wszystkich
R.id.brands magnesików.
(TextView) findView

Button R.view.color
findView View findViewById
(Spinner)
getSelectedItem()
beerType
jesteś tutaj  61
Magnesiki — rozwiązanie

Magnesiki aktywności. Rozwiązanie


Ktoś napisał nową wersję metody onClickFindBeer(), którą możesz dodać
do swojej aktywności, używając do tego celu magnesików przyczepionych
na lodówce. Niestety tajemnicza kuchenna trąba powietrzna oderwała kilka
magnesików. Czy potrafisz przyczepić je z powrotem we właściwych miejscach?

Kod musi pobierać rodzaj piwa wybrany na liście rozwijanej, a następnie


wyświetlić go w widoku tekstowym.

// Metoda wywoływana, gdy użytkownik kliknie przycisk


public void onClickFindBeer(. View .. view) {

// Pobiera referencję komponentu TextView


TextView . brands = . (TextView) findViewById ( R.id.brands .);

// Pobiera referencję komponentu Spinner


Spinner . color .. = (Spinner) findViewById ( R.id.color .);

// Pobiera wartość wybraną w komponencie Spinner


String beerType = String.valueOf(color. getSelectedItem() );

// Wyświetla wybraną wartość


brands. . setText ...(beerType);
}

R.view.brands Tych magnesików


nie musiałeś używać.

findView
Button
R.view.color

findView

62 Rozdział 2.
Tworzenie interaktywnych aplikacji

Pierwsza wersja aktywności ¨  Utworzenie projektu


¨  Aktualizacja układu
Nasz chytry plan zakłada, by kod aktywności pisać etapami i po każdym z nich go
¨  Połączenie aktywności
¨  Implementacja logiki
testować. Na samym końcu aktywność będzie pobierać wartość wybraną z rozwijanej
listy, wywoływać metodę napisanej przez nas klasy, a następnie wyświetlać dobrane
gatunki piwa. W przypadku pierwszej wersji aktywności chcemy jedynie upewnić się,
że prawidłowo pobraliśmy wartość wybraną z rozwijanej listy.

Poniżej przedstawiliśmy kod aktywności, włącznie z metodą, którą uzupełniłeś na


poprzedniej stronie. Wprowadź te zmiany w kodzie pliku FindBeerActivity.java,
a następnie go zapisz:

package com.hfad.doradcapiwny;
DoradcaPiwny

import android.os.Bundle;
import android.app.Activity; app/src/main

import android.view.View;
java
import android.widget.Spinner; Używamy tych
dodatkowych klas.
import android.widget.TextView;
com.hfad.doradcapiwny

public class FindBeerActivity extends Activity {


FIndBeerActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) { Tej metody nie musimy zmieniać.
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_beer);
}

// Metoda wywoływana, gdy użytkownik kliknie przycisk


public void onClickFindBeer( View view) {
// Pobiera referencję komponentu TextView
TextView brands = (TextView) findViewById (R.id.brands); Metoda findViewById()
zwraca obiekt View,
// Pobiera referencję komponentu Spinner musimy go zatem rzutować
Spinner color = (Spinner) findViewById(R.id.color); na odpowiedni typ.
// Pobiera wartość wybraną w komponencie Spinner
String beerType = String.valueOf(color.getSelectedItem() );
// Wyświetla wybraną wartość
brands. setText(beerType); Metoda getSelectedItem()
zwraca wynik typu Object,
} musimy zamienić go na String.
}

jesteś tutaj  63
Co się dzieje?

Co ten kod robi? ¨  Utworzenie projektu


¨  Aktualizacja układu
Zanim weźmiemy nasz kod na jazdę próbną, spróbujmy zrozumieć,
¨  Połączenie aktywności
¨  Implementacja logiki
co on tak naprawdę robi.

1 Użytkownik wybiera rodzaj piwa z rozwijanej listy i klika przycisk Odszukaj piwo!.
Powoduje to wywołanie metody public void onClickFindBeer(View) zdefiniowanej w aktywności.
Układ określa, którą metodę aktywności należy wywołać w reakcji na kliknięcie przycisku przy użyciu
właściwości android:onClick elementu <Button>.

<Layout>

</Layout>

Układ FindBeerActivity

2 Aktywność pobiera referencje do komponentów TextView i Spinner, wywołując w tym celu


metodę findViewById().

komponent Spinner

FindBeerActivity

komponent TextView

3 Aktywność odczytuje wartość wybraną w komponencie Spinner i konwertuje ją na łańcuch znaków.

bursztynowe
FindBeerActivity komponent
Spinner

4 Aktywność określa wartość właściwości text komponentu TextView tak, by odpowiadała ona
aktualnie wybranemu rodzajowi piwa.

"bursztynowe"

FindBeerActivity komponent TextView

64 Rozdział 2.
Tworzenie interaktywnych aplikacji

Jazda próbna — test modyfikacji


Wprowadź wszystkie zmiany w kodzie aktywności, następnie
zapisz plik i uruchom aplikację. Tym razem kiedy klikniesz
przycisk Odszukaj piwo!, aplikacja wyświetli wartość,
która w danej chwili będzie wybrana z rozwijanej listy.

piwa jest
Wybrany rodzaj doku
wyświetlany w wi
tekstowym.

Nie istnieją
głupie pytania

P : Dodałem nowy łańcuch znaków do pliku strings.xml,  P: W tym przypadku? A nie zawsze?
ale nie mogę go znaleźć w pliku R.java. Dlaczego go tam 
nie ma? O: Rozwijanych list można używać do bardziej złożonych rzeczy
niż wyświetlanie tekstów. Na przykład można na nich wyświetlać
O: Android Studio generuje plik R.java w momencie zapisywania ikonę obok tekstu. Fakt, że metoda getSelectedItem() zwraca
zmian wprowadzonych w projekcie. Jeśli dodałeś zasoby, lecz nie wynik typu Object, zapewnia większą elastyczność.
widzisz ich w pliku R.java, to sprawdź, czy zmiany zostały zapisane.
Plik R.java jest także aktualizowany w momencie budowania P: Czy nazwa metody onClickFindBeer ma znaczenie?
aplikacji. Aplikacja jest budowana przed jej uruchomieniem,
a zatem także uruchomienie aplikacji powoduje zaktualizowanie
O: Znaczenie ma tylko to, by nazwa metody w kodzie aktywności
odpowiadała nazwie podanej w atrybucie onClick przycisku
pliku R.java.
w kodzie układu.
P: Wygląda na to, że wartości wyświetlane w rozwijanej  P: Dlaczego zastąpiliśmy kod aktywności wygenerowany 
liście są statyczne, gdyż są to łańcuchy podane w zasobie 
przez Android Studio?
string-array. Czy można te wartości modyfikować 
programowo? O: Zintegrowane środowiska programistyczne, takie jak Android
O: Owszem, można, choć jest to bardziej złożone niż użycie Studio, zawierają wiele funkcji i narzędzi umożliwiających
programistom oszczędzanie czasu. Generują za nas dużo kodu, co
wartości statycznych. W dalszej części książki pokażemy,
w jaki sposób można uzyskać pełną kontrolę nad wartościami czasami jest bardzo użyteczne. Uważamy jednak, że podczas nauki
wyświetlanymi w komponentach, w tym także na rozwijanych nowego języka lub nowego obszaru zagadnień programistycznych,
listach. takich jak tworzenie aplikacji na Androida, lepiej jest poznawać
podstawy języka, a nie kod generowany przez IDE. Dzięki temu
P: Jaki jest typ obiektu zwracanego przez metodę  można lepiej zrozumieć kod, którego następnie będzie można
używać niezależnie od stosowanego IDE.
getSelectedItem()?

O: Jest on zadeklarowany jako Object. Ponieważ wartości


zostały określone przy użyciu zasobu string-array, w tym
przypadku faktycznym typem zwracanej wartości jest String.

jesteś tutaj  65
Klasa BeerExpert

Tworzenie własnej klasy Javy <Layout>

Zgodnie z tym, co sygnalizowaliśmy już na samym początku rozdziału, nasza </Layout> <resources>

aplikacja Doradca piwny wyświetla rekomendowane gatunki piwa, używając


Układ </resources>
napisanej przez nas niestandardowej klasy języka Java. Jest to najzwyklejsza
klasa Javy, która nic nie wie o tym, że jest używana w aplikacji działającej strings.xml
w systemie Android.

Specyfikacja własnej klasy Javy


Nasza klasa musi spełniać następujące wymagania: Aktywność

 Musi należeć do pakietu o nazwie com.hfad.doradcapiwny. Musimy napisać


klasę, której
 Musi mieć nazwę BeerExpert. aktywność będzie
mogła używać
do odnajdywania
 Ma udostępniać jedną metodę, getBrands(), pobierającą preferowany gatunków piwa
odpowiadających
kolor piwa (określony jako łańcuch znaków), i zwracać obiekt typu BeerExpert rodzajowi
List<String>, listę zawierającą sugerowane gatunki piwa. wybranemu przez
użytkownika.

Implementacja i testowanie klasy


DoradcaPiwny
Klasy Javy mogą być niesłychanie złożone i korzystać z wywołań metod
implementujących skomplikowaną logikę aplikacji. Możesz bądź to napisać
app/src/main
swoją własną wersję tej klasy, bądź też skorzystać z naszej, bardzo wyszukanej
wersji, przedstawionej poniżej:
java
package com.hfad.doradcapiwny;
import java.util.ArrayList; com.hfad.doradcapiwny
import java.util.List; To zwyczajny kod napisany
w Javie, nie ma nic
wspólnego z Androidem. BeerExpert.java
public class BeerExpert {
List<String> getBrands(String color) {
List<String> brands = new ArrayList<String>();
if (color.equals(”bursztynowe”)) { Zrób to sam!
brands.add(”Jack Amber”);
brands.add(”Red Moose”);
} else {
brands.add(”Jail Pale Ale”); Dodaj do projektu klasę BeerExpert. Zaznacz 
brands.add(”Gout Stout”); pakiet com.hfad.doradcapiwny w katalogu app/
} src/main/java i wybierz opcję File/New.../Java
return brands; Class. W efekcie zostanie utworzona nowa 
} klasa należąca do wybranego pakietu.
}

66 Rozdział 2.
Tworzenie interaktywnych aplikacji

Dodaj do aktywności wywołanie metody naszej klasy, ¨  Utworzenie projektu


¨  Aktualizacja układu
aby była wyświetlana FAKTYCZNA porada ¨  Połączenie aktywności
¨  Implementacja logiki
W drugiej wersji kodu aktywności musimy rozszerzyć kod metody onClickFindBeer()
o wywołanie metody klasy BeerExpert i zastosowanie zwróconych przez nią rekomendacji.
Te zmiany wiążą się z zastosowaniem zwyczajnego kodu napisanego w Javie. Możesz
spróbować samodzielnie napisać ten kod i wypróbować jego działanie, uruchamiając
aplikację, bądź też możesz odwrócić kartkę i przeanalizować nasze rozwiązanie.

Zaostrz ołówek
Rozszerz kod aktywności tak, by wywoływał metodę getBrands()
klasy BeerExpert i wyświetlał wyniki w widoku tekstowym.

package com.hfad.doradcapiwny;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.List; Ten wiersz kodu dodaliśmy za Ciebie.

public class FindBeerActivity extends Activity {


private BeerExpert expert = new BeerExpert(); Musisz użyć klasy
... BeerExpert, by pobrać
rekomendowane gatunki piwa,
// Metoda wywoływana, gdy użytkownik kliknie przycisk zatem dodaliśmy także ten
public void onClickFindBeer( View view) { wiersz kodu.
// Pobiera referencję komponentu TextView
TextView brands = (TextView) findViewById (R.id.brands);
// Pobiera referencję komponentu Spinner
Spinner color = (Spinner) findViewById(R.id.color);
// Pobiera wartość wybraną w komponencie Spinner
String beerType = String.valueOf(color. getSelectedItem() );

} Twoim zadaniem jest zaktualizowa


} metody onClickFindBeer(). nie kodu

jesteś tutaj  67
Rozwiązanie zadania

Zaostrz ołówek
Rozwiązanie Rozszerz kod aktywności tak, by wywoływał metodę getBrands()
klasy BeerExpert i wyświetlał wyniki w widoku tekstowym.

package com.hfad.doradcapiwny;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.List;

public class FindBeerActivity extends Activity {


private BeerExpert expert = new BeerExpert();
...

// Metoda wywoływana, gdy użytkownik kliknie przycisk


public void onClickFindBeer( View view) {
// Pobiera referencję komponentu TextView
TextView brands = (TextView) findViewById (R.id.brands);
// Pobiera referencję komponentu Spinner
Spinner color = (Spinner) findViewById(R.id.color);
// Pobiera wartość wybraną w komponencie Spinner
String beerType = String.valueOf(color. getSelectedItem() );

// pobranie rekomendacji z klasy BeerExpert

List<String> brandsList = expert.getBrands(beerType); Pobiera listę


sugerowanych gatunków.
StringBuilder brandsFormatted = new StringBuilder(); Konstruuje łańcuch znaków,
w którym zostaną zapisane
wartości z listy.
for (String brand : brandsList) {

brandsFormatted.append(brand).append( ‘\n’); Wyświetla każdy


gatunek piwa
w osobnym wierszu.
}

// wyświetlenie wyników
Wyświetla wyniki
brands.setText(brandsFormatted); w widoku tekstowym.

}
} wymaga jedynie zwyczajnego
Zastosowanie klasy BeerExpert nie musisz się przejmować,
sane go w Javi e, dlate go
kodu napi lądać nieco inaczej niż nasze.
jeśli Twoje rozw iąza nie będz ie wyg

68 Rozdział 2.
Tworzenie interaktywnych aplikacji

Kod aktywności, wersja 2 ¨  Utworzenie projektu


¨  Aktualizacja układu
Poniżej zamieściliśmy pełną wersję kodu aktywności. Wprowadź zmiany w swojej
¨  Połączenie aktywności
¨  Implementacja logiki
wersji pliku FindBeerActivity.java. Upewnij się, że dodałeś do projektu klasę
BeerExpert, a następnie zapisz wszystkie zmiany:

package com.hfad.doradcapiwny;
DoradcaPiwny
import android.os.Bundle;
import android.app.Activity; app/src/main
import android.view.Menu;
import android.view.View; java
import android.widget.Spinner;
import android.widget.TextView; com.hfad.doradcapiwny
import java.util.List; Używamy tej dodatkowej klasy.

FIndBeerActivity.java
public class FindBeerActivity extends Activity {
private BeerExpert expert = new BeerExpert();

Dodaj instancję klasy BeerExpert jako prywatne pole klasy.


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_beer);
}

// Metoda wywoływana, gdy użytkownik kliknie przycisk


public void onClickFindBeer( View view) {
// Pobiera referencję komponentu TextView
TextView brands = (TextView) findViewById (R.id.brands);
// Pobiera referencję komponentu Spinner
Spinner color = (Spinner) findViewById(R.id.color);
// Pobiera wartość wybraną w komponencie Spinner
String beerType = String.valueOf(color. getSelectedItem() );
// Pobranie rekomendacji z klasy BeerExpert Użyj klasy BeerExpert do
List<String> brandsList = expert.getBrands(beerType); pobrania listy sugerowanych
gatunków piwa.
StringBuilder brandsFormatted = new StringBuilder();
for (String brand : brandsList) {
Skonstruuj łańcuch znaków,
brandsFormatted.append(brand).append(‘\n’); który wyświetli każdy
} gatunek w nowym wierszu.
// Wyświetlenie wyników
brands.setText(brandsFormatted); Wyświetl skonstruowany łańcuch znaków
} w widoku tekstowym.
}

jesteś tutaj  69
Co się dzieje?

Co się dzieje podczas wykonywania tego kodu?


1 Kiedy użytkownik klika przycisk Odszukaj piwo!, zostaje wywołana
metoda onClickFindBeer() aktywności.
Metoda ta tworzy referencje do listy rozwijanej i widoku tekstowego,
a następnie pobiera wartość wybraną w liście.

<Layout>

</Layout> bursztynowe komponent


FindBeerActivity Spinner
Układ
komponent
TextView

2 Metoda onClickFindBeer() wywołuje metodę getBrands() klasy BeerExpert,


przekazując do niej wartość wybraną w komponencie Spinner.
Metoda getBrands() zwraca listę sugerowanych gatunków piwa.

getBrands("bursztynowe")

"Jack Amber"
FindBeerActivity "Red Moose" BeerExpert

3 Metoda onClickFindBeer() formatuje listę gatunków piwa i zapisuje ją


we właściwości text komponentu TextView.

"Jack Amber
Red Moose"

FindBeerActivity komponent
TextView

70 Rozdział 2.
Tworzenie interaktywnych aplikacji

Jazda próbna — test aplikacji ¨  Utworzenie projektu


¨  Aktualizacja układu
Kiedy już wprowadzisz w aplikacji zmiany przedstawione na poprzedniej stronie,
¨  Połączenie aktywności
¨  Implementacja logiki
uruchom ją i przetestuj. Spróbuj wybierać różne rodzaje piwa i za każdym razem
klikaj przycisk Odszukaj piwo!.

Takie wyniki uzyskasz


po wybraniu opcji „jasne”.

Takie wyniki uzyskasz


po wybraniu opcji
„bursztynowe”.

Po wybraniu rodzaju piwa i kliknięciu przycisku


Odszukaj piwo!, aplikacja użyje klasy BeerExpert do
określenia i wyświetlenia sugerowanych gatunków piwa.

jesteś tutaj  71
Przybornik

Twój przybornik do Androida


Rozdział 2.

Opanowałeś już rozdział 2. i dodałeś  j
Pełny kod przykładowe
do swojego przybornika z narzędziami  aplikacji prezen tow ane j w  tym
umiejętności tworzenia interaktywnych  rozdziale mo żes z pob rać
aplikacji na Androida. nictwa
z serwera FTP wydaw
.helion .pl/
Helion: ftp://ftp
przyklady/an drrg.z ip

    CELNE SPOSTRZEŻENIA
 Element Button pozwala dodawać przyciski.
 Element Spinner umożliwia dodawanie pola, którego kliknięcie powoduje wyświetlenie listy wartości.
 Wszystkie komponenty GUI (graficznego interfejsu użytkownika) są rodzajami widoków.
Wszystkie one dziedziczą po klasie View.
 Tablicę łańcuchów znaków można dodawać, używając następującego zapisu:
<string-array name=”tablica”>
<item>łańcuch1</item>
...
</string-array>

 Do zasobu string-array można odwołać się w układzie, używając zapisu:


”@array/nazwa_tablicy”

 Aby kliknięcie przycisku wywołało metodę, należy dodać do jego kodu w układzie następujący atrybut:
android:onClick=”metodaClick”

Oprócz tego w aktywności należy zaimplementować metodę o podanej nazwie:


public void metodaClick(View view) {
}
 Plik R.java jest generowany automatycznie. Pozwala on pobierać w kodzie Javy referencje do układów,
komponentów GUI, łańcuchów znaków oraz wszelkich innych zasobów.
 Metoda findViewById() zwraca referencję do widoku.
 Metoda setText() pozwala określić tekst wyświetlany w widoku.
 Metoda getSelectedItem() zwraca wartość, która jest aktualnie wybrana z rozwijanej listy.
 Nowe klasy można dodawać do projektu, wybierając z menu opcję File/New.../Java Class.

72 Rozdział 2.
3. Wiele aktywności i intencji

Jakie są Twoje intencje?


Wysłałam intencję w sprawie obsługi
mojej ACTION_CALL i dostałam
do wyboru oferty przeróżnych
aktywności.

Większość aplikacji potrzebuje więcej niż jednej aktywności.


Dotychczas mieliśmy do czynienia z aplikacjami składającymi się tylko z jednej aktywności. Kiedy
jednak sprawy się komplikują, jedna aktywność zwyczajnie nie wystarczy. Dlatego w tym rozdziale
pokażemy Ci, jak tworzyć aplikacje składające się z wielu aktywności i jak nasze aplikacje
mogą porozumiewać się z innymi, wykorzystując w tym celu intencje. Pokażemy także, jak można
używać intencji, by wykraczać poza granice naszych aplikacji, i jak wykorzystywać aktywności
należące do innych aplikacji dostępnych w urządzeniu do wykonywania akcji. To wszystko
zapewni nam znacznie większe możliwości.

to jest nowy rozdział  73


Zadania

Aplikacja może zawierać więcej niż jedną aktywność


Wcześniej w tej książce napisaliśmy, że aktywność jest jedną, dobrze zdefiniowaną Aktywność jest
operacją, którą użytkownik może wykonywać w aplikacji, taką jak na przykład
wyświetlanie listy receptur. Jeśli aplikacja jest w miarę prosta, to taka jedna aktywność
jedną, konkretnie
może w zupełności wystarczyć. określoną operacją,
którą użytkownik
Jednak w bardzo wielu przypadkach użytkownik będzie chciał robić więcej niż jedną
rzecz — na przykład oprócz wyświetlania listy receptur będzie także chciał je dodawać. może wykonywać.
W takich przypadkach konieczne będzie zastosowanie więcej niż jednej aktywności: Połączenie większej
jednej do wyświetlania listy receptur i drugiej do dodawania nowych receptur.
liczby aktywności
Najlepszym sposobem, by zrozumieć, o co tu chodzi, jest przeanalizowanie w celu wykonania
odpowiedniego przykładu w działaniu. W tym rozdziale napiszemy aplikację składającą
czegoś bardziej
się z dwóch aktywności. Pierwsza z nich będzie umożliwiała wpisanie wiadomości.
Kliknięcie przycisku wyświetlanego w pierwszej aktywności spowoduje uruchomienie złożonego
drugiej aktywności i przekazanie do niej treści wiadomości. Ta druga aktywność nazywamy zadaniem.
wyświetli wiadomość.

Pierwsza aktywność Kiedy klikniesz przycisk


umożliwia wpisanie Wyślij wiadomość
wiadomości. wyświetlony w pierwszej
aktywności, treść
wiadomości zostanie
przekazana do drugiej
aktywności. Następnie
druga aktywność
zostanie wyświetlona na
pierwszej i wyświetli na
ekranie przekazaną treść
wiadomości.

Oto czynności, które wykonasz:


1 Utworzysz prostą aplikację z jedną aktywnością i układem.

2 Dodasz do aplikacji drugą aktywność i układ.

3 Sprawisz, że pierwsza aktywność wywoła drugą.

4 Przekażesz dane z pierwszej aktywności do drugiej.

74 Rozdział 3.
Wiele aktywności i intencji

Oto struktura naszej aplikacji


Aplikacja składa się z dwóch aktywności i dwóch układów:

1 Podczas uruchamiania aplikacji zostanie wykonana aktywność


CreateMessageActivity.
Ta aktywność używa układu o nazwie activity_create_message.xml.

2 Użytkownik klika przycisk wyświetlony w aktywności CreateMessageActivity.


To kliknięcie powoduje uruchomienie aktywności ReceiveMessageActivity,
która z kolei używa układu activity_receive_message.xml.

<Layout> <Layout>

</Layout> </Layout>

activity_create_message.xml activity_receive_message.xml
Teskt wpisany w aktywności
CreateMessageActivity
zostaje przekazany
do aktywności
1 ReceiveMessageActivity.

Urządzenie CreateMessageActivity.java ReceiveMessageActivity.java

Utworzenie projektu ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
¨  Wywołanie drugiej aktywności
Projekt tej aplikacji możesz utworzyć w dokładnie taki sam sposób, ¨  Przekazanie danych
w jaki tworzyłeś projekty w dwóch poprzednich rozdziałach. Utwórz
w Android Studio nowy projekt aplikacji o nazwie Komunikator
i użyj przy tym pakietu o nazwie com.hfad.komunikator. Jako
minimalny poziom API wybierz API poziomu 15, tak by aplikacja
mogła działać na większości urządzeń. Aby Twoja aplikacja
odpowiadała naszej, przedstawionej w tej książce, będziesz także
potrzebować pustej aktywności o nazwie CreateMessageActivity
i układu o nazwie activity_create_message.

Na następnej stronie zajmiemy się aktualizacją układu aplikacji.

jesteś tutaj  75
Aktualizacja układu

Aktualizacja układu Komunikator

Poniżej przedstawiliśmy kod XML umieszczony w pliku activity_create_message.xml. app/src/main


Usunęliśmy z niego element <TextView> umieszczony tam przez Android Studio
i zastąpiliśmy go dwoma nowymi elementami: <Button> i <EditText>. Element res
<EditText> tworzy pole tekstowe, którego można używać do wpisywania danych.
layout
Zmień swój plik activity_create_message.xml, tak by zawierał poniższy kod XML: <xml>
</xml>

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android” activity_create_


message.xml
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
tools:context=”.CreateMessageActivity” >

<Button android:id=”@+id/send”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
Zastąp android:layout_alignParentLeft=”true”
element
<TextView> android:layout_alignParentTop=”true”
wygenerowany
przez Android android:layout_marginLeft=”36dp”
Studio android:layout_marginTop=”21dp”
elementami Kliknięcie przycisku spowoduje wywołanie
<Button> android:onClick=”onSendMessage” metody onSendMessage() zdefiniowanej
i <EditText>. w aktywności.
android:text=”@string/send” />
To jest zasób łańcuchowy.
<EditText android:id=”@+id/message”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” Element <EditText> definiuje
android:layout_alignLeft=”@+id/send” pole tekstowe służące
android:layout_below=”@+id/send” do wpisywania danych.
android:layout_marginTop=”18dp”
Dziedziczy on po tej samej
android:ems=”10” >
</EditText> klasie View co wszystkie inne
Ten atrybut określa, jak szerokie powinno
być pole <EditText>. W tym przypadku
komponenty GUI, które
</RelativeLayout> jego wielkość powinna wystarczyć do
zapisania 10 wielkich liter „M”.
poznaliśmy do tej pory.

76 Rozdział 3.
Wiele aktywności i intencji

Aktualizacja pliku strings.xml… ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
¨  Wywołanie drugiej aktywności
Tekst prezentowany na przycisku dodanym do układu jest pobierany z zasobu ¨  Przekazanie danych
@string/send. Oznacza to, że musisz dodać zasób o nazwie send do pliku
strings.xml i określić jego wartość. To właśnie ta wartość będzie tekstem,
który zostanie wyświetlony na przycisku. Zrób to teraz:
Komunikator
...
<string name=”send”>Wyślij wiadomość</string> app/src/main
Dodaj nowy zasób
... łańcuchowy o nazwie
send. W naszej aplikacji res
zapisaliśmy w nim
łańcuch znaków „Wyślij
…i dodanie metody do kodu aktywności wiadomość”, dlatego
właśnie ten tekst
values
<xml>
Poniższy atrybut umieszczony w elemencie <Button>: zostanie wyświetlony </xml>
na przycisku. strings.xml
android:onClick=”onSendMessage”
oznacza, że kliknięcie przycisku spowoduje wywołanie metody
onSendMessage() zdefiniowanej w aktywności. Musimy ją zatem dodać.

Otwórz plik CreateMessageActivity.java i zastąp kod wygenerowany przez


Android Studio poniższym kodem:
package com.hfad.komunikator;
Zastąpiliśmy kod wygenerowany
przez Android Studio, gdyż jego
import android.app.Activity; przeważająca część nie jest
import android.os.Bundle; nam do niczego potrzebna.

import android.view.View;

public class CreateMessageActivity extends Activity { Komunikator


ana
Metoda onCreate() jest wywoływ
za pierwszym razem, gdy jest app/src/main
@Override tworzona aktywność.
protected void onCreate(Bundle savedInstanceState) { java
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message); com.hfad.komunikator
}
CreateMessage
// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku Activity.java

public void onSendMessage(View view) { Ta metoda zostanie wywołana po kliknięciu


} przycisku. Jej kod uzupełnimy w trakcie
dalszych prac nad aplikacją.
}

Skoro załatwiliśmy sprawę pierwszej aktywności, przejdźmy teraz


do drugiej.
jesteś tutaj  77
Utworzenie aktywności

¨  Utworzenie pierwszej aktywności
Utworzenie drugiej aktywności i układu ¨  Utworzenie drugiej aktywności
¨  Wywołanie drugiej aktywności
Android Studio udostępnia kreator umożliwiający dodawanie do aplikacji ¨  Przekazanie danych
kolejnych aktywności i układów. Stanowi on uproszczoną wersję kreatora nowych
aplikacji i możemy go używać za każdym razem, gdy chcemy dodać do aplikacji
nową aktywność.

Aby utworzyć nową aktywność, wybierz z menu głównego Android Studio opcję
File/New/Activity, a następnie wybierz opcję Blank Activity. W efekcie na ekranie
zostanie wyświetlone okno, w którym będziesz mógł podać dane dotyczące tworzonej
aktywności.

Tworząc nowe aktywności i układy, należy określić ich nazwy. W tym przypadku
aktywności nadaj nazwę ReceiveMessageActivity, a układowi — activity_receive_
message. Upewnij się, czy oba pliki znajdą się w pakiecie com.hfad.komunikator.
W pozostałych polach zaakceptuj wartości domyślne, a następnie kliknij przycisk Finish.

Aktywności nadaj nazwę „ReceiveMessage


Activity”,
a układowi „activity_receive_message”.

W pozostałych
polach zaakceptuj
wartości
domyślne, gdyż
interesuje nas
utworzenie nowej
aktywności
i nowego układu.
Większość kodu
wygenerowanego
przez Android
Studio usuniemy
i zastąpimy
własnym.

78 Rozdział 3.
Wiele aktywności i intencji

Co się właściwie stało?


Kiedy kliknąłeś przycisk Finish, Andorid Studio wygenerowało
dla Ciebie lśniący nowością plik aktywności i równie nowiutki
plik układu. Jeśli spojrzysz do eksploratora, przekonasz
się, że w katalogu app/src/main/java pojawił się plik
ReceiveMessageActivity.java, a w katalogu app/src/main/res/layout
— plik activity_receive_message.xml.

To są dwa nowe, utworzone przed chwilą pliki: nowa


aktywność i jej układ. Teraz w aplikacji znajdują się
już dwie aktywności i dwa układy.

Każda aktywność używa innego układu. Pierwsza aktywność,


CreateMessageActivity, używa układu activity_create_
message.xml, natomiast druga, ReceiveMessageActivity
— układu activity_receive_message.xml.

<Layout> <Layout>

</Layout> </Layout>

activity_create_message.xml activity_receive_message.xml

CreateMessageActivity.java RecieveMessageActivity.java

Za kulisami Android Studio wprowadziło także niezbędne


zmiany w pliku konfiguracyjnym aplikacji — AndroidMainfest.xml.
Przyjrzyjmy się mu zatem nieco dokładniej.

jesteś tutaj  79
AndroidManifest.xml

¨  Utworzenie pierwszej aktywności
Przedstawiamy plik manifestu aplikacji ¨  Utworzenie drugiej aktywności
na Androida ¨  Wywołanie drugiej aktywności
¨  Przekazanie danych
Każda aplikacja na Androida musi zawierać plik o nazwie AndroidManifest.xml. Można
go znaleźć w katalogu projektu, w podkatalogu app/src/main. Plik ten zawiera kluczowe
informacje dotyczące aplikacji, takie jak aktywności, z których się składa, używane Komunikator
biblioteki oraz wiele innych deklaracji. Android generuje dla nas ten plik w momencie
tworzenia aplikacji. Jeśli przypomnisz sobie ustawienia wybierane podczas tworzenia app/src/main
aplikacji, to niektóre fragmenty kodu pliku manifestu będą wyglądać podobnie. <xml>
Plik AndoridManifest.xml </xml>

Poniżej przedstawiliśmy naszą wersję pliku AndroidManiest.xml: można znaleźć w tym AndroidManifest.xml
katalogu.
<?xml version=”1.0” encoding=”utf-8”?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.hfad.komunikator” > To jest podana przez
nas nazwa pakietu.
Jeśli 
piszesz 
<application Android Studio dodało do
naszej aplikacji domyślną ikonę.
Obejrzyj to!aplikacje 
android:allowBackup=”true” Przyjrzymy się jej dokładniej na Androida, nie 
android:icon=”@mipmap/ic_launcher” w dalszej części książki. używając żadnego 
zintegrowanego 
android:label=”@string/app_name” Motyw graficzny określa środowiska 
android:theme=”@style/AppTheme” > wygląd aplikacji. Także o tych
zagadnieniach dowiesz się programistycznego, 
więcej w dalszej części książki. to musisz utworzyć 
ten plik ręcznie.
<activity
android:name=”.CreateMessageActivity”
To jest nasza android:label=”@string/app_name” > Ten wiersz kodu
pierwsza określa, że to jest
aktywność, <intent-filter> główna aktywność
Create aplikacji.
<action android:name=”android.intent.action.MAIN” />
Message
Activity. <category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity> Z kolei ten wiersz informuje,
że aktywności można użyć
do uruchomienia aplikacji.
<activity
To jest
nasza druga android:name=”.ReceiveMessageActivity”
aktywność,
Receive android:label=”@string/title_activity_receive_message” >
Message
Activity. </activity>

Android Studio dodało ten fragment


</application> kodu, kiedy utworzyliśmy drugą
aktywność.
</manifest>

80 Rozdział 3.
Wiele aktywności i intencji

Każdą aktywność należy zadeklarować


Wszystkie aktywności należy zadeklarować w pliku AndrodiManifest.xml.
Jeśli nie zostanę
Jeśli aktywność nie zostanie zadeklarowana w tym pliku, to system nie wymieniona w pliku
będzie o niej wiedział. A jeśli system nie będzie nic widział o aktywności, AndroidManifest.xml, to
to nigdy nie zostanie ona wykonana. z punktu widzenia systemu
nie będę istnieć i nigdy
W pliku manifestu aktywności deklaruje się przy użyciu elementów nie zostanę wywołana.
<activity> umieszczanych wewnątrz elementu <application>. Okazuje
się, że każdej aktywności w aplikacji musi odpowiadać element <activity>.
Poniżej przedstawiamy jego ogólną postać:
<application
Każda aktywność musi zostać
... zadeklarowana wewnątrz elementu
<application>.
...> Ten wiersz jest
obowiązkowy.
<activity
android:name=”nazwa_klasy_aktywności” Ten wiersz jest opcjonalny, Aktywność
ale Android Studio
android:label=”@string/etykieta_aktywności” generuje go za nas.
... Aktywność może zawierać także inne właściwości.
...>
...
</activity>
...
</application>
Nasza druga 

Obejrzyj to!
Poniższy wiersz kodu jest obowiązkowy i służy do określenia nazwy klasy aktywności: aktywność 
została 
android:name=”nazwa_klasy_aktywności” automatycznie 
zadeklarowana, gdyż 
Przy czym nazwa_klasy_aktywności to, jak łatwo się domyślić, nazwa klasy dodaliśmy ją, używając 
aktywności poprzedzona znakiem kropki (.). W naszym przypadku będzie to zatem kreatora Android Studio.
.ReceiveMessageActivity. Nazwa klasy jest poprzedzona znakiem kropki, gdyż
Android łączy nazwę klasy z nazwą pakietu, uzyskując w ten sposób pełną nazwę klasy. W przypadku samodzielnego
dodawania dodatkowych
Ten wiersz jest opcjonalny i służy do określenia przyjaznej dla użytkownika nazwy aktywności będziemy
aktywności: musieli ręcznie wprowadzać
odpowiednie zmiany
android:label=”@string/etykieta_aktywności”
w pliku manifestu —
Ta nazwa jest wyświetlana na samej górze ekranu w czasie, gdy dana aktywność jest AndroidManifest.xml. Także
wykonywana. Jeśli atrybut nie zostanie określony, Android będzie w tym miejscu w przypadku stosowania innego
wyświetlał nazwę aplikacji. IDE informacje o dodatkowych
aktywnościach mogą nie być
Deklaracja aktywności może zawierać także inne właściwości, takie jak uprawnienia automatycznie dodawane do
niezbędne do wykonania aktywności, oraz informację, czy dana aktywność może być pliku manifestu.
używana przez aktywności należące do innych aplikacji.

jesteś tutaj  81
intencje

¨  Utworzenie pierwszej aktywności
Intencja jest rodzajem komunikatu ¨  Utworzenie drugiej aktywności
¨  Wywołanie drugiej aktywności
Dotychczas stworzyliśmy aplikację z dwiema aktywnościami, z których każda używa ¨  Przekazanie danych
własnego układu. W momencie uruchamiania aplikacji zostanie wykonana pierwsza
aktywność — CreateMessageActivity. Naszym kolejnym zadaniem jest zadbanie
o to, by po kliknięciu przycisku Wyślij wiadomość aktywność CreateMessageActivity Aktywność uruchamia się,
wywołała drugą aktywność — ReceiveMessageActivity.
tworząc intencję i używając
Zawsze, gdy chcemy uruchomić jedną aktywność z poziomu innej aktywności, musimy
użyć w tym celu intencji. Można ją sobie wyobrażać jako „intencję zrobienia czegoś”. jej w wywołaniu metody
Intencja to rodzaj komunikatu pozwalającego powiązać ze sobą niezależne obiekty
(takie jak aktywności) w trakcie działania aplikacji. Jeśli jedna aktywność chce startActivity().
uruchomić drugą, robi to, przesyłając do systemu Android odpowiednią intencję.
W efekcie system uruchomi aktywność i przekaże do niej tę intencję.
Intencja określa aktywność,
Do utworzenia i przesłania intencji wystarczy jeden lub dwa wiersze kodu. W pierwszej do której chcemy ją przesłać.
Informację tę można by porównać
kolejności należy utworzyć intencję, używając poniższego wiersza kodu: z adresem umieszczonym na
Intent intent = new Intent(this, KlasaDocelowa.class); kopercie.

Pierwszy parametr tego wywołania informuje system, z jakiego obiektu pochodzi Intencja
intencja, i jak widać, aby odwołać się do bieżącej aktywności, można posłużyć
się referencją this. Drugim parametrem jest nazwa klasy aktywności, do której
intencja jest skierowana.
Do: InnaAktywnosc
Po utworzeniu intencji można ją przekazać do systemu Android w następujący
sposób:
Wywołanie metody startActivity()
startActivity(intent); uruchamia aktywność określoną w intencji.

To wywołanie informuje system, że ma uruchomić aktywność określoną przez


przekazaną intencję.

Kiedy Android odbierze intencję, sprawdza, czy wszystko jest w porządku, po czym
uruchamia aktywność. Jeśli aktywności nie uda się odnaleźć, zgłaszany jest wyjątek
ActivityNotFoundException.

Niech no sprawdzę.
„Mój drogi Tak, wydaje się, że wszystko jest
Androidzie, czy mogłabym w porządku. Powiem Aktywności 2,
Cię prosić, żebyś kazał Aktywności 2 żeby się brała do pracy.
Oo… komunikat.
wziąć się do roboty? Szczerze oddana, Chyba zacznę działać.
Twoja stara kumpela, Aktywność 1”.

Intencja Intencja

Do: Aktywność2 Do: Aktywność2

Aktywność1 Android Aktywność2

82 Rozdział 3.
Wiele aktywności i intencji

Użycie intencji do uruchomienia drugiej aktywności


Zastosujmy to rozwiązanie w praktyce i użyjmy intencji do uruchomienia
aktywności ReceiveMessageActivity. Ponieważ aktywność chcemy uruchomić
w odpowiedzi na kliknięcie przycisku Wyślij wiadomość, do kodu metody
onSendMessage() dodamy dwa wiersze komend.

Wprowadź zmiany wyróżnione w poniższym kodzie:

package com.hfad.komunikator;
Musimy zaimportować
klasę Intent, android.
import android.app.Activity; content.Intent,
gdyż będziemy jej
import android.content.Intent; używać w metodzie
import android.os.Bundle; onSendMessage().

import android.view.View;
Komunikator

public class CreateMessageActivity extends Activity { app/src/main

Kodu tej metody nie trzeba zmieniać.


@Override java
protected void onCreate(Bundle savedInstanceState) {
com.hfad.komunikator
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message);
CreateMessage
} Activity.java

// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku


public void onSendMessage(View view) {
Intent intent = new Intent(this, ReceiveMessageActivity.class);
startActivity(intent);
} Te dwa wiersze uruchamiają aktywność
ReceiveMessageActivity.
}

Co się wydarzy, kiedy uruchomimy aplikację?

jesteś tutaj  83
Co się dzieje?

¨  Utworzenie pierwszej aktywności
Co się dzieje po uruchomieniu aplikacji? ¨  Utworzenie drugiej aktywności
¨  Wywołanie drugiej aktywności
Zanim weźmiemy naszą nową aplikację na jazdę próbną, ¨  Przekazanie danych
jeszcze raz przeanalizujmy, jak ona będzie działać:

<Layout>

1 Po uruchomieniu aplikacji </Layout>

zaczyna działać aktywność


CreateMessageActivity. activity_create_message.xml
Po uruchomieniu tej aktywności
informuje ona system, że ma być
używany układ zapisany w pliku activity_
create_message.xml. Ten właśnie układ CreateMessageActivity.java
Urządzenie
zostaje wyświetlony w nowym oknie.

onSendMessage()
2 Użytkownik klika przycisk.
W odpowiedzi na kliknięcie zostaje
wywołana metoda onSendMessage()
aktywności CreateMessageActivity.
CreateMessageActivity
Urządzenie

onSendMessage()

Intencja
3 Metoda onSendMessage() prosi
system o uruchomienie aktywności CreateMessageActivity
ReceiveMessageActivity, Do: Receive
używając do tego intencji. Message
Android upewnia się, że intencja Activity
jest prawidłowa, po czym nakazuje
uruchomienie aktywności Intencja
ReceiveMessageActivity. Android

Do: ReceiveMessageActivity

ReceiveMessageActivity

84 Rozdział 3.
Wiele aktywności i intencji

Historii ciąg dalszy


4 Po uruchomieniu aktywności
ReceiveMessageActivity informuje
ona system, że używa układu CreateMessageActivity
z pliku activity_receive_message.xml,
zatem ten układ jest wyświetlany.

Android
Urządzenie

ReceiveMessageActivity
<Layout>

</Layout>

activity_receive_message

Jazda próbna aplikacji


¨  Utworzenie pierwszej aktywności
Zapisz wszystkie zmiany, a następnie uruchom aplikację. Początkowo ¨  Utworzenie drugiej aktywności
zostanie uruchomiona aktywność CreateMessageActivity, kiedy jednak ¨  Wywołanie drugiej aktywności
klikniesz przycisk Wyślij wiadomość, aplikacja uruchomi drugą aktywność ¨  Przekazanie danych
— ReceiveMessageActivity.

Wpisz komunikat
i kliknij
przycisk Wyślij
wiadomość.

Kiedy klikniesz przycisk Wyślij wiadomość,


zostanie uruchomiona aktywność
ReceiveMessageActivity, a używany przez
nią układ pojawi się na ekranie. Widoczny
na nim będzie napis „Hello world!”, gdyż
jest to domyślny tekst umieszczany
w układzie przez Android Studio.

jesteś tutaj  85
Przekazanie tekstu

Przekazanie tekstu do drugiej aktywności ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
Jak na razie skoncentrowaliśmy się na tym, by aktywność CreateMessageActivity
¨  Wywołanie drugiej aktywności
¨  Przekazanie danych
uruchamiała drugą aktywność, ReceiveMessageActivity, po kliknięciu
przycisku Wyślij wiadomość. Kolejnym krokiem będzie zapewnienie
<Layout> <Layout>
możliwości przekazywania tekstu z aktywności CreateMessageActivity
do ReceiveMessageActivity, tak by ta druga mogła go wyświetlić. </Layout>
1 </Layout>

W tym celu konieczne będzie wykonanie następujących czynności:


activity_create_ activity_receive_
1 Zmienić układ activity_receive_message.xml, tak by można w nim message.xml message.xml
było wyświetlać tekst. Aktualnie ten układ ma domyślną postać
wygenerowaną przez kreator. 2 Intencja 3
2 Zaktualizować kod w pliku CreateMessageActivity.java, tak by aktywność
pobierała tekst wpisany przez użytkownika w polu tekstowym.
Ten tekst należy następnie dodać do intencji przed jej przesłaniem. CreateMessage RecieveMessage
Activity.java Activity.java
3 Zaktualizować kod w pliku ReceiveMessageAction.java,
tak by przesłany w intencji tekst był wyświetlany na ekranie.

Zacznijmy od aktualizacji układu


Poniżej przedstawiliśmy kod układu activity_receive_message.xml wygenerowany
dla nas przez Android Studio:
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
Komunikator
android:layout_height=”match_parent”
android:paddingLeft=”16dp”
android:paddingRight=”16dp” app/src/main
android:paddingTop=”16dp”
android:paddingBottom=”16dp” res
tools:context=”com.hfad.komunikator.ReceiveMessageActivity”>
layout
<xml>
<TextView To jest komponent </xml>

android:text=”@string/hello_world” TextView aktualnie activity_receive_


wyświetlany w układzie. message.xml
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
</RelativeLayout>

Trzeba wprowadzić kilka zmian w układzie. Musimy dodać do elementu <TextView> identyfikator
”message”, żebyśmy mogli odwoływać się do niego w kodzie aktywności; oprócz tego musimy zapobiec
wyświetlaniu tekstu „Hello world!”. Jak powinien wyglądać zmodyfikowany układ? Spróbuj go przygotować
Ćwiczenie samodzielnie, zanim spojrzysz na następną stronę.

86 Rozdział 3.
Wiele aktywności i intencji

Aktualizacja właściwości widoku tekstowego


Komunikator
W układzie drugiej aktywności trzeba zmienić kilka rzeczy.
app/src/main
W pierwszej kolejności musimy dodać identyfikator do elementu <TextView>. Takie
identyfikatory trzeba dodawać do wszystkich komponentów GUI, do których chcemy
się odwoływać w kodzie aplikacji, gdyż dzięki nim będzie można pobierać referencje res
do komponentów. Oprócz tego musimy zadbać o to, by nie był wyświetlany tekst
„Hello world!”. layout
<xml>
</xml>
Obie te sprawy możemy załatwić, wprowadzając zmiany pokazane w poniższym
activity_receive_
przykładzie:
message.xml
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:paddingBottom=”16dp”
tools:context=”com.hfad.komunikator.ReceiveMessageActivity”>

Ten wiersz dodaje do elementu <TextView>


<TextView identyfikator „message”.
android:id=”@+id/message”
Usuwamy wiersz wyświetlający w komponenci
android:text=”@string/hello_world” tekst z zasobu @string/hello_world. e

android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
</RelativeLayout>
Nie istnieją
głupie pytania
Zamiast usuwać poniższy wiersz kodu:
P: Czy muszę używać intencji? Czy nie mogę 
android:text=”@string/hello_world” utworzyć instancji drugiej aktywności w kodzie 
pierwszej?
można także zmodyfikować plik strings.xml tak, by zasób
hello_world zawierał pusty łańcuch znaków. W tym O: To jest dobre pytanie, jednak nie to nie jest
przykładzie zdecydowaliśmy się tego nie robić, gdyż jedynym androidowy sposób uruchamiania aktywności. Jednym
tekstem, który kiedykolwiek będziemy chcieli wyświetlać z powodów jest to, że przekazując do systemu intencje,
w widoku tekstowym, jest komunikat przesłany z aktywności Android będzie wiedział, w jakiej kolejności były
CreateMessageActivity. wykonywane poszczególne aktywności. A to z kolei
oznacza, że w razie kliknięcia przycisku Wstecz na
Skoro poradziliśmy sobie z układami, możemy zająć się urządzeniu Android będzie dokładnie wiedział,
kodem aktywności. gdzie ma wrócić.

jesteś tutaj  87
Ekstra, ekstra

Metoda putExtra() zapisuje w intencji ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
dodatkowe informacje ¨  Wywołanie drugiej aktywności
¨  Przekazanie danych
Wiesz już, że nową intencję można utworzyć w następujący sposób:
Metoda putExtra()
Intent intent = new Intent(this, KlasaDocelowa.class); umożliwia
dodawanie
do wysyłanych
Jednak do takiej intencji można dodać dodatkowe informacje, które komunikatów
następnie będzie można odczytać w aktywności docelowej i odpowiednio dodatkowych Intencja
na nie zareagować. Do zapisywania takich dodatkowych informacji służy informacji.
metoda putExtra():
intent.putExtra(”message”, value); Do: ReceiveMessageActivity
message: “Witam!”
gdzie message jest łańcuchem znaków określającym nazwę przekazywanej
wartości, a value jest samą wartością. Metoda putExtra() jest przeciążona,
dzięki czemu można jej używać do przekazywania wartości wielu różnych typów.
Na przykład mogą to być wartości typów prostych, takich jak boolean lub int, Do intencji można dodawać
tablice typów prostych bądź łańcuchy znaków — String. Metodę putExtra() wartości wielu różnych typów.
można wywoływać wielokrotnie, aby zapisać w intencji więcej danych. W takich Wszystkie dostępne typy można
poznać, przeglądając dokumentację
sytuacjach należy jednak pamiętać, by każda wartość miała unikalną nazwę. Androida. Oprócz tego informacje
te będą także wyświetlane
przez Android Studio podczas
Jak pobrać dodatkowe informacje z intencji? wpisywania kodu.

To jednak jeszcze nie koniec historii. Kiedy system nakaże uruchomienie


aktywności ReceiveMessageActivity, musi ona dysponować jakąś
możliwością pobrania dodatkowych informacji, które aktywność
CreateMessageActivity przesłała do niej w intencji.

Istnieje kilka przydatnych metod, których można użyć do tego celu.


Pierwszą z nich jest:
Intencja
getIntent();

Metoda getIntent() zwraca intencję, która została użyta do uruchomienia


aktywności. Z kolei tej intencji można użyć do pobrania przekazanych w niej Do: ReceiveMessageActivity
informacji. Konkretny sposób pobierania informacji z intencji zależy od ich message: “Witam!”
typu. Na przykład jeśli wiemy, że intencja zawiera łańcuch znaków o nazwie
”message”, to możemy go pobrać, używając następującego fragmentu kodu:
Pobiera intencję.
Intent intent = getIntent();
Pobiera łańcuch znaków
String string = intent.getStringExtra(“message”); o nazwie „message”
przekazany w intencji.
Nasze możliwości nie ograniczają się jednak do pobierania wyłącznie
łańcuchów znaków. Na przykład poniższe wywołanie:

int intNum = intent.getIntExtra(”name”, default_value);


pozwala pobrać wartość typu int o nazwie name. Argument default_value
określa wartość domyślną.
88 Rozdział 3.
Wiele aktywności i intencji

package com.hfad.komunikator; Zagadkowy basen


Twoim zadaniem jest powyciąganie z basenu
import android.os.Bundle; fragmentów kodu i wstawienie ich w odpowiednie
import android.app.Activity; puste miejsca pliku CreateMessageActivity.java.
import android.content.Intent; Żadnego fragmentu kodu nie można użyć więcej
niż raz, lecz nie wszystkie fragmenty będą
import android.view.View;
potrzebne. Twoim celem jest skompletowanie
............................................ aktywności, która będzie pobierać tekst z widoku
tekstowego i zapisywać go w intencji.

public class CreateMessageActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message);
}

// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku


public void onSendMessage(View view) {
.......................................................
.......................................................
Intent intent = new Intent(this, ReceiveMessageActivity.class);
.......................................................
startActivity(intent);
}
}

EditText
EditText
import putExtra

messageView putExtraString “message” =


String ;
findViewById (
getText() ( ; .
messageView =
R.id.message messageText ( .
) ;
messageText intent ,
android.widget.EditText )
) ;
toString() .

jesteś tutaj  89
Rozwiązanie zagadkowego basenu

package com.hfad.komunikator;
Zagadkowy basen. Rozwiązanie
import android.os.Bundle;
Twoim zadaniem jest powyciąganie
import android.app.Activity;
Musisz z basenu fragmentów kodu
import android.content.Intent; zaimportować i wstawienie ich w odpowiednie
import android.view.View; klasę EditText. puste miejsca pliku
import android.widget.EditText
............................................. CreateMessageActivity.java.
Żadnego fragmentu kodu
nie można użyć więcej niż
raz, lecz nie wszystkie fragmenty
public class CreateMessageActivity extends Activity { będą potrzebne. Twoim celem jest
@Override skompletowanie aktywności, która
będzie pobierać tekst z widoku
protected void onCreate(Bundle savedInstanceState) {
tekstowego i zapisywać go w intencji.
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message);
}

// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku


public void onSendMessage(View view) { Te dwa wiersze
EditText messageView = (EditText) findViewById(R.id.message); pobierają tekst
........................................................... z pola tekstowego
String messageText = messageView.getText().toString(); o identyfikatorze
........................................................... „message”.
Intent intent = new Intent(this, ReceiveMessageActivity.class);
intent.putExtra("message", messageText);
.......................................................
startActivity(intent);
Ten wiersz dodaje tekst do
} intencji, nadając mu nazwę
„message”.
}

Te fragmenty kodu
nie były potrzebne.

putExtraString

90 Rozdział 3.
Wiele aktywności i intencji

Aktualizacja kodu aktywności CreateMessageActivity ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
Zaktualizowaliśmy kod pliku CreateMessageActivity.java w taki sposób, że aktywność
¨  Wywołanie drugiej aktywności
¨  Przekazanie danych
pobiera tekst wpisany przez użytkownika w polu tekstowym i dodaje go do intencji.
Poniżej przedstawiliśmy kompletną wersję kodu (koniecznie wprowadź te same
modyfikacje, wyróżnione pogrubioną czcionką, w swoim pliku):

package com.hfad.komunikator;

Komunikator
import android.os.Bundle;
import android.app.Activity; app/src/main
import android.content.Intent;
Musisz zaimportować klasę EditText,
import android.view.View; android.widget.EditText, gdyż jest java
import android.widget.EditText; ona używana w kodzie aktywności.
com.hfad.komunikator

public class CreateMessageActivity extends Activity { CreateMessage


@Override Activity.java

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message);
}

Te wiersze pobierają
// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku tekst zapisany
w komponencie EditText.
public void onSendMessage(View view) {
EditText messageView = (EditText)findViewById(R.id.message);
String messageText = messageView.getText().toString();
Intent intent = new Intent(this, ReceiveMessageActivity.class);
intent.putExtra(ReceiveMessageActivity.EXTRA_MESSAGE, messageText);
startActivity(intent);
} Ten fragment kodu tworzy intencję,
następnie dodaje do niej tekst. Nazwę
} To wywołanie informacji zapisywanej w intencji
uruchamia aktywność określamy przy użyciu stałej, dzięki
ReceiveMessageActivity, czemu możemy mieć pewność, że w obu
używając w tym celu intencji. aktywnościach, CreateMessageActivity
i ReceiveMessageActivity, będzie używany
ten sam łańcuch znaków. Tę stałą dodamy
do klasy ReceiveMessageActivity na
następnej stronie.
Teraz, kiedy aktywność CreateMessageActivity zapisuje już
dodatkowe informacje w intencji, musimy zająć się ich pobraniem
i wyświetleniem.

jesteś tutaj  91
Metoda getStringExtra()

Zastosowanie informacji przekazanych w intencji ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
w klasie ReceiveMessageActivity ¨  Wywołanie drugiej aktywności
¨  Przekazanie danych
Skoro zmodyfikowaliśmy już kod aktywności CreateMessageActivity Intencja
i zaimplementowaliśmy zapisywanie tekstu w intencji, nadszedł czas
na wprowadzenie zmian w kodzie klasy ReceiveMessageActivity
i zastosowanie przekazanego tekstu.

Chcemy sprawić, by aktywność ReceiveMessageActivity, CreateMessage RecieveMessage


bezpośrednio po je utworzeniu, wyświetlała przekazany tekst w widoku Activity.java Activity.java
tekstowym. Ponieważ metoda onCreate()jest wywoływana zaraz po
utworzeniu aktywności, to właśnie do niej dodamy niezbędny kod. Musimy zadbać
o to, by aktywność
Aby pobrać tekst z intencji, w pierwszej kolejności musimy pobrać ReceiveMessageActivity
skorzystała z intencji,
intencję, używając metody getIntent(), a następnie pobrać sam która ją uruchomiła.
łańcuch znaków, używając metody getStringExtra().

Poniżej przedstawiliśmy pełny kod aktywności zapisany w pliku


ReceiveMessageActivity.java (zastąp nim początkowy kod wygenerowany
przez Android Studio, a następnie zapisz zmodyfikowany plik):
package com.hfad.komunikator;
Komunikator

import android.app.Activity;
app/src/main
import android.content.Intent;
Musimy
import android.os.Bundle; zaimportować klasy
Intent i TextView. java
import android.widget.TextView;
com.hfad.komunikator
public class ReceiveMessageActivity extends Activity {
public static final String EXTRA_MESSAGE = ”message”;
ReceiveMessage
Activity.java
@Override To nazwa wartości, którą będziemy przekazywali w intencji.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); Te dwa wiersze pobierają
intencję, a następnie używają
setContentView(R.layout.activity_receive_message); metody getStringExtra(),
by odczytać przekazany
Intent intent = getIntent(); w niej łańcuch znaków.
String messageText = intent.getStringExtra(EXTRA_MESSAGE);
TextView messageView = (TextView)findViewById(R.id.message);
messageView.setText(messageText);
}
} Ten wiersz wyświetla łańcuch znaków w widoku tekstowym.

Zanim weźmiemy aplikację na jazdę próbną, przeanalizujmy


dokładniej, jak działa ten kod.

92 Rozdział 3.
Wiele aktywności i intencji

Co się dzieje, gdy użytkownik kliknie przycisk Wyślij wiadomość?


1 Kiedy użytkownik klika przycisk, Intencja
zostaje wywołana metoda onSendMessage()
onSendMessage().
Kod metody onSendMessage() tworzy nową Do: ReceiveMessage
intencję, żądającą uruchomienia aktywności Activity
ReceiveMessageActivity, dodaje do niej CreateMessageActivity message:”Cześć!” Android
wiadomość wpisaną przez użytkownika, po
czym przekazuje intencję do systemu wraz
z prośbą o uruchomienie aktywności.

2 Android sprawdza, czy z intencją


jest wszystko w porządku,
a następnie uruchamia aktywność CreateMessageActivity
ReceiveMessageActivity.
Intencja

Do: ReceiveMessage Android


Activity
message:”Cześć!”
ReceiveMessageActivity

3 Po uruchomieniu aktywność
ReceiveMessageActivity określa,
że używa układu activity_receive_ CreateMessageActivity
message.xml, i to właśnie on jest
wyświetlany na ekranie urządzenia. Cześć!
Aktywność aktualizuje także układ,
wyświetlając w nim tekst przekazany
w intencji. Urządzenie

ReceiveMessageActivity

<Layout>

</Layout>

activity_receive_message

jesteś tutaj  93
Jazda próbna

Jazda próbna aplikacji ¨  Utworzenie pierwszej aktywności


¨  Utworzenie drugiej aktywności
Upewnij się, czy wprowadziłeś zmiany w obu aktywnościach, następnie zapisz ¨  Wywołanie drugiej aktywności
wszystkie zmienione pliki i uruchom aplikację. Początkowo zostanie uruchomiona ¨  Przekazanie danych
aktywność CreateMessageActivity, lecz po wpisaniu tekstu i kliknięciu przycisku
Wyślij wiadomość aplikacja uruchomi aktywność ReceiveMessageActivity.
Wpisany wcześniej tekst zostanie wyświetlony w widoku tekstowym.

Oto wpisany wcześniej tekst,


który pomyślnie udało się
przekazać do drugiej aktywności Obie aktywności zajmują
przy użyciu intencji. cały obszar ekranu
urządzenia, lecz tu
pominęliśmy część pustych
fragmentów ekranu.

Możemy zmienić aplikację tak, by wiadomości były wysyłane do innych osób


Teraz, kiedy nasza aplikacja wysyła już wiadomości do innej aktywności, możemy ją zmienić
w taki sposób, by wiadomości były wysyłane do innych osób. Możemy to zrobić, integrując ją
z innymi aplikacjami wysyłającymi widomości, już zainstalowanymi na naszym urządzeniu.
W zależności od zainstalowanych aplikacji możemy zapewnić możliwość wysyłania
wiadomości przy użyciu Messaging, GMaila, Google+, Facebooka, Twittera itd.

Hej, zaczekajcie no chwilkę!


Zapewnienie współpracy naszej aplikacji
z innymi wymaga pewnie strasznie dużych
nakładów pracy. A poza tym skąd możemy
wiedzieć, jakie aplikacje będą zainstalowane na
urządzeniach użytkowników? Skończcie, proszę,
z tymi fantazjami.

Okazuje się, że ze względu na sposób, w jaki zaprojektowano 
Androida, nie jest to aż tak trudne, jak się wydaje.
Czy pamiętasz, jak na początku rozdziału napisaliśmy, że zadania to
sekwencje połączonych aktywności? No więc okazuje się, że nasze
możliwości nie ograniczają się do stosowania aktywności należących do
naszej aplikacji. Można wykraczać poza granice własnej i używać aktywności
należących do innych aplikacji.
94 Rozdział 3.
Wiele aktywności i intencji

Jak działają aplikacje na Androida?


Jak już się przekonaliśmy, wszystkie aplikacje na Androida składają się
z jednej lub kilku aktywności oraz z komponentów dodatkowych, takich jak
układy. Każda aktywność reprezentuje jedną, dobrze zdefiniowaną operację,
którą może wykonywać użytkownik. Na przykład takie aplikacje jak Gmail,
Google+, Messaging, Facebook oraz Twitter udostępniają aktywności
umożliwiające wysyłanie wiadomości, choć każda z nich może to robić
w zupełnie odmienny sposób.
Urządzenie

Gmail Google+ SMS/MMS


Każda aplikacja składa się
z grupy aktywności. Oprócz
tego aplikacje zawierają
także inne komponenty, lecz
na razie koncentrujemy się
wyłącznie na aktywnościach.

Intencje mogą uruchamiać aktywności w innych aplikacjach


Wiesz już, w jaki sposób można używać intencji do uruchomienia drugiej
aktywności w tej samej aplikacji. Pierwsza aktywność przekazuje systemowi
intencję, a Android nakazuje uruchomienie drugiej aktywności.

Dokładnie to samo dotyczy uruchamiania aktywności należących do innych


aplikacji. Aktywność należąca do naszej aplikacji przekazuje systemowi
intencję, ten ją sprawdza, a następnie nakazuje uruchomienie drugiej
aktywności, nawet jeśli będzie ona należeć do innej aplikacji. Na przykład
możemy użyć intencji w celu uruchomienia aktywności służącej do wysyłania
wiadomości należącej do aplikacji Gmail i przekazać jej tekst, który chcemy Możemy stworzyć intencję, która
wysłać. Zamiast pisać własne aktywności do wysyłania poczty elektronicznej, uruchomi wybraną aktywność,
nawet jeśli ta aktywność będzie
możemy skorzystać z już istniejącej aplikacji Gmail. należała do innej aplikacji.

Komunikator Intencja Gmail


Intencja
To jest
aplikacja,
którą
piszemy
w tym
rozdziale.
Android

Oznacza to, że tworząc sekwencję aktywności dostępnych na urządzeniu,


możemy tworzyć aplikacje o znacznie większych możliwościach.

jesteś tutaj  95
Stosowanie akcji

Ale przecież nie wiemy, jakie aplikacje są dostępne na urządzeniu


Zanim będziemy mogli korzystać z aplikacji należących do innych aplikacji, musimy
znaleźć odpowiedzi na trzy pytania:

 Skąd mamy wiedzieć, jakie aktywności są dostępne na urządzeniu użytkownika?


 Skąd mamy wiedzieć, które z tych aktywności nadają się do wykonania interesującej nas operacji?
 Skąd mamy wiedzieć, jak używać tych aktywności?
Bardzo dobrą wiadomością jest to, że wszystkie te problemy możemy rozwiązać, używając akcji
(ang. actions). Akcje to sposób pozwalający na poinformowanie systemu Android o tym, jakie
standardowe operacje może wykonywać dana aktywność. Na przykład Android wie, że wszystkie
aktywności zarejestrowane do wykonywania akcji SEND (wysyłania) potrafią wysyłać wiadomości.

Kolejną rzeczą, której się nauczymy, będzie tworzenie intencji zwracających zbiór aktywności,
których można używać w standardowy sposób — na przykład do wysyłania wiadomości.

Oto, co mamy zamiar zrobić


1 Utworzyć intencję określającą akcję.
Intencja ta przekaże Androidowi informację, że chcemy użyć aktywności, która potrafi wysyłać
wiadomości. Intencja ta będzie także zawierała tekst tej wiadomości.

2 Zapewnić użytkownikowi możliwość wyboru, której aplikacji chce użyć.


Istnieje spora szansa, że na urządzeniu będzie dostępnych więcej aplikacji pozwalających na wysyłanie
wiadomości, dlatego użytkownik będzie musiał wybrać jedną z nich. Chcemy, aby użytkownik mógł
wybrać używaną aplikację po każdym kliknięciu przycisku Wyślij wiadomość.

96 Rozdział 3.
Wiele aktywności i intencji

Utworzenie intencji określającej akcję ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
Dotychczas dowiedziałeś się, w jaki sposób można tworzyć intencje uruchamiające
konkretną aktywność:
Przekazaliśmy intencji informację
Intent intent = new Intent(this, ReceiveMessageActivity.class); o tym, która klasa nas interesuje.
Ale co zrobić w przypadku, gdy nie
Są to tak zwane intencje jawne (ang. explicit intent), gdyż jawnie informują będziemy znali tej klasy?
system, którą klasę ma uruchomić.
Jeśli jednak chcemy wykonać określoną czynność, lecz nie interesuje nas, która
aktywność to zrobi, to możemy utworzyć intencję niejawną (ang. implicit intent).
W takim przypadku określamy akcję, którą chcemy wykonać, pozostawiając
Androidowi możliwość doboru odpowiednich aktywności.

Sposób tworzenia intencji Informacje o wszelkich


Intencję określającą akcję do wykonania można utworzyć w następujący sposób: dostępnych akcjach
Intent intent = new Intent(action); oraz dodatkowych
gdzie action określa typ akcji, którą aktywność ma wykonać. Android udostępnia informacjach, które
obszerną grupę standardowych akcji. Na przykład akcja Intent.ACTION_DIAL
pozwala wybrać numer telefonu, akcja Intent.ACTION_WEB_SEARCH umożliwia można do nich
wyszukanie frazy w internecie, a za pomocą akcji Intent.ACTION_SEND można przekazywać, znajdziesz
wysłać wiadomość. A zatem gdybyśmy chcieli utworzyć intencję określającą, że
zależy nam na wysłaniu wiadomości, moglibyśmy to zrobić w następujący sposób: w dokumentacji
Intent intent = new Intent(Intent.ACTION_SEND);
Androida na stronie
http://tinyurl.com/
Dodawanie informacji n57qb5.
Po określeniu akcji, której chcemy użyć, możemy dodać do intencji dodatkowe
informacje. W naszym przypadku chcemy dodać tekst, który stanie się treścią
wysyłanej wiadomości. Do tego celu musimy użyć dwóch poniższych wierszy kodu:

intent.setType(”text/plain”); Wszystkie te atrybuty są powiązane


intent.putExtra(Intent.EXTRA_TEXT, messageText); z akcją Intent.ACTION_SEND.
Nie będą one miały zastosowania
wszystkich dostępnych akcjach.
gdzie messageText jest tekstem, który chcemy wysłać. Powyższy fragment kodu
informuje system, że poszukujemy aktywności potrafiącej obsługiwać dane typu
MIME ”text/plain”, a dodatkowo określa sam wysyłany tekst.

Gdyby istniały jeszcze jakieś inne informacje, które chcielibyśmy dodać do intencji,
to moglibyśmy to zrobić, używając kolejnych wywołań metody putExtra().
Na przykład poniższy kod pozwala określić temat wysyłanej wiadomości: Jeśli dana aplikacja nie
umożliwia określania tematu
intent.putExtra(Intent.EXTRA_SUBJECT, subject); wiadomości, to zostanie on
zignorowany. Jednak wszystkie
gdzie subject jest tekstem określającym temat wiadomości. aplikacje, które wiedzą, jak
używać tematu, zastosują go.

jesteś tutaj  97
Zastosowanie akcji

Zmiana intencji w celu użycia akcji ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
Zmienimy teraz kod w pliku CreateMessageActivity.java i utworzymy w nim intencję
niejawną z akcją do wysyłania wiadomości. Wprowadź zatem zmiany przedstawione
w poniższym kodzie i zapisz plik:

package com.hfad.komunikator;

Komunikator
import android.os.Bundle;
import android.app.Activity; app/src/main

import android.content.Intent;
java
import android.view.View;
import android.widget.EditText; com.hfad.komunikator

public class CreateMessageActivity extends Activity { CreateMessage


Activity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_message);
}

// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku


public void onSendMessage(View view) {
EditText messageView = (EditText)findViewById(R.id.message);
String messageText = messageView.getText().toString();
Usuń te dwa
wiersze kodu. Intent intent = new Intent(this, ReceiveMessageActivity.class);
intent.putExtra(ReceiveMessageActivity.EXTRA_MESSAGE, messageText);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType(“text/plain”);
intent.putExtra(Intent.EXTRA_TEXT, messageText);
startActivity(intent);
Zamiast tworzyć intencję, która
} jawnie żąda uruchomienia aktywności
ReceiveMessageActivity, tworzymy intencję,
} używając akcji wysyłania wiadomości.

Przeanalizujmy teraz krok po kroku, co się dzieje, kiedy użytkownik


kliknie przycisk Wyślij wiadomość.

98 Rozdział 3.
Wiele aktywności i intencji

Co się dzieje podczas działania kodu?

1 Po wywołaniu metody onSendMessage()


zostaje utworzona intencja. Metoda onSendMessage() Intencja
startActivity() przekazuje tę intencję
do systemu Android.
Intencja zawiera określenie akcji ACTION_SEND ACTION_SEND
type: “text/plain”
i określenie typu MIME text/plain. CreateMessageActivity messageText:”Cześć!” Android

A… Intencja niejawna.
Muszę zatem znaleźć wszystkie
2 Android dowiaduje się, że intencję może
aktywności, które są w stanie
przekazać wyłącznie do aktywności, które
obsługiwać akcję ACTION_SEND,
potrafią wykonywać akcję ACTION_SEND
i operują na danych typu text/plain.
operują na danych typu text/plain
Sprawdza zatem wszystkie aktywności,
i należą do kategorii DEFAULT.
wyszukując spośród nich te, do których
może wysłać intencję.
Jeśli system nie znajdzie aktywności, które mogą
obsłużyć intencję, zostanie zgłoszony wyjątek
ActivityNotFoundException.

CreateMessageActivity Android

CreateMessageActivity
3 Jeśli tylko jedna aktywność jest
w stanie obsługiwać przekazaną intencję, Intencja
to Android uruchomi ją i przekaże
do niej intencję.
Do: Aktywność Android
messageText:”Cześć!”

Aktywność

jesteś tutaj  99
Co się dzieje?

Historii ciąg dalszy ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru

Hej, użytkowniku!
Każda z tych aktywności
4 Jeśli więcej niż jedna może wysyłać wiadomości.
aktywność może odebrać Której z nich chcesz
intencję, Android użyć?
wyświetli okno dialogowe
pozwalające wybrać
jedną z nich i poprosi CreateMessageActivity
użytkownika o dokonanie
wyboru.

Android
Użytkownik

5 Kiedy użytkownik
wybierze aktywność,
której chce użyć,
Android uruchamia ją CreateMessageActivity
i przekazuje do niej
intencję. Intencja
Aktywność wyświetli
Android
dodatkowy tekst
przekazany w intencji Do: Wybrana aktywność
Użytkownik
jako treść wysyłanej messageText: „Cześć!”
wiadomości. Wybrana
aktywność

Aby wyświetlić okno dialogowe do wyboru aktywności, Android


musi wiedzieć, które z nich są w stanie odebrać i obsłużyć intencję.
Na kilku następnych stronach dowiesz się, jak to zrobić.

100 Rozdział 3.
Wiele aktywności i intencji

Filtry intencji informują system, które aktywności


mogą obsługiwać poszczególne akcje
Kiedy do systemu Android zostanie przekazana intencja, musi on określić, która
aktywność (bądź które aktywności) jest w stanie ją obsłużyć. Ten proces jest
nazywany wyznaczaniem intencji (ang. intent resolution).

W razie zastosowania intencji jawnej wyznaczanie intencji jest banalnie proste.


W takim przypadku intencja bowiem jawnie określa, do którego komponentu jest
skierowana, zatem Android dysponuje jednoznacznymi instrukcjami dotyczącymi
tego, co należy zrobić. Na przykład poniższy kod jawnie informuje system, że należy
uruchomić aktywność ReceiveMessageActivity:

Intent intent = new Intent(this, ReceiveMessageActivity.class);


startActivity(intent);

W przypadku zastosowania intencji niejawnej Android określa komponenty, które


mogą ją obsłużyć, na podstawie informacji zawartych w tej intencji. W tym celu
system sprawdza filtry intencji umieszczone w plikach AndroidManifest.xml wszystkich
zainstalowanych aplikacji.

Filtr intencji (ang. intent filter) określa typy intencji, które mogą być obsługiwane
przez dany komponent. Na przykład poniższy fragment kodu dotyczy aktywności,
która potrafi obsługiwać akcje ACTION_SEND. Co więcej, aktywność ta obsługuje dane
typu MIME text/plain lub obrazy:
Ten wiersz informuje Androida,
że aktywność jest w stanie
<activity android:name=”ShareActivity”> obsługiwać akcję ACTION_SEND.

<intent-filter>
<action android:name=”android.intent.action.SEND”/> Ten filtr intencji musi
określać kategorię
<category android:name=”android.intent.category.DEFAULT”/> o wartości DEFAULT,
<data android:mimeType=”text/plain”/> gdyż w przeciwnym
razie nie będzie mógł
<data android:mimeType=”image/*”/> To są typy danych, obsługiwać intencji
które aktywność niejawnych.
</intent-filter>
potrafi obsługiwać.
</activity>

Filtry intencji określają także kategorię. Kategoria określa dodatkowe informacje


dotyczące aktywności, na przykład czy dana aktywność może być uruchamiana przez
przeglądarkę WWW bądź czy jest ona głównym punktem wejścia aplikacji. Filtr
intencji musi określać kategorię o wartości android.intent.category.DEFAULT,
jeśli ma zapewniać możliwość obsługi intencji niejawnych. Jeżeli aktywność nie
ma żadnego filtra intencji albo nie określa kategorii o wartości android.intent.
category.DEFAULT, to oznacza to, że nie będzie jej można uruchamiać przy użyciu
intencji niejawnych. Taką aktywność będzie można uruchomić wyłącznie za pomocą
intencji jawnej, określającej pełną nazwę komponentu.

jesteś tutaj  101


Filtry intencji

Jak Android korzysta z filtrów intencji? ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
W przypadku stosowania intencji niejawnych Android porównuje informacje
umieszczone w intencji z informacjami z filtrów intencji zapisanych w pliku
AndroidManifest.xml aplikacji.

W pierwszej kolejności Android bierze pod uwagę filtry określające kategorię typu
android.intent.category.DEFAULT:

<intent-filter>
<category android:name=”android.intent.category.DEFAULT”/>
...
</intent-filter>

Intencje, które nie określają tej kategorii, zostaną pominięte, gdyż nie mogą one obsługiwać
intencji niejawnych.
Oprócz tego, jeśli w intencji
Następnie Android porównuje intencje z filtrami intencji, uwzględniając przy tym została określona kategoria,
podaną w nich akcję i obsługiwany typ MIME. Na przykład jeśli intencja określa akcję to Android sprawdzi także
kategorię podaną w filtrze
Intent.ACTION_SEND, gdyż została utworzona w poniższy sposób: intencji. To rozwiązanie jest
jednak stosowane raczej
Intent intent = new Intent(Intent.ACTION_SEND); sporadycznie, dlatego nie
przedstawimy go tutaj.
to Android uwzględni wyłącznie aktywności, których filtry intencji określają akcję
android.intent.action.SEND, używając następującego kodu:

<intent-filter>
<action android:name=”android.intent.action.SEND”/>
...
</intent-filter>

I podobnie jeśli w intencji zostanie określony typ MIME o wartości ”text/plain”:

intent.setType(”text/plain”);

to Android uwzględni tylko te aktywności, które potrafią obsługiwać konkretny typ danych:

<intent-filter>
<data android:mimeType=”text/plain”/>
...
</intent-filter>

Jeśli w intencji nie został określony typ MIME, to system postara się go wywnioskować
na podstawie danych zapisanych w intencji.

Kiedy Android zakończy porównywanie intencji z filtrami intencji komponentów, będzie wiedział,
ile z nich udało mu się dopasować. Jeżeli uda mu się odnaleźć tylko jedno dopasowanie, to uruchomi
odpowiedni komponent (w naszym przypadku będzie to aktywność) i przekaże do niego intencję. Jeżeli
natomiast takich dopasowań będzie więcej, to Android poprosi użytkownika o wybranie jednego z nich.

102 Rozdział 3.
Wiele aktywności i intencji

Wczuj się w intencję! To jest intencja.


Twoim zadaniem jest wcielenie się
w rolę intencji przedstawionej Intent intent = new Intent(Intent.ACTION_SEND);
z prawej strony i wskazanie, które intent.setType(”text/plain”);
z zaprezentowanych intent.putExtra(Intent.EXTRA_TEXT, ”Witaj”);
poniżej aktywności
są zgodne z Twoją
aktywnością i typem
danych. Dla każdej
z aktywności podaj,
dlaczego jest lub dlaczego nie jest
zgodna z intencją.

<activity android:name=”SendActivity”>
<intent-filter>
<action android:name=”android.intent.action.SEND”/>
<category android:name=”android.intent.category.DEFAULT”/>
<data android:mimeType=”*/*”/>
</intent-filter>
</activity>

<activity android:name=”SendActivity”>
<intent-filter>
<action android:name=”android.intent.action.SEND”/>
<category android:name=”android.intent.category.MAIN”/>
<data android:mimeType=”text/plain”/>
</intent-filter>
</activity>

<activity android:name=”SendActivity”>
<intent-filter>
<action android:name=”android.intent.action.SENDTO”/>
<category android:name=”android.intent.category.MAIN”/>
<category android:name=”android.intent.category.DEFAULT”/>
<data android:mimeType=”text/plain”/>
</intent-filter>
</activity>

jesteś tutaj  103


Rozwiązanie

Wczuj się w intencję. Rozwiązanie


Twoim zadaniem jest wcielenie się
Intent intent = new Intent(Intent.ACTION_SEND);
w rolę intencji przedstawionej z prawej
strony i wskazanie, które intent.setType(”text/plain”);
z zaprezentowanych poniżej intent.putExtra(Intent.EXTRA_TEXT, ”Witaj”);
aktywności są zgodne z Twoją
aktywnością i typem danych.
Dla każdej z aktywności podaj,
dlaczego jest lub dlaczego nie
jest zgodna z intencją.
Ta aktywność akceptuje akcję ACTION_SEND
i potrafi obsługiwać dane dowolnego typu MIME,
<activity android:name=”SendActivity”> dlatego może odpowiedzieć na intencję.
<intent-filter>
<action android:name=”android.intent.action.SEND”/>
<category android:name=”android.intent.category.DEFAULT”/>
<data android:mimeType=”*/*”/>
</intent-filter>
</activity>

Ta aktywność nie zawiera kategorii


<activity android:name=”SendActivity”> typu DEFAULT, więc nie może odbierać
<intent-filter> aktywności niejawnych.
<action android:name=”android.intent.action.SEND”/>
<category android:name=”android.intent.category.MAIN”/>
<data android:mimeType=”text/plain”/>
</intent-filter>
</activity>

Ta aktywność nie obsługuje intencji z akcją ACTION_SEND,


<activity android:name=”SendActivity”> lecz z akcją ACTION_SENDTO. Ta akcja pozwala wysłać
wiadomość do odbiorcy określonego w danych intencji.
<intent-filter>
<action android:name=”android.intent.action.SENDTO”/>
<category android:name=”android.intent.category.MAIN”/>
<category android:name=”android.intent.category.DEFAULT”/>
<data android:mimeType=”text/plain”/>
</intent-filter>
</activity>

104 Rozdział 3.
Wiele aktywności i intencji

Musisz uruchomić aplikację ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
na PRAWDZIWYM urządzeniu
Dotychczas wszystkie nasze aplikacje uruchamialiśmy na emulatorze. Emulator
udostępnia niewielką liczbę aplikacji i jest całkiem możliwe, że wśród nich będzie
tylko jedna potrafiąca obsługiwać akcję ACTION_SEND. Aby prawidłowo przetestować
aplikację, będziemy musieli uruchomić ją na rzeczywistym, fizycznym urządzeniu,
mając przy tym pewność, że jest na nim zainstalowanych więcej aplikacji, które
potrafią obsługiwać naszą akcję — na przykład aplikacja umożliwiająca wysyłanie
e-maili i aplikacja do przesyłania komunikatów.

A oto, w jaki sposób możesz uruchomić tworzoną aplikację na fizycznym urządzeniu:

1. Włącz debugowanie USB


Na swoim urządzeniu otwórz opcję Opcje programistyczne
(począwszy od Androida 4.0 opcja ta jest domyślnie niedostępna).
Aby ją włączyć, wybierz opcję Ustawienia/Informacje o urządzeniu,
a następnie siedmiokrotnie kliknij pozycję Numer wersji. Kiedy
Właśnie tak,
serio! wrócisz do poprzedniego ekranu, powinna już być na nim
widoczna opcja Opcje programisty.

Na ekranie Opcje programistyczne zaznacz pole wyboru


Debugowanie USB.
Musisz włączyć opcję Debugowanie USB.

2. Skonfiguruj system, by wykrył urządzenie


Jeśli używasz komputera Mac, możesz pominąć te czynności.

Jeśli używasz komputera z systemem Windows, to musisz zainstalować


sterownik USB. Jego najnowszą wersję można pobrać ze strony:

http://developer.android.com/tools/extras/oem-usb.html

Jeśli używasz systemu Ubuntu, musisz utworzyć plik reguł udev.


Aktualne informacje o tym, jak to zrobić, można znaleźć na stronie:

http://developer.android.com/tools/device.html#setting-up

3. Podłącz urządzenie do komputera kablem USB


Twoje urządzenie może zapytać, czy chcesz zaakceptować klucz RSA
umożliwiający przeprowadzanie debugowania USB na danym komputerze. Ten komunikat zostanie wyświetlony,
Jeśli faktycznie tak się stanie, zaznacz opcję Zawsze zezwalaj temu jeśli używasz urządzenia z systemem
komputerowi, a następnie kliknij przycisk OK, by podłączyć urządzenie. Android 4.2.2 lub nowszym.

jesteś tutaj  105


Uruchamianie na prawdziwym urządzeniu

Uruchamianie aplikacji na prawdziwym urządzeniu ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
(ciąg dalszy)
4. W normalny sposób uruchom aplikację w Android Studio
Android Studio zainstaluje aplikację na urządzeniu i ją uruchomi. Być może zostaniesz
zapytany, na którym urządzeniu chcesz uruchomić aplikację. W takim przypadku wybierz
urządzenie z listy dostępnych urządzeń i kliknij przycisk OK.

Pierwszym z wyświetlonych
urządzeń jest emulator.

To jest nasze
fizyczne urządzenie.

A oto aplikacja uruchomiona


na fizycznym urządzeniu
Zauważysz zapewne, że aplikacja wygląda niemal
identycznie jak w przypadku uruchamiania jej na
emulatorze. Oprócz tego pewnie przekonasz się
także, że na fizycznym urządzeniu aplikacja działa
znacznie szybciej niż na emulatorze.

Skoro już wiesz, jak możesz uruchamiać własne


aplikacje na własnym, fizycznym urządzeniu,
możesz już przetestować aplikację Komunikatora.

106 Rozdział 3.
Wiele aktywności i intencji

Jazda próbna aplikacji


Spróbuj uruchomić aplikację w emulatorze, a następnie na własnym urządzeniu.
Uzyskane wyniki będą zależały od tego, jak dużo będzie dostępnych aktywności
obsługujących akcję wysyłania danych tekstowych.

Jeśli będzie jedna aktywność


W takim przypadku kliknięcie przycisku Wyślij
wiadomość spowoduje przejście bezpośrednio
do wybranej aplikacji.

Na emulatorze mamy dostępną


tylko jedną aktywność, która jest
w stanie wysyłać wiadomości
z danymi tekstowymi, dlatego kiedy
klikniemy przycisk Wyślij wiadomość,
Android automatycznie uruchomi tę
aktywność.
To jest wiadomość.
Jeśli będzie więcej niż jedna aktywność
W tym przypadku Android wyświetli okno dialogowe z listą wszystkich odszukanych
aktywności i poprosi o wskazanie tej, której chcemy użyć. Oprócz tego zostaniemy
zapytani, czy chcemy, by wybrana akacja została wykonana raz, czy też ma już być
wykonywana zawsze. W razie wybrania drugiej możliwości każde kolejne kliknięcie Na naszym fizycznym urządzeniu
dostępnych jest wiele aktywności
przycisku sprawi, że domyślnie zostanie wykonana ta sama akcja. spełniających nasze potrzeby.
Zdecydowaliśmy się wybrać aplikację
SMS/MMS (ang. Messaging). Oprócz
tego wybraliśmy opcję Zawsze — świetne
rozwiązanie, jeśli chcemy zawsze używać
tej samej aplikacji SMS/MMS, lecz już
nie tak dobre, jeśli za każdym razem
chcielibyśmy używać innej aplikacji.

jesteś tutaj  107


Niech wybierze użytkownik

A co, jeśli chcemy, by użytkownik ZAWSZE wybierał aktywność?


Przekonałeś się właśnie, że jeśli na urządzeniu jest więcej aktywności, Metoda createChooser()
które mogą odebrać i obsłużyć intencję, to Android automatycznie prosi
o wybór tej, której chcesz użyć. Co więcej, pyta nawet, czy ta aktywność umożliwia podanie tytułu
ma zostać użyta tylko jeden raz, czy też ma być wykonywana już zawsze.
okna dialogowego wyboru
Z tym domyślnym sposobem działania systemu wiąże się tylko jeden aktywności i jednocześnie
problem: co zrobić w przypadku, gdy chcemy zagwarantować, aby
użytkownik mógł wybrać aktywność po każdym kliknięciu przycisku ukrywa w nim opcje
Wyślij wiadomość? Jeśli użytkownik zdecyduje na przykład, że zawsze
pozwalające użytkownikowi
chce wysyłać wiadomości za pomocą aplikacji Gmail, to w przyszłości
nie zostanie już zapytany, czy chce użyć Twittera. określać, czy dana
Na szczęście ten problem można łatwo rozwiązać. Okazuje się, że można aktywność ma być używana
utworzyć okno dialogowe wyboru aktywności, które nie będzie pytało
użytkownika, czy wybrana aktywność ma zostać użyta tylko raz, czy zawsze.
domyślnie. Jednocześnie,
jeśli nie uda się znaleźć
Metoda Intent.createChooser() wyświetla żadnej pasującej aktywności,
okno dialogowe wyboru aktywności to użytkownik zostanie
Możliwość wyświetlenia okna dialogowego wyboru aktywności zapewnia
metoda Intent.createChooser(). Metoda ta pobiera utworzoną o tym poinformowany
intencję i przekazuje ją do okna dialogowego wyboru aktywności. odpowiednim komunikatem.
Podstawowa różnica pomiędzy stosowaniem tej metody a standardowym
sposobem uruchamiania aktywności polega na tym, że wyświetlone
okno dialogowe nie daje możliwości określania aktywności domyślnej
— użytkownik będzie proszony o wybór aktywności za każdym razem.
To jest utworzona wcześniej intencja.
Oto, jak wygląda wywołanie metody createChooser():

Intent chosenIntent = Intent.createChooser(intent, ”Wysyłanie wiadomości...”);

Metoda createChooser() pobiera dwa parametry: intencję i opcjonalny zać


łańcuch znaków określający tytuł wyświetlanego okna dialogowego. Możemy także przeka
tyt uł, któ ry zos tan ie
Możemy do niej przekazać utworzoną wcześniej intencję, tę, która wyświetlony na samej
korzysta z akcji ACTION_SEND i używa danych tekstowych. górze ekranu.

Metoda createChooser() zwraca nowiutki obiekt Intent. Ta nowa intencja


jawna zostanie skierowana bezpośrednio do aktywności wybranej przez
użytkownika. Będzie ona także zawierać wszelkie informacje dodatkowe
przekazane w początkowej intencji, w tym także wszystkie łańcuchy znaków.

Aby uruchomić wybraną aktywność, wystarczy użyć poniższego wywołania:

startActivity(chosenIntent);
Na kilku kolejnych stronach nieco dokładniej przyjrzymy się temu, co się dzieje
w momencie wywołania metody createChooser().

108 Rozdział 3.
Wiele aktywności i intencji

Co się dzieje w momencie wywołania ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
metody createChooser()?
Oto, co się stanie w momencie wykonania dwóch poniższych wierszy kodu:

Intent chosenIntent = Intent.createChooser(intent, ”Wysyłanie wiadomości...”);


startActivity(chosenIntent);
createChooser()
1 Zostaje wywołana metoda Intencja
createChooser().
W jej wywołaniu przekazywana jest intencja
określająca akcję, którą chcemy wykonać, ACTION_SEND
oraz informacje na temat typu danych type: “text/plain”
CreateMessageActivity message:”Cześć!” Android
MIME.

Widzę, że muszę
2 Android sprawdza, które aktywności
utworzyć okno dialogowe
mogą obsłużyć tę intencję, wyboru aktywności, które
sprawdzając ich filtry intencji. obsługują akcję SEND i operują
na danych typu text/plain.
Porównywane są akcja, typ danych
oraz kategoria, obsługiwane przez
poszczególne aktywności.

CreateMessageActivity Android

3 Jeśli aktywność może zostać obsłużona


przez więcej niż jedną aktywność, Hej, użytkowniku! Powiedz mi,
to Android wyświetla okno dialogowe
proszę, której aktywności
i prosi użytkownika o wybór jednej
chciałbyś użyć tym razem?
spośród odnalezionych aktywności.
Tym razem użytkownik nie będzie miał CreateMessageActivity
możliwości określenia, że wybrana
aktywność ma być używana za każdym
razem — w oknie dialogowym zostanie tylko
wyświetlony tytuł „Wysyłanie wiadomości…”.

Jeśli nie uda się znaleźć żadnych pasujących


aktywności, to Android i tak wyświetli okno
Android
dialogowe, a dodatkowo także komunikat Użytkownik
informujący użytkownika, że nie ma aplikacji,
które mogłyby obsłużyć wybraną akcję.
jesteś tutaj  109
Co się dzieje?

Historii ciąg dalszy ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru

4 Kiedy użytkownik wybierze, Ona chce użyć aktywności


której aktywności chce użyć, Aktywność2. Proszę bardzo,
Android zwraca nową intencję to odpowiednia intencja.
jawną skierowaną do wybranej
aktywności.
Ta nowa intencja zawiera wszelkie
informacje dodatkowe, które Intencja
były zapisane w początkowej
aktywności, na przykład wszystkie
teksty. WybranaAktywność
CreateMessageActivity message:”Cześć!”
Android
Użytkownik

Dzięki za intencję,
Androidzie. A czy możesz
teraz uruchomić tę
5 Aktywność prosi
intencję?
system o uruchomienie
aktywności określonej
Intencja
w intencji.

Do: WybranaAktywność
message:”Cześć!”
CreateMessageActivity Android

CreateMessageActivity
6 Android uruchamia
aktywność określoną
w intencji i przekazuje
do niej tę intencję.
Intencja

Do: WybranaAktywność Android


message:”Cześć!”
WybranaAktywność

110 Rozdział 3.
Wiele aktywności i intencji

Zmień kod, aby wyświetlać okno dialogowe


Zmienimy teraz kod naszej aktywności w taki sposób, by po każdym kliknięciu
przycisku Wyślij wiadomość użytkownik był proszony o wybór aktywności,
której chce użyć do wysłania wiadomości. W tym celu zmienimy kod metody
onSendMessage() w pliku CreateMessageActivity.java, dodając do niego
wywołanie metody createChooser(), a oprócz tego do pliku strings.xml
dodamy nowy zasób łańcuchowy określający tytuł okna dialogowego.

Komunikator
Zaktualizuj plik strings.xml…
Chcemy, by okno dialogowe do wyboru aktywności miało tytuł Wysyłanie app/src/main
wiadomości…, a zatem do pliku strings.xml dodaj zasób łańcuchowy o nazwie
chooser i wartości Wysyłanie wiadomości... (nie zapomnij także res
o zapisaniu zmienionego pliku).
... values
<string name=”chooser”>Wysyłanie wiadomości...</string> <xml>
</xml>
... strings.xml

…a następnie kod metody onSendMessage()


Musimy zmienić kod metody onSendMessage() w taki sposób, by pobierała
Komunikator
z pliku strings.xml zasób określający tytuł okna dialogowego, wywoływała
metodę createChooser(), a następnie uruchamiała wybraną aktywność.
A zatem zmodyfikuj kod aktywności w następujący sposób: app/src/main

...
java
// Metoda onSendMessage() jest wywoływana po kliknięciu przycisku
public void onSendMessage(View view) {
com.hfad.komunikator
EditText messageView = (EditText)findViewById(R.id.message);
String messageText = messageView.getText().toString();
Intent intent = new Intent(Intent.ACTION_SEND); CreateMessage
intent.setType(”text/plain”); Activity.java
intent.putExtra(Intent.EXTRA_TEXT, messageText); Pobranie tytułu okna.
String chooserTitle = getString(R.string.chooser);
Intent chosenIntent = Intent.createChooser(intent, chooserTitle);
startActivity(intent);
startActivity(chosenIntent); Uruchomienie aktywności Wyświetlenie okna dialogowego
} wybranej przez użytkownika. wyboru aktywności.
...
Metoda getString() służy do pobierania wartości zasobów łańcuchowych.
Ma ona jeden parametr określający identyfikator zasobu (w naszym przypadku
jest to R.string.chooser):
Jeśli zajrzysz do pliku R.java, znajdziesz
getString(R.string.chooser); chooser w klasie wewnętrznej o nazwie string.

Teraz, kiedy już zaktualizowaliśmy aplikację, zobaczmy, jak w działaniu wygląda nasze
nowe okno dialogowe wyboru aktywności.
jesteś tutaj  111
Jazda próbna

Jazda próbna aplikacji ¨  Określenie akcji


¨  Zapewnienie możliwości wyboru
Zapisz zmiany i uruchom aplikację.

Jeśli będzie jedna aktywność


W takim przypadku kliknięcie przycisku Wyślij
wiadomość spowoduje, jak wcześniej, przejście
bezpośrednio do wybranej aplikacji.

Tu nic się nie zmieniło


— Android bezpośrednio
uruchamia wybraną
aktywność.

Jeśli będzie więcej niż jedna aktywność


W tym przypadku Android wyświetli okno dialogowe z listą wszystkich
odszukanych aktywności, lecz nie zapyta, czy chcemy, by wybrana akacja
została wykonana raz, czy też ma już być wykonywana zawsze. Oprócz
, które
tego okno ma tytuł określony na podstawie zasobu łańcuchowego. To jest okno dialogowe wyboru aktywności er().
utworzyliśmy przy użyciu metody createChoos
lającej na
Jak widać, nie zawiera ono już opcji pozwa razem.
m
wykonywanie wybranej aktywności za każdy

112 Rozdział 3.
Wiele aktywności i intencji

Jeśli nie będzie ŻADNYCH pasujących aktywności


Jeśli na naszym urządzeniu nie będzie żadnych aktywności pozwalających wysyłać
wiadomości, to metoda createChooser() powiadomi nas o tym, wyświetlając
stosowny komunikat.

To kolejna zaleta stosowania metody createChooser() — doskonale radzi


sobie ona z sytuacjami, w których nie ma dostępnych aktywności służących
do wykonywania konkretnej akcji.

Jeśli chcesz samodzielnie uzyskać


podobne wyniki, to spróbuj uruchomić
swoją aplikację w emulatorze i wyłączyć
przy tym aplikację SMS/MMS.

Nie istnieją
głupie pytania

P: A zatem mogę uruchamiać  P: Czy należy używać intencji  O: Akcja określa, co dana aktywność


swoje aplikacje w emulatorze lub na  niejawnych czy jawnych? potrafi robić, natomiast kategoria
fizycznym urządzeniu. Które z tych 
rozwiązań jest lepsze? O: Wszystko sprowadza się do określa dodatkowe informacje na temat
wykonywanej czynności. Nie opisaliśmy
pytania, czy chcemy wykonać akcję,
O: Każde z nich ma swoje wady i zalety. używając konkretnej aktywności, czy
tych kategorii szczegółowo, gdyż
w praktyce rzadko kiedy pojawia się
W razie stosowania urządzenia zależy nam wyłącznie na wykonaniu potrzeba tworzenia intencji z kategoriami.
fizycznego testowane aplikacje zazwyczaj operacji. Załóżmy, że chcemy wysłać
wczytują się znacznie szybciej niż na e-mail. Jeśli nie interesuje nas, której P: Napisaliście, że w przypadku, 
emulatorze. To rozwiązanie jest także aplikacji użytkownik użyje, byleby tylko gdy nie ma aktywności, która 
przydatne w przypadku pisania kodu, wiadomość została wysłana, to możemy by mogła obsłużyć intencję, to 
który współpracuje z komponentami użyć intencji niejawnej. Natomiast jeśli wywołanie metody createChooser() 
sprzętowymi urządzenia. musimy przekazać intencję do konkretnej wyświetli komunikat. Co by się stało, 
Z kolei emulator pozwala testować
aktywności w aplikacji, to powinniśmy gdybym użył standardowego okna 
użyć intencji jawnej. W takim przypadku do wyboru aktywności i przekazał 
aplikację na wielu różnych wersjach
musimy jawnie określić aktywność, intencję niejawną w wywołaniu 
Androida, na ekranach o różnych
do której ma trafić intencja. metody startActivity()?
wielkościach oraz na urządzeniach
o różnych specyfikacjach. Pozwala zatem
P: Wspominaliście, że filtr intencji  O: Jeśli nie ma aktywności pasujących do
uniknąć konieczności kupowania wielu intencji przekazanej w wywołaniu metody
aktywności oprócz akcji może także 
różnych urządzeń. startActivity(), to zostanie zgłoszony
określać kategorię. Czym różni się 
Najważniejsze jest, by dokładnie kategoria od akcji? wyjątek ActivityNotFoundException.
przetestować aplikację, używając Jeśli nie przechwycimy go w bloku try/
kombinacji urządzeń emulowanych catch, to może on doprowadzić do awarii
i fizycznych, zanim udostępnimy ją aplikacji.
szerszemu gronu odbiorców.

jesteś tutaj  113


Przybornik

Twój przybornik do Androida Pełny kod przykładowe


aplikacji prezen tow ane
j
j
Rozdział 3.

ale mo żes z
w tym rozdzi
Opanowałeś już rozdział 3. i dodałeś  pobrać z serwe ra FTP
do swojego przybornika z narzędziami  wydawnictwa Helion:
klady/
umiejętności tworzenia aplikacji zawierających  ftp://ftp.helion.pl/przy
wiele aktywności i stosowania intencji. andrrg.zip

    CELNE SPOSTRZEŻENIA
 Zadanie to co najmniej dwie aktywności połączone w sekwencję.
 Element <EditText> definiuje pole służące do wpisywania tekstów. Klasa EditText dziedziczy po klasie View.
 W Android Studio nowe aktywności można dodawać, wybierając z menu opcję File/New.../Activity.
 Każdej tworzonej aktywności musi odpowiadać wpis w pliku AndroidManifest.xml.
 Intencje to komunikaty, których komponenty systemu Android używają do wzajemnej komunikacji.
 Intencja jawna w jawny sposób określa komponent, do którego jest skierowana. Takie intencje można tworzyć,
używając wywołania o postaci Intent intent = new Intent(this, KlasaDocelowa.class);.
 Aby uruchomić aktywność, wystarczy użyć wywołania startActivity(intent). Jeśli nie uda się znaleźć
odpowiednich aktywności, to takie wywołanie zgłosi wyjątek ActivityNotFoundException.
 Metoda putExtra() pozwala na dodawanie do intencji dodatkowych informacji.
 Intencję, która doprowadziła do uruchomienia aktywności, można pobrać przy użyciu metody getIntent().
 Grupa metod get*Extra() pozwala pobierać dodatkowe informacje zapisane w intencji metoda
getStringExtra() pobiera łańcuchy znaków, getIntExtra() pobiera liczby całkowite, i tak dalej.
 Akcja aktywności określa standardową czynność, którą dana aktywność potrafi wykonywać. Aby wysłać wiadomość,
należy użyć akcji ACTION_SEND.
 W celu utworzenia intencji niejawnej umożliwiającej wykonanie określonej akcji należy użyć wywołania Intent intent
= new Intent(akcja);.
 Metoda setType() pozwala określić typ danych zapisanych w intencji.
 Android wyznacza intencje na podstawie nazwy komponentu, akcji, typu danych oraz kategorii, określonych w intencji.
Wszystkie te informacje są porównywane z zawartością filtrów intencji zapisanych w pliku AndroidManifest.xml, którym
dysponuje każda aplikacja na Androida. Aby aktywność mogła odbierać i obsługiwać intencje niejawne, musi mieć
kategorię o wartości DEFAULT.
 Metoda createChooser() pozwala przesłonić domyślne systemowe okno dialogowe wyboru aktywności. Metoda ta
pozwala określić tytuł i nie zapewnia użytkownikowi możliwości ustawienia aktywności domyślnej. Jeśli żadna aktywność
nie jest w stanie obsłużyć intencji, to metoda wyświetli stosowny komunikat. Metoda createChooser() zwraca obiekt
klasy Intent.
 Wartość zasobu łańcuchowego można pobrać, używając wywołania getString(R.string.nazwalancucha);.

114 Rozdział 3.
4. Cykl życia aktywności

Była sobie aktywność


…wtedy mu
powiedziałam, że jeśli zaraz
nie wykona onStop(), to
pokażę mu, co z nim zrobi
moja onDestroy().

Aktywności stanowią podstawę wszystkich aplikacji na Androida.


Wiesz już, jak tworzyć aktywności i jak sprawić, by jedna aktywność uruchomiła drugą, używając
do tego celu intencji. Ale co tak naprawdę dzieje się za kulisami? W tym rozdziale nieco
dokładniej poznamy cykl życia aktywności. Co się dzieje, kiedy aktywność jest tworzona
i usuwana? Jakie metody są wywoływane, gdy aktywność jest wyświetlana i pojawia się na
ekranie, a jakie gdy aktywność traci miejsce wprowadzania i jest ukrywana? W jaki sposób
można zapisywać i odtwarzać stan aktywności?

to jest nowy rozdział  115


Jak działają aktywności?

Jak właściwie działają aktywności?


Dotychczas dowiedziałeś się jedynie, jak można tworzyć aktywności, które prowadzą interakcję
z użytkownikami, i aplikacje używające kilku aktywności do wykonywania akcji. Skoro dodałeś
już te podstawowe umiejętności do swojego przybornika z narzędziami, nadszedł czas, by nieco
dokładniej przyjrzeć się temu, jak tak naprawdę działają aktywności. Poniżej zamieściliśmy
podsumowanie tego, co już wiesz, dodając do niego kilka dodatkowych szczegółów.

 Aplikacja jest kolekcją aktywności, układów oraz innych zasobów.


Jedna z tych aktywności jest główną aktywnością aplikacji.

Aplikacja

Każda aplikacja ma aktywność


główną, wskazaną w pliku
manifestu AndroidManifest.xml.
Aktywność główna
Aktywność

Aktywność
Aktywność

 Domyślnie każda aplikacja jest uruchamia w odrębnym, własnym procesie.


Dzięki temu łatwiej jest zapewnić bezpieczeństwo aplikacji. Więcej informacji na ten temat
można znaleźć w „Dodatku A” (poświęconym środowisku uruchomieniowemu Androida — ART),
umieszczonym na końcu książki.
Proces 1 Proces 2
Aplikacja 1 Aplikacja 2

Aktywność Aktywność
Aktywność

Aktywność

Aktywność Aktywność
Aktywność

116 Rozdział 4.
Cykl życia aktywności

 Można uruchomić aktywność w innej aplikacji, przekazując,


przy użyciu metody startActivity(), odpowiednią intencję.
System Android dysponuje informacjami o wszystkich zainstalowanych aplikacjach
i o ich aktywnościach i potrafi użyć intencji do uruchomienia odpowiedniej aktywności.

Aplikacja 1 Aplikacja 2

startActivity()

Intencja
Aktywność Aktywność
Intencja
Aktywność Aktywność
Android

Aktywność Aktywność

 Kiedy trzeba uruchomić jakąś aktywność, Android sprawdza,


czy istnieje już proces danej aplikacji.
Jeśli taki proces istnieje, to system uruchamia w nim wybraną aktywność.
W przeciwnym razie, jeśli procesu nie ma, to system go utworzy.
Proces 1
Aplikacja 1
Już wykonuję aktywności
tej aplikacji w ramach
procesu 1. Zatem tę
aktywność także uruchomię
w tym procesie.
Android

 Kiedy Android uruchamia aktywność, wywołuje jej metodę onCreate().


Metoda onCreate() jest zawsze wywoływana w momencie tworzenia aktywności.

onCreate()

Aktywność
Android

Niemniej jednak cały czas nie wiemy jeszcze wielu rzeczy na temat sposobu
działania aktywności. Jak długo istnieją aktywności? Co się dzieje, kiedy
aktywność znika z ekranu? Czy wciąż działa? Czy wciąż zajmuje miejsce
w pamięci? I co się dzieje, gdy działanie aplikacji zostaje przerwane przez
nadchodzącą rozmowę telefoniczną? Chcielibyśmy mieć możliwość kontrolowania
działania aktywności w całym zakresie różnych sytuacji. Ale jak to zrobić?

jesteś tutaj  117


Stoper

Aplikacja stopera
W tym rozdziale przyjrzymy się dokładniej tajnikom działania aktywności,
często spotykanym przyczynom problemów z aplikacjami oraz sposobom
rozwiązywania tych problemów za pomocą metod cyklu życia aktywności.
Metody te mamy zamiar poznać, pisząc prostą aplikację — Stoper.

Nasza aplikacja będzie się składała z jednej aktywności i jednego układu.


Układ będzie zawierał tekst pokazujący, ile czasu upłynęło, oraz przyciski:
Start (rozpoczynający pomiar czasu), Stop (zatrzymujący stoper) oraz Kasuj
(zerujący licznik czasu).

To jest liczba
sekund.

Kiedy klikniesz
przycisk Start, liczba
sekund zaczyna być
inkrementowana.

Kiedy klikniesz
przycisk Stop, liczba
sekund przestaje być
inkrementowana.

Kiedy klikniesz przycisk


Kasuj, liczba sekund
zostaje zmniejszona do 0.

Implementacja aplikacji <Layout>

Dysponujesz już dostateczną wiedzą i doświadczeniem, </Layout>

by samodzielnie napisać tę aplikację, bez większej pomocy


activity_stopwatch.xml
z naszej strony. Pokażemy tu tylko tyle kodu, ile potrzebujesz,
by móc ją w całości zaimplementować, a następnie sprawdzić, Aplikacja składa
się z jednej
co się stanie, kiedy ją uruchomisz. aktywności
i jednego układu.
Zacznij od utworzenia nowego projektu aplikacji na Androida
o nazwie Stoper, która będzie umieszczona w pakiecie
com.hfad.stoper. Wybierz minimalny poziom API
o wartości 15, tak by aplikacja mogła działać na przeważającej
StopwatchActivity.java
większości urządzeń. Utwórz także aktywność o nazwie
StopwatchActivity i układ o nazwie activity_stopwatch.

118 Rozdział 4.
Cykl życia aktywności

Kod układu aplikacji stopera


Poniżej przedstawiliśmy kod XML układu aplikacji. Składa się on z jednego
widoku tekstowego używanego do wyświetlania czasu zmierzonego przez
stoper oraz trzech przycisków kontrolujących działanie stopera. Zastąp zatem
zawartość swojego pliku activity_stopwatch.xml poniższym kodem XML:

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent” Stoper
android:paddingBottom=”16dp”
android:paddingLeft=”16dp” app/src/main

android:paddingRight=”16dp”
res
android:paddingTop=”16dp”
tools:context=”.StopwatchActivity” > layout
<xml>

Tego komponentu będziemy </xml>


<TextView używać do wyświetlania activity_
android:id=”@+id/time_view” liczby sekund. stopwatch.xml

android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentTop=”true”
android:layout_centerHorizontal=”true”
android:layout_marginTop=”0dp”
android:text=””
android:textAppearance=”?android:attr/textAppearanceLarge”
android:textSize=”92sp” /> Te atrybuty sprawią, że czas
wyświetlany przez nasz stoper
będzie ładny i duży.
<Button
android:id=”@+id/start_button”
Ten kod dotyczy przycisku
android:layout_width=”wrap_content” Start. Jego kliknięcie
android:layout_height=”wrap_content” powoduje wywołanie
metody onClickStart().
android:layout_below=”@+id/time_view”
android:layout_centerHorizontal=”true”
android:layout_marginTop=”32dp”
android:onClick=”onClickStart” Dalsza część
kodu układu
android:text=”@string/start” /> znajduje się
na następnej
stronie.

jesteś tutaj  119


Kod układu

Kod układu (ciąg dalszy)


Stoper
<Button
android:id=”@+id/stop_button” app/src/main
android:layout_width=”wrap_content”
res
android:layout_height=”wrap_content”
android:layout_below=”@+id/start_button” layout
Ten kod dotyczy przycisku
android:layout_centerHorizontal=”true” Stop. Jego kliknięcie <xml>
</xml>
powoduje wywołanie metody
android:layout_marginTop=”17dp” onClickStop(). activity_
android:onClick=”onClickStop” stopwatch.xml

android:text=”@string/stop” />

Ten kod dotyczy przycisku Kasuj.


<Button Jego kliknięcie powoduje wywołanie
metody onClickReset().
android:id=”@+id/reset_button”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_below=”@+id/stop_button” Zrób to sam!
android:layout_centerHorizontal=”true”
android:layout_marginTop=”17dp”
android:onClick=”onClickReset”
Zanim przejdziesz do
android:text=”@string/reset” />
dalszych prac nad aplikacją,
koniecznie zaktualizuj
</RelativeLayout> plik układu i plik zasobów
strings.xml.
Plik strings.xml aplikacji stopera
Nasz układ korzysta z trzech dodatkowych łańcuchów znaków — każdy
z nich określa tekst jednego przycisku. Łańcuchy te zostaną zdefiniowane
jako zasoby łańcuchowe, co oznacza, że będziesz musiał je zapisać w pliku Stoper
strings.xml. A zatem dodaj do tego pliku poniższe wiersze kodu:
app/src/main
...
<string name=”start”>Start</string> res
To są etykiety
<string name=”stop”>Stop</string> przycisków.
<string name=”reset”>Kasuj</string> values
<xml>
... </xml>

strings.xml
Układ jest gotowy! Teraz zajmijmy się aktywnością.

120 Rozdział 4.
Cykl życia aktywności

Jak będzie działał kod aktywności?


Układ aplikacji definiuje trzy przyciski, których będziemy używać do
obsługi stopera. Każdy z tych przycisków korzysta z atrybutu onClick
i przy jego użyciu określa metodę aktywności, którą należy wywołać
w momencie kliknięcia danego przycisku. W przypadku kliknięcia
przycisku Start zostanie wywołana metoda onClikStart(), kliknięcie
przycisku Stop spowoduje wywołanie metody onClickStop(),
a rezultatem kliknięcia przycisku Kasuj będzie wywołanie metody
onClickReset(). Tych trzech metod będziemy używali do
uruchamiania, zatrzymywania oraz zerowania stopera.

Kiedy klikniesz przycisk Start, zostanie


wywołana metoda onClickStart().

Kiedy klikniesz przycisk Stop, zostanie


wywołana metoda onClickStop().

Kiedy klikniesz przycisk Kasuj, zostanie


wywołana metoda onClickReset().

Sam stoper będziemy aktualizować przy użyciu metody o nazwie


runTimer(). Będzie ona wywoływana co sekundę, by sprawdzić,
czy stoper działa, i inkrementować liczbę sekund przechowywaną
w aplikacji i wyświetlaną w widoku tekstowym. runTimer()

Obsługę aplikacji ułatwimy sobie, używając dwóch zmiennych


Aktywność
prywatnych, które będą przechowywały stan stopera. Pierwszą
z nich będzie zmienna typu int o nazwie seconds, która będzie
przechowywała liczbę sekund, jaka upłynęła od momentu
włączenia stopera, a drugą zmienna typu boolean o nazwie
running, określająca, czy stoper aktualnie działa, czy nie.

Zaczniemy od napisania kodu obsługującego przyciski,


a następnie zajmiemy się metodą runTimer().

jesteś tutaj  121


Przyciski

Dodanie kodu obsługującego przyciski


running=true
Kiedy użytkownik kliknie przycisk Start, przypiszemy zmiennej running
wartość true, dzięki czemu stoper zacznie działać. Następnie, gdy
użytkownik kliknie przycisk Stop, zmienimy wartość zmiennej running running=false
na false, co spowoduje zatrzymanie stopera. Kiedy zaś użytkownik
kliknie przycisk Kasuj, przypiszemy zmiennej running wartość false,
a zmiennej seconds wartość 0, co spowoduje, że stoper zostanie running=false
zatrzymany i wyzerowany. seconds=0

Zastąp zawartość swojego pliku StopwatchActivity.java następującym kodem:

package com.hfad.stoper;

import android.os.Bundle;
Stoper
import android.app.Activity;
import android.view.View;
app/src/main

public class StopwatchActivity extends Activity {


java

private int seconds = 0; Używamy zmiennych seconds


i running do przechowywania liczby com.hfad.stoper
private boolean running; sekund, które upłynęły, i informacji,
czy stoper jest włączony, czy nie.
@Override Stopwatch
Activity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
}

// Metoda uruchamia stoper po kliknięciu przycisku Start


public void onClickStart(View view) {
Ta metoda jest wywoływana
running = true; po kliknięciu przycisku Start.
Ta instrukcja uruchamia stoper.
}

// Metoda zatrzymuje stoper po kliknięciu przycisku Stop


public void onClickStop(View view) { Ta metoda jest wywoływana
running = false; po kliknięciu przycisku Stop.
Ta instrukcja zatrzymuje stoper.
}

// Metoda zeruje stoper po kliknięciu przycisku Kasuj


public void onClickReset(View view) { Ta metoda jest wywoływana
po kliknięciu przycisku Kasuj.
running = false; Te instrukcje zatrzymują
seconds = 0; stoper i przypisują liczbie
sekund wartość 0.
}
}
122 Rozdział 4.
Cykl życia aktywności

Metoda runTimer()
Kolejną rzeczą, którą musimy napisać, jest metoda runTimer(). Metoda ta
pobierze referencję do komponentu TextView umieszczonego w układzie
aplikacji, sformatuje liczbę sekund do postaci łańcucha znaków prezentującego
liczbę godzin, minut i sekund, a następnie wyświetli ten łańcuch znaków
w układzie. Oprócz tego, jeśli wartość zmiennej running będzie wynosiła true,
to metoda runTimer() dodatkowo inkrementuje wartość zmiennej seconds.
Poniżej przedstawiliśmy kod tej metody:
Pobranie referencji do komponentu
private void runTimer() { TextView.
final TextView timeView = (TextView)findViewById(R.id.time_view);
...
int hours = seconds/3600;
int minutes = (seconds%3600)/60; Ten fragment formatuje liczbę
W tych
miejscach int secs = seconds%60; sekund do postaci godzin,
pominęliśmy minut i sekund. To zwyczajny
krótkie String time = String.format(”%d:%02d:%02d”, kod napisany w Javie.
fragmenty hours, minutes, secs);
kodu.
Przedstawimy timeView.setText(time);
je na Ta instrukcja wyświetla tekst w komponencie TextView.
następnej if (running) {
stronie. Jeśli zmienna running ma
seconds++;
wartość true, to ta instrukcja
} inkrementuje wartość zmiennej
seconds.
...
}

Musimy zapewnić, aby kod metody runTimer() był wykonywany cyklicznie,


tak by co sekundę inkrementował wartość zmiennej seconds i aktualizował
wyświetlany czas zmierzony przez stoper. Jednocześnie musimy to zrobić
w taki sposób, by nie zablokować głównego wątku aplikacji.

W aplikacjach pisanych w innych językach programowania niż Java takie


zadania można wykonywać przy użyciu wątków działających w tle. Jednak
w Androidowie takie rozwiązania nastręczają nieco problemów, gdyż tylko
główny wątek aplikacji może aktualizować jej interfejs użytkownika — jeśli
spróbuje to zrobić jakikolwiek inny wątek, to zostanie zgłoszony wyjątek
CalledFromWrongThreadException.

Rozwiązaniem jest zastosowanie obiektu klasy Handler. Przyjrzymy się mu


dokładniej na następnej stronie.

jesteś tutaj  123


Obiekty Handler

Obiekty Handler umożliwiają planowanie


wykonania kodu
Handler to klasa systemu Android umożliwiająca tworzenie kodu, który ma
zostać wykonany w przyszłości. Można jej także używać do przekazywania kodu,
który ma zostać wykonany w innym wątku. W naszym przypadku zastosujemy tę
klasę do utworzenia kodu stopera, który będzie wykonywany co sekundę.

By skorzystać z klasy Handler, kod, którego wykonanie chcemy zaplanować,


musimy umieścić w obiekcie Runnable, a następnie użyć metody post() lub
postDelayed() klasy Handler, by określić, kiedy kod ma zostać wykonany.
Przyjrzyjmy się nieco dokładniej każdej z tych metod.

Metoda post()
Metoda post() przekazuje kod, który ma zostać wykonany najszybciej jak to
możliwe (czyli zazwyczaj niemal natychmiast). Metoda ta ma jeden parametr
— obiekt typu Runnable. W Androidowie obiekty Runnable odpowiadają
obiektom tego samego typu stosowanym w zwyczajnych programach pisanych
w Javie — reprezentują zadanie, które należy wykonać. Kod, który chcemy
wykonać, należy umieścić w metodzie run() obiektu Runnable, a obiekt
Handler zadba o to, by został on wykonany możliwie jak najszybciej.
Oto, jak wygląda implementacja takiego rozwiązania:

final Handler handler = new Handler();


handler.post(Runnable); Kod, który chcemy wykonać, należy umieścić
w metodzie run() obiektu Runnable.

Metoda postDelayed()
Metoda postDelayed() działa bardzo podobnie do metody post(), z tym,
że używamy jej do przesyłania kodu, który ma zostać wykonany w przyszłości.
Metoda postDelayed() ma dwa parametry: obiekt Runnable oraz liczbę typu
long. Obiekt Runnale, w swojej metodzie run(), zawiera kod, który należy
wykonać, natomiast liczba określa o ile milisekund chcemy opóźnić jego
wykonanie. Kod zostanie wykonany możliwie jak najszybciej po upłynięciu
czasu określonego przez opóźnienie. Poniżej przedstawiliśmy przykład
zastosowania tej metody:

final Handler handler = new Handler();


Tej metody można użyć, aby opóźnić
handler.postDelayed(Runnable, long); wykonanie kodu o określoną liczbę milisekund.

Na następnej stronie użyjemy tych metod do cyklicznego


aktualizowania stopera.

124 Rozdział 4.
Cykl życia aktywności

Pełny kod metody runTimer()


W celu aktualizacji stopera będziemy cyklicznie planowali wykonanie kodu z użyciem
obiektu Handler, za każdym razem stosując opóźnienie o długości 1000 milisekund.
Każde wykonanie kodu będzie powodowało inkrementację wartości zmiennej seconds
i aktualizację tekstu wyświetlanego w widoku tekstowym.

Poniżej przedstawiamy pełny kod metody runTimer():

private void runTimer() {


final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler(); Tworzy nowy obiekt Handler.
handler.post(new Runnable() { To wywołanie metody post() i przekazanie do niej nowego
obiektu typu Runnable(). Metoda post() wykonuje kod bez
@Override opóźnienia, zatem kod umieszczony w obiekcie Runnable
public void run() { zostanie wykonany niemal natychmiast.
int hours = seconds/3600;
int minutes = (seconds%3600)/60;
Metoda run() obiektu Runnable
int secs = seconds%60; zawiera kod, który chcemy
wykonać — w naszym
String time = String.format(”%d:%02d:%02d”, przypadku jest to kod, który
hours, minutes, secs); aktualizuje liczbę sekund
timeView.setText(time); wyświetloną w widoku
tekstowym.
if (running) {
seconds++;
} To wywołanie przekazuje kod w obiekcie Runnable
i planuje jego wykonanie z opóźnieniem wynoszącym
handler.postDelayed(this, 1000); 1000 milisekund, czyli 1 sekundę. Ponieważ ten
wiersz kodu jest umieszczony wewnątrz metody run()
} obiektu Runnable, będzie on wykonywany cyklicznie.
});
}

Zastosowanie metod post() i postDelayed() oznacza, że przekazany kod będzie


wykonywany najszybciej jak to możliwe, po upływie podanego opóźnienia. W praktyce
oznacza to, że będzie on wykonany niemal natychmiast po upływie zadanego okresu czasu.
Choć oznacza to, że im dłużej będzie działała aplikacja, tym opóźnienia mierzonego czasu
będą większe, to jednak rozwiązanie to będzie wystarczająco dokładne jak na potrzeby
zamieszczonej w tym rozdziale prezentacji cyklu życia aktywności.

Metodę runTimer() uruchomimy w momencie tworzenia aktywności StopwatchActivity,


a to oznacza, że wywołamy ją w metodzie onCreate() aktywności:

protected void onCreate(Bundle savedInstanceState) {


...
runTimer();
}
Pełny kod aktywności zamieściliśmy na następnej stronie.

jesteś tutaj  125


Kod aktywności StopwatchActivity

Kompletny kod aktywności StopwatchActivity


Poniżej przedstawiliśmy kompletny kod pliku StopwatchActivity.java.
Zaktualizuj ten plik w swojej aplikacji, by miał tę samą zawartość.

package com.hfad.stoper;

Stoper
import android.os.Bundle;
import android.os.Handler; app/src/main
import android.app.Activity;
java
import android.view.View;
import android.widget.TextView; com.hfad.stoper

public class StopwatchActivity extends Activity { Stopwatch


// Liczba sekund wyświetlana przez stoper Activity.java

private int seconds = 0;


Zmiennych seconds i running
// Czy stoper działa? używamy odpowiednio do
przechowywania liczby zmierzonych
private boolean running; sekund i informacji, czy aktualnie
stoper działa, czy nie.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
runTimer();
Do aktualizacji stopera używamy innej
} metody. Wywołujemy ją w momencie
tworzenia aktywności.

// Metoda uruchamia stoper po kliknięciu przycisku Start


public void onClickStart(View view) {
Ta metoda jest wywoływana
running = true; Ta instrukcja uruchamia stoper. po kliknięciu przycisku Start.
}

// Metoda zatrzymuje stoper po kliknięciu przycisku Stop


public void onClickStop(View view) { Ta metoda jest wywoływana
po kliknięciu przycisku Stop.
running = false; Ta instrukcja zatrzymuje stoper.
}

126 Rozdział 4.
Cykl życia aktywności

Kod aktywności (ciąg dalszy)


// Metoda zeruje stoper po kliknięciu przycisku Kasuj
public void onClickReset(View view) { Ta metoda jest Stoper
running = false; wywoływana
Te instrukcje zatrzymują po kliknięciu
seconds = 0; stoper i zerują liczbę przycisku Kasuj. app/src/main
zmierzonych sekund.
}
java
Ta instrukcja pobiera
referencję do widoku
// Wyświetla w stoperze liczbę sekund tekstowego. com.hfad.stoper
private void runTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view); Stopwatch
final Handler handler = new Handler(); Activity.java

handler.post(new Runnable() { Tu używamy obiektu Handler


@Override do przekazania kodu.

public void run() {


int hours = seconds/3600;
int minutes = (seconds%3600)/60; Ten fragment formatuje liczbę
sekund do postaci godzin,
int secs = seconds%60; minut i sekund.
String time = String.format(”%d:%02d:%02d”, hours, minutes, secs);
timeView.setText(time);
Ta instrukcja wyświetla tekst w komponencie TextView.
if (running) {
seconds++; Jeśli zmienna running ma wartość true,
to ta instrukcja inkrementuje wartość
} zmiennej seconds.

handler.postDelayed(this, 1000); To wywołanie ponownie przekazuje kod do


wywołania, określając przy tym opóźnienie
} o długości 1 sekundy.
});
}
}
Zrób to sam!
Zobaczmy, co się stanie, kiedy wykonamy ten kod.

Upewnij się, że zaktualizowałeś


kod swojej aktywności,
wprowadzając w nim wszystkie
pokazane zmiany.

jesteś tutaj  127


Co się dzieje?

Co się dzieje w trakcie działania aplikacji?


1 Użytkownik decyduje się uruchomić aplikację.
Klika zatem ikonę aplikacji.

Urządzenie
Użytkownik

2 Plik AndroidManifest.xml aplikacji określa, którą aplikację należy wykonać


podczas uruchamiania aplikacji.
System tworzy intencję, która spowoduje uruchomienie aktywności, i przekazuje ją
w wywołaniu metody startActivity(intent).

<xml>
</xml>

AndroidManifest.xml Android

3 Android sprawdza, czy jest uruchomiony inny proces tej samej aplikacji,
a jeśli takiego nie ma, to tworzy nowy proces.
Następnie system tworzy nowy obiekt aktywności — w naszym przypadku będzie to
obiekt aktywności StopwatchActivity.
Proces 1
Aplikacja 1

Android

128 Rozdział 4.
Cykl życia aktywności

Historii ciąg dalszy


4 Zostaje wywołana metoda onCreate() aktywności.
Kod onCreate() wywołuje metodę setContnetView(), określając używany układ,
a następnie uruchamia stoper, wywołując w tym celu metodę runTimer().

runTimer() <xml>
</xml>

StopwatchActivity Układ

5 Po zakończeniu wykonywania metody onCreate() na ekranie urządzenia zostaje


wyświetlony układ.
Metoda runTimer() określa tekst, który należy wyświetlić w widoku tekstowym na podstawie
wartości zmiennej seconds. Natomiast zmienna running pozwala jej określić, czy należy
inkrementować liczbę sekund, czy nie. Ponieważ zmienna running ma początkowo wartość
false, liczba sekund nie jest inkrementowana.

seconds=0

StopwatchActivity

Urządzenie running=false

Nie istnieją
głupie pytania

P: Dlaczego Android uruchamia aplikację w odrębnym P : Czy nie można by napisać w metodzie onCreate()
procesie? pętli, która aktualizowałaby stoper?

O: Ze względów bezpieczeństwa i dla zapewnienia stabilności. O: Nie. Wykonywanie metody onCreate() musi się zakończyć
Uniemożliwia to aplikacji uzyskanie dostępu do danych innych przed wyświetleniem ekranu aplikacji. Umieszczenie w niej
aplikacji. Oprócz tego oznacza to, że jeśli aplikacja ulegnie awarii, nieskończonej pętli uniemożliwiłoby zatem wyświetlenie układu.
nie będzie to miało zgubnych konsekwencji dla innych aplikacji.
P: Metoda runTimer() wygląda na naprawdę złożoną.
P: Do czego służy metoda onCreate()? Nie prościej by Czy naprawdę trzeba wykonywać te wszystkie operacje?

O: Fakt, jest trochę skomplikowana, jednak za każdym razem,


było umieszczać kod w konstruktorze?

O: Android musi przygotować środowisko na potrzeby gdy będziemy musieli zaplanować wykonanie kodu, używane
aktywności już po jej utworzeniu. Kiedy aktywność jest już gotowa, rozwiązanie będzie wyglądało podobnie do tego z metody
Android wywołuje jej metodę onCreate(). To właśnie dlatego runTimer().
kod przygotowujący ekran aplikacji jest wykonywany w metodzie
onCreate(), a nie w konstruktorze aktywności.

jesteś tutaj  129


Jazda próbna

Jazda próbna aplikacji


Kiedy uruchomimy tę aplikację w emulatorze, będzie działała doskonale.
Możemy uruchomić stoper, zatrzymać go, wyzerować, a wszystko to bez
najmniejszych problemów. Innymi słowy: aplikacja działa dokładnie
według oczekiwań.

Przyciski działają zgodnie z oczekiwaniami.


Kliknięcie przycisku Start rozpoczyna
działanie stopera, kliknięcie przycisku
Stop zatrzymuje go, a kliknięcie przycisku
Kasuj powoduje wyzerowanie stopera.

Ale jest jeden problem…


Kiedy uruchomimy aplikację na fizycznym urządzeniu, będzie ona
działała prawidłowo, dopóki nie obrócimy urządzenia. Gdy to zrobimy,
stoper zostanie wyzerowany.

Stoper działał, lecz po obróceniu


urządzenia został wyzerowany.

W androidowych aplikacjach wyjątkowo często występują problemy


po obróceniu urządzenia. Zanim zajmiemy się rozwiązaniem tego
problemu, przyjrzymy się dokładniej jego przyczynie.

130 Rozdział 4.
Cykl życia aktywności

Co się stało?
A więc co się stało, że po obróceniu urządzenia w aplikacji wystąpił błąd?

Przyjrzyjmy się nieco dokładniej temu, co się w niej stało.

1 Użytkownik uruchamia aplikację i klika przycisk Start, by rozpocząć działanie stopera.


Metoda runTimer() rozpoczyna inkrementację liczby sekund wyświetlanych w widoku tekstowym
o identyfikatorze time_view, wykorzystując przy tym obie zmienne: seconds i running.

seconds=12

StopwatchActivity

Urządzenie running=true

2 Użytkownik obraca urządzenie.


Android zauważa, że zmieniła się orientacja urządzenia i wielkość ekranu, i w efekcie usuwa
działającą aktywność, włącznie ze zmiennymi używanymi przez metodę runTimer().

Urządzenie

3 Aktywność StopwatchActivity zostaje ponownie utworzona.


Metoda onCreate() aktywności zostaje ponownie wykonana, co powoduje wywołanie
metody runTimer(). Ponieważ aktywność została ponownie utworzona, zmienne
seconds i running przyjmują wartości domyślne.

Zmiennej seconds zostaje


przypisana wartość 0,
seconds=0 a zmiennej running
wartość false. Ta zmiana
jest wynikiem usunięcia
StopwatchActivity i ponownego utworzenia
aktywności, spowodowanych
Urządzenie obróceniem urządzenia.
running=false
jesteś tutaj  131
Konfiguracje urządzenia

Obrót ekranu zmienia konfigurację urządzenia


Kiedy Android uruchamia aplikację i rozpoczyna wykonywanie aktywności,
uwzględnia przy tym konfigurację urządzenia. Konfiguracja ta obejmuje
zarówno konfigurację fizycznego urządzenia (czyli takie jego aspekty jak
wielkość ekranu, orientacja ekranu, dostępność podłączonej klawiatury),
jak i opcje konfiguracyjne określane przez użytkownika (takie jak wybrane
ustawienia lokalne).

Android musi znać konfigurację urządzenia w momencie uruchamiania


aktywności, gdyż może ona mieć wpływ na zasoby niezbędne dla działania
aplikacji. Na przykład inny układ może być używany, gdy urządzenie jest
trzymane w pionie, a inny gdy jest trzymane w poziomie, i podobnie inne
zasoby łańcuchowe mogą być używane w przypadku, kiedy na urządzeniu Konfiguracja urządzenia
zostaną wybrane francuskie ustawienia lokalne.
obejmuje opcje podane
przez użytkownika
(takie jak ustawienia
lokalne), jak również opcje
związane z fizycznym
urządzeniem (takie jak
jego orientacja i wielkość
Każda aplikacja
na Androida może ekranu). Zmiana dowolnej
zawierać wiele
plików zasobów, spośród tych opcji
przechowywanych
w katalogu /app/src/
main/res. Na przykład
spowoduje usunięcie
jeśli w urządzeniu
zostaną wybrane i ponowne odtworzenie
francuskie ustawienia
lokalne, to aplikacja
będzie używała pliku
aktywności.
strings.xml pobranego
z katalogu values-fr.

Kiedy zmienia się konfiguracja urządzenia, trzeba zaktualizować


wszystko, co prezentuje interfejs użytkownika, tak by odpowiadał
on nowej konfiguracji. Jeśli obrócimy urządzenie, Android
zauważy zmianę orientacji i wielkości ekranu i uzna to za zmianę
konfiguracji urządzenia. W efekcie system usunie aktualną aktywność
i ponownie ją utworzy, aby można było pobrać i zastosować zasoby
odpowiadające bieżącej konfiguracji urządzenia.

132 Rozdział 4.
Cykl życia aktywności

Od narodzin do śmierci: stany aktywności


Kiedy Android tworzy i usuwa aktywność, zmienia się jej stan: początkowo jest
uruchomiona, potem działa, a w końcu zostaje zniszczona.

Podstawowym stanem aktywności jest stan działania lub aktywności. Aktywność


działa, kiedy jest wyświetlona na ekranie (znajduje się na pierwszym planie),
dysponuje miejscem wprowadzania (ang. input focus), a użytkownik może z nią
prowadzić interakcje. Większość swojego życia aktywność spędza właśnie w tym
stanie. Aktywność zaczyna działać po wcześniejszym uruchomieniu, a potem,
pod koniec swojego istnienia, zostanie usunięta (lub zniszczona).

Aktywność Obiekt aktywności został już


utworzony, ale jeszcze nie działa.
uruchomiona

Aktywność Większość swojego Aktywność działa,


życia aktywność
działająca spędza w tym stanie. kiedy znajduje się na
pierwszym planie,
czyli jest widoczna
Aktywność Na tym etapie na ekranie.
aktywność już
usunięta nie istnieje.
Metoda onCreate()
Kiedy stan aktywności zmienia się z utworzonej na usuniętą, wykonywane jest wywoływana
są dwie kluczowe metody cyklu życia aktywności: onCreate() i onDestroy(). w momencie
Są to metody cyklu życia każdej aplikacji, które są dziedziczone i które,
w razie takie potrzeby, można przesłonić.
pierwszego tworzenia
aktywności i stanowi
Metoda onCreate() jest wywoływana bezpośrednio po uruchomieniu aktywności.
To właśnie w niej można wykonywać wszystkie standardowe czynności miejsce, w którym
związane z przygotowaniem działania aktywności, takie jak wywołanie metody aktywność jest
setContentView(). Tę metodę zawsze należy przesłaniać — w przeciwnym razie
przygotowywana
nie będziemy w stanie poinstruować systemu, którego układu używa aktywność.
do działania.
Z kolei onDestroy() to ostatnia metoda aktywności, która zostaje wywołana
przed jej ostatecznym usunięciem. Istnieje kilka różnych sytuacji, które mogą
skutkować usunięciem aplikacji — na przykład gdy nakazano jej zakończenie,
Metoda onDestroy()
gdy trzeba ją ponownie utworzyć z powodu zmiany konfiguracji urządzenia jest wywoływana
albo gdy Android zdecyduje się usunąć aktywność, aby zaoszczędzić miejsce.
przed usunięciem
Na następnej stronie przyjrzymy się powiązaniom tych dwóch metod ze stanami aktywności.
aktywności.
jesteś tutaj  133
Od narodzin do śmierci

Cykl życia aktywności: od utworzenia do usunięcia


Poniżej przedstawiony został cykl życia aktywności, od jej narodzin do śmierci. Jak się
przekonasz w dalszej części rozdziału, pominęliśmy tu kilka szczegółów, lecz na razie
koncentrujemy się wyłącznie na metodach onCreate() i onDestroy().

Aktywność 1 Aktywność zostaje uruchomiona.


uruchomiona System tworzy obiekt aktywności i zostaje
wywołany jej konstruktor.

2 Bezpośrednio po uruchomieniu aktywności


zostaje wywołana jej metoda onCreate().
onCreate() Metoda onCreate() jest miejscem, w którym należy
umieścić cały kod inicjujący działanie aktywności,
gdyż jest ona zawsze wywoływana po uruchomieniu
aktywności, lecz zanim zacznie ona działać.

Aktywność 3 Aktywność działa, kiedy jest widoczna


na ekranie i użytkownik może prowadzić
działająca z nią interakcję.
To właśnie w tym stanie aktywność spędza
przeważającą większość swego życia.

4 Metoda onDestroy() jest wywoływana


onDestroy() bezpośrednio przed usunięciem aktywności.
Metoda onDestroy() pozwala wykonać ostateczne
porządki po aktywności, na przykład zwolnić
używane przez nią zasoby.

Aktywność 5 Po wykonaniu metody onDestroy() aktywność


jest usuwana.
usunięta W tym momencie aktywność przestaje istnieć.

134 Rozdział 4.
Cykl życia aktywności

Twoja aktywność dziedziczy metody cyklu życia


Jak już wspomnieliśmy, wszystkie aktywności rozszerzają klasę android.app.Activity.
To właśnie ta klasa zapewnia naszym aktywnościom dostęp do metod cyklu życia.

Context Abstrakcyjna klasa Context


(android.content.Context)
startActivity(Intent) i. Zapewnia
Interfejs do globalnych informacji na temat środowiska aplikacj i.
dostęp do zasobów aplikacj i, klas oraz operacji na poziomie aplikacj

ContextWrapper Klasa ContextWrapper


(android.content.ContextWrapper)
startActivity(Intent) Implementacja pośrednika do klasy
Context.

ContextThemeWrapper Klasa ContextThemeWrapper


(android.view.ContextThemeWrapper)
Klasa ContextThemeWrapper umożliwia modyfikowanie
motywu z poziomu zawartości obiektu ContextWrapper.

Activity Klasa Activity


(android.app.Activity)
onCreate(Bundle)
Klasa Activity zawiera domyślne implementacje
onCreateOptionsMenu(Menu) metod cyklu życia aktywności. Oprócz tego definiuje
także takie metody jak findViewById(Int)
onStart() i setContentView(View).
onRestart()
onResume()
onPause()
onStop()
onDestroy()
onSaveInstanceState()
startActivity(Intent)
findViewById(Int)
setContentView(View)

MojaAktywnosc Klasa MojaAktywnosc


(com.hfad.foo)
onCreate(Bundle)
Większość zachowań aktywności jest obsługiwanych przez metody
onCreateOptionsMenu(Menu) klasy bazowej. Nam pozostaje jedynie przesłonięcie tych metod,
które będą nam potrzebne.
mojaMetoda()

jesteś tutaj  135


Pomijać, ale ostrożnie

W jaki sposób radzić sobie ze zmianami konfiguracji?


Jak się przekonałeś, aplikacja stopera przestała działać prawidłowo, gdy użytkownik
obrócił urządzenie. Aktywność została usunięta i ponownie utworzona, a to oznaczało
usunięcie używanych przez nią zmiennych. W jaki sposób można rozwiązać ten problem?
W jaki sposób mamy radzić sobie ze zmianami konfiguracji urządzenia, takimi jak zmiana
orientacji ekranu?

Istnieją dwa rozwiązania: można nakazać, żeby system pominął ponowne uruchamianie
aktywności, albo można zapisać stan aktywności w taki sposób, by nowa aktywność mogła
go odtworzyć. Teraz przyjrzymy się obu tym rozwiązaniom nieco dokładniej.

Pominięcie ponownego utworzenia aktywności


Z tego sposobu
Pierwszym rozwiązaniem jest nakazanie systemowi, by nie uruchamiał ponownie
obsługi zmian
aktywności po wykryciu zmiany konfiguracji urządzenia. Choć pokażemy Ci, jak to
zrobić, to jednak musisz pamiętać, że zazwyczaj nie jest to najlepsze rozwiązanie.
Obejrzyj to! konfiguracji można
korzystać wyłącznie
Wynika to z faktu, że gdy Android odtwarza aktywność, upewnia się, czy będzie w ostateczności.
ona korzystać z zasobów odpowiadających bieżącej konfiguracji urządzenia. Może
się zatem okazać, że jeśli pominiemy ten systemowy mechanizm, będziemy musieli Powoduje on bowiem pomijanie
sami napisać dodatkowy kod niezbędny do obsługi nowej konfiguracji. standardowego sposobu działania
systemu, co może być przyczyną
Aby poinformować Android, że nie powinien odtwarzać aktywności po zmianie problemów.
konfiguracji urządzenia, należy dodać do elementu activity w pliku manifestu
AndroidManifest.xml poniższy atrybut:

android:configChanges=”zmiana_konfiguracji”
Stoper
gdzie zmiana_konfiguracji jest typem zmiany konfiguracji urządzenia.
app/src/main
W naszym przypadku chcemy, by system pomijał zmiany wielkości i orientacji <xml>
ekranu, dlatego musimy dodać do pliku AndroidManifest.xml następujący fragment </xml>

kodu: AndroidManifest.xml

<activity Pionowa kreska (|) oznacza, że


należy pomijać
obie podane zmiany konfiguracji.
android:name=”com.hfad.stoper.StopwatchActivity” z faktu, że większość urządzeń Wynika to
ma prostokątne
ekrany, a zatem obrócenie takie
android:label=”@string/app_name” go
powoduje zmianę zarówno wymiaró urządzenia
jak i jego orientacji. w ekranu,
android:configChanges=”orientation|screenSize” >

Jeśli Android rozpozna zmianę konfiguracji, której typ został podany w atrybucie,
to zamiast standardowego odtwarzania aktywności zostanie wywołana metoda
onConfigurationChanged(Configuration):

public void onConfigurationChanged(Configuration config) {


}

Tę metodę można przesłonić, aby obsługiwać zmiany konfiguracji, o ile pojawi się
taka konieczność.

136 Rozdział 4.
Cykl życia aktywności

Albo zapisanie bieżącego stanu…


Znacznie lepszym sposobem obsługi zmian konfiguracji, którego
będziemy używali zdecydowanie najczęściej, jest zapisywanie bieżącego Aktywność
stanu aktywności, a następnie odtwarzanie go w metodzie onCreate(). uruchomiona
Aby zapisać bieżący stan aktywności, konieczne jest zaimplementowanie
metody onSaveInstanceState(). Metoda ta jest wywoływana przed
usunięciem aktywności, co oznacza, że zapewnia nam możliwość
zapisania wszelkich wartości, których nie chcemy utracić. onCreate()

Metoda onSaveInstanceState() ma jeden parametr — obiekt klasy


Bundle. Klasa Bundle pozwala gromadzić w jednym obiekcie dane
różnych typów:
Aktywność
public void onSaveInstanceState(Bundle savedInstanceState) { działająca
}
Metoda
onSaveInstanceState()
Obiekt klasy Bundle jest także przekazywany jako parametr do metody jest wywoływana przed
onCreate(). Oznacza to, że jeśli zapiszemy wartości zmiennych seconds metodą onDestroy().
i running w obiekcie Bundle, to metoda onCreate() będzie w stanie je Zapewnia nam ona onSaveInstanceState()
możliwość zapisania
odczytać i zastosować podczas odtwarzania aktywności. Do umieszczania stanu aktywności przed
informacji w obiekcie Bundle służą metody, które zapisują w nim pary jej usunięciem.
nazwa – wartość. Metody te mają następującą postać: onDestroy()
bundle.put*(”nazwa”, wartość)

gdzie bundle jest nazwą zmiennej typu Bundle, * określa typ zapisywanej
wartości, a nazwa i wartość to odpowiednio nazwa i wartość zapisywanej
danej. Na przykład aby zapisać w obiekcie Bundle wartość typu int
Aktywność
przechowywanej w zmiennej seconds, należałoby użyć następującego usunięta
wywołania:

bundle.putInt(”seconds”, seconds);

W jednym obiekcie typu Bundle można zapisać wiele takich par danych.

Poniżej przedstawiliśmy kod metody onSaveInstanceState() używanej Stoper


w naszej aplikacji:

@Override app/src/main
public void onSaveInstanceState(Bundle savedInstanceState)
{ java
savedInstanceState.putInt(”seconds”, seconds);
savedInstanceState.putBoolean(”running”, running); com.hfad.stoper
Te wywołania
} zapisują wartości
zmiennych seconds Stopwatch
Skoro już zapisaliśmy wartość naszych zmiennych w obiekcie Bundle, i running w obiekcie
Activity.java
Bundle.
możemy z nich skorzystać w metodzie onCreate().

jesteś tutaj  137


Odtworzenie stanu

…i następnie odtworzenie stanu w onCreate()


Jak już wspominaliśmy wcześniej, metoda onCreate() ma jeden parametr —
obiekt Bundle. Jeśli aktywność jest tworzona po raz pierwszy, parametr ten
Stoper
będzie miał wartość null. Jeśli jednak aktywność jest odtwarzana i wcześniej
została wywołana metoda onSaveInstanceState(), to w wywołaniu metody
app/src/main
onCreate() zostanie przekazany ten sam obiekt Bundle, który wcześniej został
użyty w metodzie onSaveInstanceState():
java
protected void onCreate(Bundle savedInstanceState) {
com.hfad.stoper
...
}
Stopwatch
Activity.java
Dane z obiektu Bundle można pobierać, używając metod o postaci:

bundle.get*(”nazwa”);

gdzie bundle to nazwa zmiennej typu Bundle, * to określenie typu pobieranej


wartości, a nazwa nazwa pary, którą zapisaliśmy na poprzedniej stronie.
Na przykład aby pobrać z obiektu Bundle liczbę całkowitą (typu int)
o nazwie seconds, należałoby użyć następującego wywołania:

int seconds = bundle.getInt(”seconds”);

Łącząc to wszystko w jedną całość, uzyskamy naszą nową metodę onCreate(),


która będzie miała następującą postać:

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState); bierają
Te instrukcje po wartości
z obiek tu Bu nd le
setContentView(R.layout.activity_stopwatch); s i running.
zmiennych second
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt(“seconds”);
running = savedInstanceState.getBoolean(“running”);
} Zrób to sam!
runTimer();
}

A jak ten kod działa w praktyce?


Upewnij się, że zaktualizowałeś
swoją metodę onCreate()
i dodałeś metodę
onSaveInstanceState().

138 Rozdział 4.
Cykl życia aktywności

Co się stanie po uruchomieniu aplikacji?


1 Użytkownik uruchamia aplikację i klika przycisk Start, by rozpocząć działanie
stopera.
Metoda runTimer() zaczyna inkrementować liczbę sekund wyświetlanych na ekranie
w widoku tekstowym o identyfikatorze time_view.

seconds=8

StopwatchActivity

Urządzenie running=true

2 Użytkownik obraca urządzenie.


Android uznaje to za zmianę konfiguracji i przygotowuje się do usunięcia aktywności.
Zanim to jednak zrobi, wywołuje metodę onSaveInstanceState(). Metoda ta zapisuje
wartości zmiennych seconds i running w obiekcie Bundle.

Zaraz mnie
usuną, muszę
was zapisać…

seconds=8

StopwatchActivity
Urządzenie
running=true

bundle
“seconds”=8
“running”=true

jesteś tutaj  139


Co się dzieje?

Historii ciąg dalszy


3 Android usuwa aktywność, a następnie ją odtwarza.
Zostaje wywołana metoda onCreate(), a do niej przekazywany jest obiekt typu Bundle.

Urządzenie
seconds=0

StopwatchActivity

bundle
running=false
“seconds”=8
“running”=true

4 Obiekt Bundle zawiera wartości zmiennych seconds i running, zapisane przed


usunięciem aktywności.
Kod metody onCreate() przywraca wcześniejsze wartości zmiennych seconds i running,
odczytując je z obiektu Bundle.

seconds=8

StopwatchActivity
Urządzenie
running=true

bundle
“seconds”=8
“running”=true

5 Zostaje wywołana metoda runTimer(), a stoper rozpoczyna działanie


w miejscu, w którym wcześniej zostało ono przerwane.
Czas zmierzony przez stoper zostaje wyświetlony na ekranie urządzenia.

140 Rozdział 4.
Cykl życia aktywności

Jazda próbna aplikacji


Wprowadź zmiany w kodzie swojej aktywności, a następnie uruchom aplikację. Kiedy klikniesz
przycisk Start, stoper zacznie mierzyć czas i nie zatrzyma się nawet po obróceniu urządzenia.

Kiedy obrócimy urządzenie,


stoper wciąż będzie działał.

Nie istnieją
głupie pytania

P: Dlaczego Android chce P: Dlaczego Android nie zapisuje P: Czy Bundle to jakiś rodzaj mapy
odtwarzać aktywność tylko dlatego, automatycznie wartości wszystkich zaimplementowanej w Javie?
że obróciliśmy ekran? zmiennych? Dlaczego muszę ten kod
pisać ręcznie? O: Nie, ale ta klasa została
O: Metoda onCreate() jest normalnie zaprojektowana, by działać podobnie do
używana do przygotowywania ekranu. Jeśli O: Może się zdarzyć, że nie będziesz klasy java.util.Map. Obiekty klasy
jej kod jest zależny od konfiguracji ekranu (na chciał zapisywać wartości wszystkich Bundle mają dodatkowe możliwości,
przykład jeśli używamy odmiennych układów zmiennych. Na przykład możesz używać którymi nie dysponują mapy, na przykład
dla urządzeń działających w orientacji zmiennej zawierającej bieżącą szerokość można je przekazywać pomiędzy
pionowej i poziomej), to będziemy chcieli, ekranu. Zapewne nie chciałbyś odtwarzać procesami. To naprawdę bardzo przydatna
by metoda onCreate() była wywoływana wartości tej zmiennej podczas ponownego możliwość, gdyż pozwala Androidowi mieć
po każdej zmianie konfiguracji. Co więcej, wywołania metody onCreate(). dostęp do bieżącego stanu aktywności.
możemy także chcieć zmieniać teksty
wyświetlane w interfejsie użytkownika
aplikacji, kiedy użytkownik zmieni wybrane
ustawienia lokalne.

jesteś tutaj  141


Widzialny cykl życia

Tworzenie i usuwanie to nie cały cykl życia aktywności


Dotychczas przyjrzeliśmy się wyłącznie dwóm etapom cyklu życia aktywności,
a mianowicie tworzeniu i usuwaniu, oraz pokazaliśmy, jak można obsługiwać
zmiany konfiguracji urządzenia, takie jak zmiana jego orientacji. Cykl życia
aplikacji zawiera jednak także inne zdarzenia, które mogą się nam przydać
do zapewnienia prawidłowego działania aplikacji.

Na przykład załóżmy, że w trakcie pomiaru czasu za pomocą naszej aplikacji Nawet jeśli wcale nie chcesz,
by aplikacja stopera działała
stopera zadzwoni telefon. W takim przypadku, pomimo tego, że stoper w taki sposób, to jest to doskonały
nie będzie widoczny, wciąż będzie działał. A co zrobić, gdybyśmy chcieli, pretekst do przedstawienia kolejnych
metod cyklu życia aktywności.
aby niewidoczny stoper był zatrzymywany i wznawiał pomiar czasu po jego
ponownym wyświetleniu?

Uruchamianie, zatrzymywanie i restartowanie


Na szczęście dzięki metodom cyklu życia aplikacji obsługa akcji związanych
z widzialnością aplikacji jest całkiem łatwa. Oprócz metod onCreate() Aktywność jest
i onDestroy(), związanych z ogólnie pojętym cyklem życia aktywności,
zawiera on także grupę metod związanych z jej widzialnością. zatrzymana, gdy jest
Istnieją trzy podstawowe metody wywoływane, gdy aktywność staje się całkowicie przesłonięta
widoczna lub gdy znika z ekranu. Są to: onStart(), onStop() oraz
onRestart(). Wszystkie te metody, podobnie jak onCreate() i onDestroy(), przez inną aktywność
są dziedziczone po klasie Activity. i nie jest widoczna
Metoda onStart() jest wywoływana, gdy aktywność staje się widoczna dla
dla użytkownika.
użytkownika.

Metoda onStop() jest wywoływana, gdy aktywność przestaje być widoczna


Taka aktywność
dla użytkownika. Może się to zdarzyć na przykład, kiedy aktywność zostanie wciąż istnieje w tle
całkowicie przesłonięta przez inną aktywność wyświetloną na ekranie albo
kiedy aktywność ma zostać usunięta. Jeśli metoda onStop() jest wywoływana i zachowuje wszystkie
w wyniku planowanego usunięcia aktywności, to przed nią zostanie wywołana
metoda onSaveInstanceState(). informacje o swoim
Metoda onRestart() jest wywoływana, gdy aktywność jest już niewidoczna, stanie.
ale ma zostać ponownie wyświetlona

Na następnej stronie przyjrzymy się dokładniej, jak te nowe metody cyklu


życia aktywności pasują do przedstawionych wcześniej metod onCreate()
i onDestroy().

142 Rozdział 4.
Cykl życia aktywności

Cykl życia aktywności: widzialny czas życia


Spróbujmy teraz narysować ten sam diagram cyklu życia aktywności, który pokazaliśmy
już wcześniej, uwzględniając na nim metody onStart(), onStop() oraz onRestart()
(te elementy diagramu, na których masz się skoncentrować, zostały wytłuszczone):

Aktywność 1 Aktywność jest uruchamiana i zostaje


wywołana jej metoda onCreate().
uruchomiona Wykonywany jest kod inicjalizujący aktywność,
umieszczony w jej metodzie onCreate().
1 Na tym etapie aktywność jeszcze nie jest widoczna
i nie wywołano jeszcze jej metody onStart().
onCreate()

2 Po wywołaniu metody onCreate() zostaje


wywołana metoda onStart(). To wywołanie
jest wykonywane, gdy aktywność ma zostać
2 onStart() wyświetlona.
Po wykonaniu metody onStart() użytkownik może
już zobaczyć aktywność wyświetloną na ekranie.
4
Aktywność onRestart() 3 Metoda onStop() jest wywoływana, gdy
działająca aktywność przestaje być widoczna dla
użytkownika.
Po wykonaniu metody onStop() aktywność nie
będzie już widoczna.
3 onStop()
4 Jeśli aktywność ponownie ma być wyświetlona,
to zostaje wywołana metoda onRestart(),
a po niej metoda onStart().
onDestroy() Aktywność może wielokrotnie przejść ten cykl,
5 o ile wielokrotnie będzie ukrywana i ponownie
wyświetlana.

Aktywność 5 I w końcu aktywność jest usuwana.


usunięta Zazwyczaj metoda onStop() jest wywoływana przed
wywołaniem metody onDestroy(), jeśli jednak
w urządzeniu jest wyjątkowo mało dostępnej
pamięci, to wywołanie onStop() może zostać
pominięte.

Jeśli w urządzeniu jest wyjątkowo


mało pamięci, to może się zdarzyć,
Obejrzyj to! że przed usunięciem aktywności nie
zostanie wywołana metoda onStop().

jesteś tutaj  143


Zatrzymywanie i uruchamianie

Musimy zaimplementować dwie dodatkowe metody cyklu życia


Aby zaktualizować naszą aplikację stopera, musimy teraz wykonać dwie
modyfikacje. Przede wszystkim musimy zaimplementować metodę onStop(),
która zatrzyma stoper, gdy stanie się on niewidoczny. Kiedy to już zrobimy, Stoper
będziemy musieli zaimplementować metodę onStart(), która uruchomi
stoper, gdy tylko aplikacja ponownie zostanie wyświetlona. Zacznijmy od app/src/main
metody onStop().
java
Implementacja metody onStop() stopera
Metodę onStop() klasy Activity można przesłonić, dodając do własnej klasy com.hfad.stoper
aktywności następujący fragment kodu:
Stopwatch
@Override Activity.java
protected void onStop() {
super.onStop();
}

Gdy przesłaniamy metody cyklu życia aktywności, bardzo ważne jest, by w ich
kodzie w pierwszej kolejności wywołać przesłanianą metodę klasy bazowej
— w naszym przypadku będzie to metoda onStop():

super.onStop();
W przypadku przesłaniania
Istnieje kilka ważnych powodów, by to robić. Po pierwsze, musimy się
upewnić, że aktywność wykona wszystkie czynności zaimplementowane
metod cyklu życia
w metodzie klasy bazowej. Po drugie, Android nigdy nam nie wybaczy aktywności koniecznie
pominięcia tej czynności — jeśli nie wywołamy metody klasy bazowej,
Android zgłosi wyjątek. musimy wywoływać
W przypadku naszej aplikacji chcemy, by wywołanie metody onStop() metody klasy bazowej.
spowodowało zatrzymanie stopera. W tym celu wystarczy, że przypiszemy
zmiennej running wartość false: Jeśli tego nie zrobimy,
@Override Android zgłosi wyjątek.
protected void onStop() {
super.onStop();
running = false;
}

A zatem nasz stoper już się zatrzymuje, gdy aktywność staje się niewidoczna.
Naszym kolejnym zadaniem jest uruchomienie stopera po ponownym
wyświetleniu aktywności.

144 Rozdział 4.
Cykl życia aktywności

Zaostrz ołówek
Teraz Twoja kolej. Zmień kod aktywności w taki sposób, że jeśli przed
wywołaniem metody onStop() stoper działał, to zacznie on działać
także po ponownym wyświetleniu aktywności.

public class StopwatchActivity extends Activity {


private int seconds = 0;
private boolean running;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt(”seconds”);
running = savedInstanceState.getBoolean(”running”);
}
runTimer();
z
To jest pierwsza część kodu aktywności. Musis
} zaimplementować metodę onStart() i wprow
adzić
.
drobne modyfikacje w kodzie innych metod

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(”seconds”, seconds);
savedInstanceState.putBoolean(”running”, running);
savedInstanceState.putBoolean(”wasRunning”, wasRunning);
}

@Override
protected void onStop() {
super.onStop();
running = false;
}

jesteś tutaj  145


Zaostrz ołówek — rozwiązanie

Zaostrz ołówek
Teraz Twoja kolej. Zmień kod aktywności w taki sposób, że jeśli przed
Rozwiązanie wywołaniem metody onStop() stoper działał, to zacznie on działać
także po ponownym wyświetleniu aktywności.

public class StopwatchActivity extends Activity {


private int seconds = 0;
Dodaliśmy nową zmienną, wasRunning, w której
private boolean running; będziemy zapisywali informację, czy stoper działał przed
private boolean wasRunning; wywołaniem metody onStop(), żebyśmy w razie potrzeby
mogli go ponownie uruchomić, gdy aktywność znowu
zostanie wyświetlona.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt(”seconds”);
running = savedInstanceState.getBoolean(”running”);
wasRunning = savedInstanceState.getBoolean(“wasRunning”);
}
runTimer(); Jeśli aktywność jest ponownie tworzona,
} to odtwarzamy stan zmiennej wasRunning.

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(”seconds”, seconds);
savedInstanceState.putBoolean(”running”, running);
savedInstanceState.putBoolean(“wasRunning”, wasRunning); Zapisujemy stan zmiennej
} wasRunning.

@Override
protected void onStop() {
super.onStop(); Rejestrujemy, czy w momencie wywoływania
wasRunning = running; metody onStop() stoper działał, czy nie.
running = false;
}

@Override
protected void onStart() {
super.onStart();
To jest implementacja metody
if (wasRunning) { onStart(). Jeśli stoper działał,
to niech dalej działa.
running = true;
}
}

146 Rozdział 4.
Cykl życia aktywności

Zaktualizowany kod aktywności StopwatchActivity


Zmodyfikowaliśmy kod naszej aktywności w taki sposób, że jeśli stoper działał, zanim
aktywność zniknęła z ekranu, to będzie kontynuował działanie po jej ponownym Stoper
wyświetleniu. Wprowadź wyróżnione zmiany do kodu swojej aktywności:

public class StopwatchActivity extends Activity { app/src/main


private int seconds = 0; Nowa zmienna, wasRunning, przechowuje
private boolean running; informację, czy przed wywołaniem metody java
onStop() stoper działał, czy nie. Dzięki niej
private boolean wasRunning; będziemy wiedzieć, czy należy kontynuować
działanie stopera, gdy aktywność ponownie com.hfad.stoper
zastanie wyświetlona.
@Override
protected void onCreate(Bundle savedInstanceState) { Stopwatch
super.onCreate(savedInstanceState); Activity.java
setContentView(R.layout.activity_stopwatch);
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt(”seconds”);
running = savedInstanceState.getBoolean(”running”);
wasRunning = savedInstanceState.getBoolean(“wasRunning”);
}
runTimer(); Ta instrukcja odtwarza stan zmiennej wasRunning
} po ponownym utworzeniu aktywności.

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(”seconds”, seconds);
savedInstanceState.putBoolean(”running”, running);
savedInstanceState.putBoolean(“wasRunning”, wasRunning); Tu zapisujemy stan
} zmiennej wasRunning.

@Override
protected void onStop() {
super.onStop(); Ta instrukcja zapisuje, czy w momencie
wasRunning = running; wywoływania metody onStop() stoper działał,
czy nie.
running = false;
}

@Override
protected void onStart() {
super.onStart();
To jest implementacja metody
if (wasRunning) { onStart(). Jeśli wcześniej
running = true; stoper działał, to tu go
ponownie uruchamiamy.
}
}
...
jesteś tutaj  147
Co się dzieje?

Co się dzieje podczas działania aplikacji?


1 Użytkownik uruchamia aplikację i klika przycisk Start, by rozpocząć działanie stopera.
Metoda runTimer() zaczyna inkrementować liczbę sekund wyświetlanych na ekranie w widoku tekstowym
o identyfikatorze time_text.

seconds=16

Stopwatch running=true
Activity
Urządzenie
wasRunning=false

2 Użytkownik przechodzi do ekranu głównego urządzenia, przez co nasza aplikacja


przestaje być widoczna.
W efekcie zostaje wywołana metoda onStop(), zmiennej wasRunning zostaje przypisana wartość true,
zmiennej running wartość false, a aktywność przestaje inkrementować liczbę sekund.

seconds=16
Aktywność wciąż W metodzie onStop()
istnieje, choć nie jest zmiennej running
widoczna. zostaje przypisana
wartość false.
Stopwatch running=false
Activity
Urządzenie
wasRunning=true

3 Użytkownik ponownie wyświetla naszą aplikację.


Powoduje to wywołanie metody onStart(), która przypisuje zmiennej running wartość true,
dzięki czemu aktywność ponownie będzie inkrementować liczbę sekund.

seconds=16

W metodzie onStart()
zmiennej running
running=true przypisywana jest
Stopwatch wartość true.
Activity

Urządzenie
wasRunning=true

148 Rozdział 4.
Cykl życia aktywności

Jazda próbna aplikacji


Zapisz zmiany wprowadzone w kodzie aktywności, a następnie uruchom aplikację. Kiedy klikniesz
przycisk Start, stoper zacznie mierzyć upływający czas; kiedy aplikacja będzie niewidoczna, pomiar
zostanie zatrzymany, a ponowne wyświetlenie aplikacji spowoduje jego wznowienie.

Włączyliśmy stoper,
a następnie przeszliśmy
do ekranu głównego
urządzenia.
Na czas, w którym
aplikacja była
niewidoczna, stoper
został zatrzymany. Po ponownym wyświetleniu
aplikacji stoper wznowił
pomiar czasu.

Nie istnieją
głupie pytania

P: Czy zamiast tych dwóch metod moglibyśmy P: A czym różni się ta sytuacja?
zastosować metodę onRestart()?
O: W przypadku obrócenia urządzenia dotychczasowa
O : Metoda onRestart() jest używana w sytuacjach, w których aktywność jest usuwana, a na jej miejsce jest tworzona nowa.
chcemy wykonać jakiś kod, gdy aplikacja zostaje wyświetlona po Gdybyśmy zatem umieścili kod w metodzie onRestart(), to nie
jej wcześniejszym ukryciu. Nie jest ona natomiast wywoływana zostałby on wykonany po ponownym utworzeniu aktywności.
podczas wyświetlania aktywności po raz pierwszy. W naszym Metoda onStart() jest wywoływana w obu tych przypadkach.
przypadku chcieliśmy, by aplikacja cały czas działała według
założeń, nawet po obróceniu urządzenia.

jesteś tutaj  149


Cykl życia na pierwszym planie

A co się dzieje, jeśli aplikacja jest tylko częściowo widoczna?


Wiesz już, co się stanie, kiedy aktywność zostanie utworzona i usunięta, a także co się dzieje,
gdy aktywność jest wyświetlana i ukrywana. Jednak jest jeszcze jedna sytuacja, którą musimy wziąć
pod uwagę: kiedy aktywność jest widoczna, lecz nie dysponuje miejscem wprowadzania.

Kiedy aktywność jest widoczna, lecz nie dysponuje miejscem wprowadzania, jej działanie zostaje
wstrzymane. Może się to stać, gdy aktywność zostanie przesłonięta przez inną, która nie zajmuje całego
ekranu albo jest przezroczysta. Aktywność wyświetlona na wierzchu będzie dysponować miejscem
wprowadzania, lecz ta pod nią wciąż będzie widoczna, choć jej działanie zostanie wstrzymane.

Nasza aktywność
stopera wciąż
jest widoczna,
lecz została
częściowo
przesłonięta
i nie dysponuje
miejscem
wprowadzania.
Aktywność zmienia stan
na wstrzymaną, gdy
nie dysponuje miejscem
wprowadzania, lecz
cały czas jest widoczna
dla użytkownika. Taka
To jest aktywność aktywność cały czas żyje
należąca do
innej aplikacji, i zachowuje wszystkie
wyświetlona na
naszym stoperze. informacje o swoim
stanie.

Istnieją dwie metody cyklu życia aktywności, które są związane ze wstrzymywaniem jej działania
i jego wznawianiem: onPause() i onResume(). Metoda onPause() jest wywoływana, gdy
aktywność jest widoczna, lecz inna aktywność dysponuje miejscem wprowadzania. Z kolei metoda
onResume() jest wywoływana bezpośrednio przed momentem, gdy aktywność ponownie zacznie
prowadzić interakcję z użytkownikiem. Jeśli aplikacja musi w jakiś sposób reagować na wstrzymanie
i wznowienie działania aktywności, konieczne będzie zaimplementowanie tych dwóch metod.

Na następnej stronie zobaczysz, jak te dwie metody pasują do pozostałych, przedstawionych


wcześniej metod cyklu życia aplikacji.

150 Rozdział 4.
Cykl życia aktywności

Cykl życia aktywności: życie na pierwszym planie


Kontynuujmy tworzenie przedstawionego już wcześniej diagramu metod cyklu życia
aktywności, a konkretnie dodajmy do niego dwie metody: onPause() i onResume()
(te nowe elementy schematu zostały wyróżnione pogrubieniem):

1 Aktywność jest uruchamiana i zostają wywołane


Aktywność jej metody onCreate() i onStart().
W tym momencie aktywność jest już widoczna,
uruchomiona ale jeszcze nie dysponuje miejscem wprowadzania.

1 2 Po metodzie onStart() zostaje wywołana


metoda onResume(). Jest ona wywoływana przed
onCreate() przeniesieniem aktywności na pierwszy plan.
Po wywołaniu metody onResume() aktywność
dysponuje już miejscem wprowadzania i użytkownik
może prowadzić z nią interakcję.
onStart()
3 Kiedy aktywność przestaje być wyświetlana na
2 pierwszym planie, zostaje wywołana metoda
onPause().
onResume() Po wywołaniu metody onPause() aktywność wciąż jest
widoczna, lecz nie dysponuje miejscem wprowadzania.

4 Jeśli aktywność zostanie ponownie przeniesiona


do pierwszego planu, to zostanie wywołana
4 Aktywność onRestart() metoda onResume().
działająca Ten cykl może się powtarzać wielokrotnie, jeśli tylko
6
aktywność będzie na przemian tracić i ponownie
odzyskiwać miejsce wprowadzania.

onPause() Kiedy aktywność przestanie być widoczna


5
3 dla użytkownika, zostanie wywołana metoda
onStop().
Po wywołaniu metody onStop() aktywność nie jest
5 onStop() już widoczna dla użytkownika.

6 Jeśli aktywność ponownie stanie się widoczna,


onDestroy() zostanie wywołana jej metoda onRestart(),
a następnie dwie kolejne metody, onStart()
i onResume().
7 Także ten cykl wywołań może być wykonywany
wielokrotnie.
Aktywność
usunięta 7 I w końcu aktywność zostaje usunięta.
Kiedy aktywność ma zostać usunięta, najpierw
wywoływana jest metoda onPasue(). Zazwyczaj
wywoływana jest także metoda onStop().

jesteś tutaj  151


Obroty
Wcześniej opisywaliście,
jak aktywność jest usuwana
i ponownie tworzona w efekcie obrócenia
ekranu urządzenia. A co się stanie, kiedy
urządzenie zostanie obrócone w momencie,
gdy aplikacja będzie wstrzymana? Czy wówczas
wykonywane będą te same metody cyklu życia
aktywności?

To jest świetne pytanie, dlatego zanim wrócimy do naszej


aplikacji, przyjrzymy się temu zagadnieniu dokładniej.
W początkowej aktywności wywoływane są wszystkie metody cyklu jej życia,
zaczynając od onCreate(), a kończąc na onDestroy(). Nowa aktywność
jest tworzona po usunięciu początkowej. Ponieważ ta nowa aktywność nie
jest jeszcze wyświetlona na pierwszym planie, zostają wywołane tylko dwie
metody cyklu jej życia, a mianowicie onCreate() i onStart():

Początkowa aktywność
1 Użytkownik uruchamia aktywność.
Zostają wywołane metody onCreate(), onStart()
Aktywność oraz onResume() cyklu życia aktywności.
uruchomiona
2 Nasza aktywność jest przesłaniana przez inną.
onCreate() Zostaje wywołana metoda onPause() aktywności.

1 onStart() 3 Użytkownik obraca urządzenie.


Android zauważa zamianę konfiguracji
onResume() urządzenia. Zostają wywołane metody onStop()
i onDestroy(), po czym system usuwa aktywność.
Na jej miejsce jest tworzona nowa aktywność.
Aktywność
działająca 4 Aktywność jest widoczna, lecz nie na
pierwszym planie.
Zostają wywołane metody onCreate()
2 onPause() i onStart(). Ponieważ aktywność jest widoczna,
lecz nie dysponuje miejscem wprowadzania,
onStop() metoda onResume() nie zostanie wywołana.

onDestroy() Aktywność zastępcza

Aktywność
Aktywność uruchomiona
usunięta
onCreate()
3
onStart() 4
152 Rozdział 4.
Cykl życia aktywności

Widzę, że aktywność zastępcza nie przeszła do


stanu „działająca”, gdyż nie została wyświetlona
na pierwszym planie. Ale co by się stało, gdybyśmy
całkowicie przeszli do innej aktywności, tak że ta
nasza w ogóle nie byłaby widoczna? Jeśli aktywność
została zatrzymana, to czy przed metodą onStop()
są wywoływane metody onResume() i onPause()?

Aktywność
Aktywności mogą przechodzić uruchomiona
bezpośrednio od wywołania
onStart() do wywołania
onStop(), pomijając przy tym
onCreate()
wywołania metod onPause()
i onResume().

Jeśli aktywność jest widoczna, lecz


onStart()
nigdy nie znalazła się na pierwszym
planie ani nie uzyskała miejsca
wprowadzania, to jej metody
onPause() i onResume() nigdy nie onResume()
zostaną wywołane.

Metoda onResume() jest wywoływana,


kiedy aktywność zostaje wyświetlona
na pierwszym planie i uzyskuje Aktywność onRestart()
miejsce wprowadzania. Jeśli aktywność działająca
jest widoczna, lecz przesłonięta
innymi aktywnościami, to jej metoda
onResume() nie zostanie wywołana.
onPause()
I podobnie metoda onPause()
jest wywoływana, kiedy aktywność
przestaje być widoczna na pierwszym
planie. A zatem jeśli aktywność nigdy onStop()
nie była wyświetlona na pierwszym
planie, to jej metoda onPause() nie
zostanie wywołana.
onDestroy()
Jeśli aktywność zostanie zatrzymana lub
usunięta, zanim pojawi się na pierwszym
planie, to po wywołaniu metody onStart()
zostaje wywołana metoda onStop(). W takim
przypadku wywołania metod onResume()
i onPause() są pomijane. Aktywność
usunięta

jesteś tutaj  153


Zmiana kodu metod

Zatrzymanie stopera w razie


wstrzymania aktywności onStart()

Wróćmy do naszej aplikacji stopera.

Na razie zatrzymujemy stoper, kiedy aplikacja nie jest widoczna,


onResume()
a następnie wznawiamy jego działanie, gdy aplikacja ponownie
pojawi się na ekranie. Oprócz tego chcemy zatrzymywać stoper,
kiedy działanie aktywności zostanie wstrzymane, i ponownie go
uruchamiać po wznowieniu działania aktywności. Których metod
cyklu życia aktywności powinniśmy w tym celu użyć? Aktywność onRestart()
działająca
Najprościej byłoby stwierdzić, że musimy w tym celu
zaimplementować metody onPause() i onResume(). Możemy
jednak pójść o krok dalej. Użyjemy tych metod do zastąpienia
zaimplementowanych wcześniej metod onStop() i onStart().
onPause()
Jeśli ponownie przyjrzymy się diagramowi cyklu życia aktywności,
to zauważymy, że podczas zatrzymywania i uruchamiania
aktywności oprócz metod onStop() i onStart() zawsze
wywoływane są także metody onPasue() i onResume(). onStop()
A zatem, ponieważ chcemy, by aplikacja działała tak samo
w obu sytuacjach, użyjemy tych samych metod.
Stoper
Poniżej przedstawiliśmy naszą wersję metody onPause():
app/src/main
@Override
protected void onPause() { java
super.onPause();
wasRunning = running; com.hfad.stoper
running = false;
} Stopwatch
Activity.java
A to jest kod metody onResume():

@Override
protected void onResume() {
super.onResume();
Zrób to sam!
if (wasRunning) {
running = true;
}
} W swoim kodzie zastąp
metody onStop() i onStart()
Przekonajmy się teraz, co się stanie po uruchomieniu aplikacji. przedstawionymi obok
metodami onPause() i onResume().

154 Rozdział 4.
Cykl życia aktywności

Co się stanie po uruchomieniu aplikacji?


1 Użytkownik uruchamia aplikację i klika przycisk Start, by rozpocząć mierzenie czasu.
Metoda runTimer() zaczyna inkrementować liczbę sekund, która jest wyświetlana w widoku tekstowym
o identyfikatorze time_view.

seconds=15

running=true
Stopwatch
Activity
Urządzenie
wasRunning=false

2 Inna aktywność zostaje wyświetlona na pierwszym planie, przez co nasza aktywność


StopwatchActivity staje się tylko częściowo widoczna.
Zostaje wywołana metoda onPause(), zmiennej wasRunning zostaje przypisana wartość true,
zmiennej running wartość false, a zmienna seconds przestaje być inkrementowana.

seconds=15
Aktywność zostaje
wstrzymana, gdyż W metodzie
jest widoczna, ale onPause() zmiennej
nie na pierwszym running zostaje
running=false przypisana wartość
planie. Stopwatch false.
Activity

Urządzenie
wasRunning=true

3 Kiedy aktywność StopwatchActivity ponownie zostaje wyświetlona na pierwszym planie,


wywoływana jest jej metoda onResume(), zmiennej running jest przypisywana wartość true,
a zmienna seconds ponownie zaczyna być inkrementowana.

seconds=15

W metodzie onResume()
zmiennej running zostaje
running=true przypisana wartość true.
Stopwatch
Activity

Urządzenie
wasRunning=true

jesteś tutaj  155


Jazda próbna

Jazda próbna aplikacji


Zapisz zmiany wprowadzone w kodzie aktywności, a następnie uruchom aplikację.
Kiedy klikniesz przycisk Start, stoper zacznie mierzyć upływające sekundy; pomiar
zostanie zatrzymany po częściowym przesłonięciu naszej aktywności przez jakąś inną
aktywność, a po jej ponownym wyświetleniu na pierwszym planie pomiar zostanie
wznowiony.

Uruchomiliśmy nasz stoper.


Jego działanie zostało
wstrzymane po częściowym
przesłonięciu aktywności
przez inną.

Nasz stoper wznowił


działanie, kiedy aktywność
ponownie została wyświetlona
na pierwszym planie.

Nie istnieją
głupie pytania

P: Niektóre z metod cyklu życia aktywności Metody onCreate() i onStart() zawsze będą wywoływane
w odpowiednim czasie, a to oznacza, że nasza aplikacja może
nie zawsze są wywoływane. Czy to nie może być
zadbać o to, by na początku jej działania wszystko z nią było
przyczyną dziwacznego działania aplikacji?
w porządku. A to jest znaczenie ważniejsze.
O: W niektórych okolicznościach Android może pominąć Najważniejsze jest jednak, by dobrze zrozumieć, które
wywołania metod onStop() i onPause(). Zazwyczaj metody cyklu życia aplikacji są wywoływane w różnych
zawierają one kod wykonujący operacje porządkowe. okolicznościach.

156 Rozdział 4.
Cykl życia aktywności

Kompletny kod aktywności


Poniżej przedstawiliśmy kompletny kod aktywności zapisany
w pliku StopwatchActivity.java:

package com.hfad.stoper;
Stoper
import android.os.Bundle;
import android.os.Handler;
app/src/main
import android.app.Activity;
import android.view.View;
java
import android.widget.TextView;

com.hfad.stoper
public class StopwatchActivity extends Activity {
// Liczba sekund wyświetlana przez stoper
private int seconds = 0; Stopwatch
Zmiennych seconds, running i wasRunning Activity.java
// Czy stoper działa? używamy odpowiednio do przechowywania liczby
private boolean running; zmierzonych sekund, określenia, czy stoper działa,
i określenia, czy stoper działał w momencie
private boolean wasRunning; wstrzymywania aktywności.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); Ten fragment kodu odtwarza
setContentView(R.layout.activity_stopwatch); stan stopera w razie usunięcia
i ponownego utworzenia
if (savedInstanceState != null) { aktywności.
seconds = savedInstanceState.getInt(”seconds”);
running = savedInstanceState.getBoolean(”running”);
wasRunning = savedInstanceState.getBoolean(”wasRunning”);
}
runTimer();
}

Jeśli aktywność zostaje wstrzymana,


@Override to zatrzymujemy stoper.
protected void onPause() {
super.onPause();
wasRunning = running;
running = false;
}

@Override Jeśli działanie aktywności


zostaje wznowione i podczas jej
protected void onResume() { wstrzymywania stoper działał,
super.onResume(); to teraz go uruchamiamy. Dalsza część kodu
if (wasRunning) { aktywności została
zamieszczona na
running = true; następnej stronie.
}
} jesteś tutaj  157
Kod aktywności StopwatchActivity

Kod aktywności (ciąg dalszy) Ta metoda zapisuje stan stopera,


jeśli aktywność ma zostać usunięta.
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(”seconds”, seconds);
savedInstanceState.putBoolean(”running”, running);
savedInstanceState.putBoolean(”wasRunning”, wasRunning);
}

// Metoda uruchamia stoper po kliknięciu przycisku Start


public void onClickStart(View view) {
running = true;
Ta metoda jest wywoływana w odpowiedzi
} na kliknięcie przycisku Start.

// Metoda zatrzymuje stoper po kliknięciu przycisku Stop


public void onClickStop(View view) {
Ta metoda jest wywoływana w odpowiedzi
running = false; na kliknięcie przycisku Stop.
}

// Metoda zeruje stoper po kliknięciu przycisku Kasuj


public void onClickReset(View view) { Ta metoda jest wywoływana w odpowiedzi
running = false; na kliknięcie przycisku Kasuj.
seconds = 0; Metoda runTimer() używa obiektu
} Handler do inkrementacji liczby sekund
i wyświetlania ich w widoku tekstowym.
// Wyświetla w stoperze liczbę sekund
private void runTimer() {
final TextView timeView = (TextView)findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
Stoper
@Override
public void run() {
int hours = seconds/3600; app/src/main
int minutes = (seconds%3600)/60;
int secs = seconds%60; java
String time = String.format(”%d:%02d:%02d”, hours, minutes, secs);
timeView.setText(time); com.hfad.stoper
if (running) {
seconds++; Stopwatch
} Activity.java
handler.postDelayed(this, 1000);
}
});
}
}

158 Rozdział 4.
Cykl życia aktywności

Bądź aktywnością ...


class MyActivity extends Activity{
Po prawej stronie przedstawiliśmy kod
pewnej aktywności. Twoim zadaniem protected void onCreate(
jest wcielić się w rolę tej aktywności Bundle savedInstanceState) {
i podać, który kod zostanie // Wykonujemy kod A
A
wykonany w każdej ...
z poniższych sytuacji. }
Kod, który masz brać
pod uwagę, zaznaczyliśmy protected void onPause() {
literami w kółkach. Aby ułatwić // Wykonujemy kod B
Ci rozwiązanie ćwiczenia, podaliśmy B
...
odpowiedź na pierwsze pytanie. }

protected void onRestart() {


Użytkownik uruchamia aktywność // Wykonujemy kod C
C
i zaczyna jej używać. ...
}
Zostają wykonane fragmenty kodu A, G i D.
Aktywność jest tworzona, następnie zostaje
wyświetlona na pierwszym planie i uzyskuje protected void onResume() {
miejsce wprowadzania. // Wykonujemy kod D
D ...
}
Użytkownik uruchamia aktywność,
zaczyna jej używać, a następnie przechodzi protected void onStop() {
do innej aktywności. // Wykonujemy kod E
E
...
}

protected void onRecreate() {


dne.
To jest tru // Wykonujemy kod F
F ...
Użytkownik uruchamia aktywność, }
zaczyna jej używać, obraca urządzenie,
przechodzi do innej aktywności, protected void onStart() {
a następnie wraca do początkowej. // Wykonujemy kod G
G ...
}

protected void onDestroy() {


// Wykonujemy kod H
H
...
}
}

jesteś tutaj  159


Rozwiązanie

Bądź aktywnością. Rozwiązanie ...


class MyActivity extends Activity{
Po prawej stronie przedstawiliśmy kod
pewnej aktywności. Twoim zadaniem protected void onCreate(
jest wcielić się w rolę tej aktywności Bundle savedInstanceState) {
i podać, który kod zostanie // Wykonujemy kod A
A
wykonany w każdej ...
z poniższych sytuacji. }
Kod, który masz brać
pod uwagę, zaznaczyliśmy protected void onPause() {
literami w kółkach. Aby ułatwić // Wykonujemy kod B
Ci rozwiązanie ćwiczenia, podaliśmy B
...
odpowiedź na pierwsze pytanie. }

Użytkownik uruchamia aktywność i zaczyna jej protected void onRestart() {


używać. // Wykonujemy kod C
C
Zostają wykonane fragmenty kodu A, G i D. Aktywność jest ...
tworzona, następnie zostaje wyświetlona na pierwszym planie }
i uzyskuje miejsce wprowadzania.

protected void onResume() {


// Wykonujemy kod D
Użytkownik uruchamia aktywność, zaczyna jej D ...
używać, a następnie przechodzi do innej aktywności.
}
Zostaną wykonane fragmenty kodu A, G, D, B i E. Aktywność
jest tworzona, następnie zostaje wyświetlona i uzyskuje miejsce
wprowadzania. Potem użytkownik przechodzi do innej aplikacji, protected void onStop() {
więc aktywność traci miejsce wprowadzania i nie jest już dłużej // Wykonujemy kod E
widoczna dla użytkownika. E
...
W cyklu życia aktywności
} nie ma metody o nazwie
onRecreate().
Użytkownik uruchamia aktywność, zaczyna jej
protected void onRecreate() {
używać, obraca urządzenie, przechodzi do innej
// Wykonujemy kod F
aktywności, a następnie wraca do początkowej. F ...
Zostaną wykonane fragmenty kodu A, G, D, B, E, H, A, G, D, B, E,
}
C, G i D. Aktywność jest tworzona, następnie zostaje wyświetlona
i uzyskuje miejsce wprowadzania. Kiedy urządzenie zostaje
obrócone, aktywność traci miejsce wprowadzania, przestaje być protected void onStart() {
widoczna i zostaje usunięta. Potem jest ponownie tworzona, staje // Wykonujemy kod G
się widoczna i uzyskuje miejsce wprowadzania. Gdy użytkownik G ...
przechodzi do innej aplikacji, a następnie wraca do tej, aktywność
traci miejsce wprowadzania, jest ukrywana, po czym zostaje }
ponownie wyświetlona i uzyskuje miejsce wprowadzania.
protected void onDestroy() {
// Wykonujemy kod H
H
...
}
}

160 Rozdział 4.
Cykl życia aktywności

Wygodny przewodnik po metodach cyklu życia aktywności

Metoda Kiedy jest wywoływana? Następna metoda

onCreate() Gdy aktywność jest tworzona po raz pierwszy. Można jej używać onStart()
do normalnej, statycznej inicjalizacji, takiej jak tworzenie widoków.
Metoda udostępnia także obiekt Bundle, zapewniający dostęp do
zapisanego wcześniej stanu aktywności.

onRestart() Gdy aktywność została wcześniej zatrzymana, bezpośrednio przed onStart()


jej ponownym uruchomieniem.

onStart() Gdy aktywność staje się widoczna. Po niej jest wywoływana metoda onResume()
onResume(), jeśli aktywność ma zostać wyświetlona na pierwszym lub onStop()
planie, lub metoda onStop(), jeśli aktywność ma zostać ukryta.

onResume() Gdy aktywność jest wyświetlana na pierwszym planie. onPause()

onPause() Gdy aktywność nie jest już wyświetlana na pierwszym planie, onResume()
ponieważ ma zostać wznowione działanie innej aktywności. lub onStop()
Następna aktywność zostanie uruchomiona dopiero po wykonaniu
tej metody, zatem musi ona działać bardzo szybko. Jeśli aktywność
ma być ponownie wyświetlona na pierwszym planie, to w następnej
kolejności wywoływana jest metoda onResume(), jeśli natomiast
aktywność jest ukrywana, to kolejną wywoływaną metodą będzie
onStop().

onStop() Gdy aktywność nie jest już widoczna. Przyczyną może być onRestart()
przesłonięcie danej aktywności przez inną bądź też planowane lub onDestroy()
usunięcie aktywności. Jeśli aktywność ma zostać ponownie
wyświetlona, to po tej metodzie zostanie wywołana metoda
onRestart(), jeśli natomiast aktywność ma zostać usunięta,
to następną wywołaną metodą będzie onDestroy().

onDestroy() Gdy aktywność ma zostać usunięta lub gdy kończy działanie. żadna

jesteś tutaj  161


Przybornik

Twój przybornik do Androida Pełny kod przykładowe


aplikacji prezen tow ane
j
j
Rozdział 4.

ale mo żes z
w tym rozdzi
Opanowałeś już rozdział 4. i dodałeś pobrać z ser we ra FT P
do swojego przybornika z narzędziami wydawnictwa Helion:
klady/
znajomość metod tworzących cykl życia ftp://ftp.helion.pl/przy
aktywności. andrrg.zip

Aktywność
CELNE SPOSTRZEŻENIA uruchomiona
 Każda aplikacja domyślnie działa we własnym procesie.
 Tylko główny wątek aplikacji może aktualizować jej
interfejs użytkownika. onCreate()
 Do planowania wykonania kodu lub przekazywania
go do innego wątku można używać obiektów klasy
Handler. onStart()
 Zmiana konfiguracji urządzenia skutkuje usunięciem
i ponownym utworzeniem aktywności.
 Aktywność dziedziczy metody cyklu życia po klasie onResume()
Activity. W razie przesłaniania którejkolwiek z tych
metod należy wywołać przesłanianą metodę klasy
bazowej.
 Metoda onSaveInstanceState(Bundle) pozwala Aktywność onRestart()
zapisać stan aktywności przed jej usunięciem. Tego działająca
samego obiektu Bundle można użyć w metodzie
onCreate() do odtworzenia stanu aktywności.
 Do zapisywania wartości w obiekcie Bundle służą
onPause()
metody bundle.put*(”nazwa”, wartosc).
Wartości te można następnie odczytywać, używając
metod bundle.get*(”nazwa”).
 Metody onCreate() i onDestroy() obsługują onStop()
odpowiednio narodziny i śmierć aktywności.
 Metody onRestart(), onStart() oraz onStop()
są związane ze zmianami widzialności aktywności. onDestroy()
 Metody onResume() i onPause() są związane
z uzyskiwaniem i traceniem przez aktywność miejsca
wprowadzania.
Aktywność
usunięta

162 Rozdział 4.
5. Interfejs użytkownika

Podziwiaj widoki

Zapamiętaj: layout_row=”18”
i layout_column=”56”.
A nie: „za tym białym”.

Nie masz innego wyjścia, musisz tworzyć szałowe układy.


Jeśli chcesz pisać aplikacje, których inni będą używać, musisz zadbać o to, by wyglądały one
dokładnie tak, jak sobie tego życzysz. Zagadnienie tworzenia układów potraktowaliśmy
dotychczas bardzo powierzchownie, najwyższy zatem czas, by przyjrzeć mu się dokładniej.
W tym rozdziale pokażemy Ci różne typy układów, które można tworzyć, i zabierzemy Cię na
wycieczkę po najważniejszych komponentach GUI i sposobach ich stosowania. Pod koniec tego
rozdziału przekonasz się, że choć wszystkie te układy i komponenty wyglądają nieco inaczej,
to jednak mają ze sobą więcej wspólnego, niż można by przypuszczać.

to jest nowy rozdział  163


Interfejs użytkownika

Interfejs użytkownika aplikacji


składa się z układów i komponentów GUI
Jak już wiesz, układ definiuje, jak wygląda ekran aplikacji, a postać układu definiuje się, używając
kodu XML. Układy zazwyczaj zawierają komponenty użytkownika, takie jak przyciski i pola
tekstowe. Użytkownik prowadzi z nimi interakcje i sprawia, że aplikacja wykonuje jakieś operacje.

Wszystkie układy przedstawione w poprzednich rozdziałach książki wykorzystywały układy


względne, istnieją jednak także inne typy układów, których możemy używać, by nasze aplikacje
wyglądały dokładnie tak, jak byśmy sobie tego życzyli.

W tym rozdziale pokażemy kilka innych układów, których możesz używać, tworząc
aplikacje, oraz parę dodatkowych komponentów GUI, dzięki którym Twoje
aplikacje staną się bardziej interaktywne. Zacznijmy od układów.

164 Rozdział 5.
Interfejs użytkownika

Trzy kluczowe układy: względny, liniowy i siatki


Dostępnych jest kilka rodzajów układów, a każdy ma odmienne zasady rozmieszczania
widoków. Poniżej przedstawiliśmy trzy najważniejsze spośród tych układów. Na razie nie
przejmuj się szczegółami ich działania — wyjaśnimy je dokładnie na kilku następnych
stronach książki.

Widoki mogą być


rozmieszczane
względem układu
RelativeLayout nadrzędnego…

Układ względny, jak sama nazwa wskazuje,


rozmieszcza widoki w sposób względy. W jego
…bądź względem
przypadku położenie widoku definiowane jest innych widoków.
względem innego widoku należącego do układu bądź
względem układu nadrzędnego. Na przykład możemy
określić położenie pola tekstowego względem górnej
krawędzi układu nadrzędnego, pole typu Spinner Kolejne widoki są
wyświetlić poniżej pola tekstowego, a przycisk powyżej rozmieszczane jeden obok
drugiego, bądź to w pionie,
dolnej krawędzi układu. bądź w poziomie.

LinearLayout
Układ liniowy umieszcza poszczególne widoki
jeden obok drugiego, w pionie lub w poziomie. Jeśli
układ rozmieszcza widoki w pionie, to utworzą one
pojedynczą kolumnę. Jeśli natomiast układ rozmieszcza
widoki w poziomie, to utworzą one pojedynczy wiersz.

GridLayout Ekran jest dzielony


Układ siatki dzieli ekran na siatkę składającą się na wiersze i kolumny,
a podczas dodawania
z wierszy, kolumn oraz komórek. Tworząc układ tego do niego widoków
typu, określamy, z ilu kolumn ma się składać siatka, określamy, w której
gdzie mają być umieszczone poszczególne widoki oraz komórce powinny się
znaleźć i ile komórek
ile wierszy lub kolumn ma zajmować każdy z nich. zajmą.

jesteś tutaj  165


Układ względny

¨  RelativeLayout
Układ RelativeLayout rozmieszcza widoki ¨  LinearLayout
w sposób względny ¨  GridLayout

Jak już wiesz, układ względy pozwala określać położenie widoków względem układu
lub względem innych widoków w nim umieszczonych.

Układ względny definiuje się przy użyciu elementu <RelativeLayout>, takiego jak ten
przedstawiony poniżej:

Ten element <RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”


informuje system, android:layout_width=”match_parent” Atrybuty layout_width i layout_height
że używamy określają, jak szeroki i wysoki ma być
układu względnego. android:layout_height=”match_parent” definiowany układ.
...>
W elemencie można umieszczać także inne znaczniki.
...
</RelativeLayout>

Atrybut xmlns:android służy do określenia przestrzeni nazw Androida i zawsze


musi mieć wartość ”http://schemas.android.com/apk/res/android”.

Szerokość i wysokość układu MUSZĄ zostać określone


Atrybuty android:layout_width i android:layout_height
określają, jak szeroki i wysoki ma być układ. Te atrybuty są
obowiązkowe dla wszystkich rodzajów układów i widoków. Dla maniaków
Atrybutom android:layout_width i android:layout_height
można przypisać wartość ”match_partent”, ”wrap_content” bądź Co to są piksele niezależne od gęstości?
konkretny rozmiar, na przykład 10dp, czyli 10 pikseli niezależnych Niektóre urządzenia mają bardzo małe
od gęstości. Wartość ”wrap_content” oznacza, że układ ma być piksele, dzięki czemu mogą wyświetlać
na tyle duży, by pomieścił całą swoją zawartość, natomiast wartość bardzo ostre obrazy. Z kolei inne urządzenia
”match_parent” oznacza, że chcemy, by układ był tak duży jak są tańsze w produkcji, gdyż ich piksele są
jego element nadrzędny — w naszym przypadku tak duży jak ekran większe i jest ich mniej. Pikseli niezależnych
urządzenia pomniejszony o wypełnienie. Jak można się przekonać, od gęstości (dp) można używać do
podczas definiowania układów zazwyczaj są używane wartości tworzenia interfejsów użytkownika, które
”match_parent”. nie będą zbyt małe na jednych urządzeniach,
a zbyt duże na innych. Wielkości wyrażone
Czasami można się także spotkać z układami, w których w pikselach niezależnych od gęstości są
atrybutom android:layout_width i android:layout_height są w przybliżeniu takie same na wszystkich
urządzeniach.
przypisywane wartości ”fill_parent”. Wartość ta była stosowana
we wcześniejszych wersjach systemu Android, lecz obecnie zamiast
niej należy używać ”match_parent”. Wartość ”fill_parent” jest
uznawana za przestarzałą.

166 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Dodawanie wypełnienia ¨  LinearLayout
¨  GridLayout
Jeśli chcesz, aby wokół układu pozostało troszkę wolnego miejsca,
możesz zastosować atrybuty padding. Pozwalają one określać,
ile miejsca chcemy pozostawić pomiędzy daną krawędzią układu
a jego elementem nadrzędnym. Poniżej pokazaliśmy, w jaki sposób
można nakazać zastosowanie wypełnienia o szerokości 16dp wokół
wszystkich krawędzi układu: paddingTop

<RelativeLayout ...
android:paddingBottom=”16dp” paddingLeft
android:paddingLeft=”16dp”
android:paddingRight=”16dp” Dodajemy wypełnienie Układ
android:paddingTop=”16dp”> o wielkości 16 dp.

...
</RelativeLayout>

Atrybuty android:padding* są opcjonalne i można ich używać w dowolnych paddingBottom paddingRight


układach i widokach.

W powyższym przykładzie określiliśmy wypełnienie na stałe, nadając mu


wielkość 16dp. Alternatywnym rozwiązaniem może być określenie wielkości
wypełnień w pliku zasobów wymiarów. Użycie tego pliku ułatwia spójne
stosowanie wypełnień we wszystkich układach używanych w aplikacji.

Aby użyć pliku zasobów wymiarów, w atrybutach wypełnienia należy podać


nazwy zasobów wymiarów, w sposób pokazany w poniższym przykładzie: Atrybutom paddingLeft
i paddingRight zostaje
przypisana wartość @dimen/
<RelativeLayout ... activity_horizontal_margin.
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin” Atrybutom paddingTop
i paddingBottom zostaje
android:paddingBottom=”@dimen/activity_vertical_margin”> przypisana wartość @dimen/
activity_vertical_margin.
W tym przypadku Android odczyta wielkości wypełnień z pliku zasobów
wymiarów w trakcie działania aplikacji. Plik ten jest umieszczony
w katalogu app/src/main/res/values i nosi nazwę dimens.xml:

<resources>
<dimen name=”activity_horizontal_margin”>16dp</dimen> Układ odczyta wartości
wypełnień z pliku zasobów
<dimen name=”activity_vertical_margin”>16dp</dimen> wymiarów.
</resources>

Zazwyczaj ten plik jest tworzony automatycznie przez Android Studio


podczas tworzenia nowego projektu i dodawania do niego aktywności.

jesteś tutaj  167


Względem elementu nadrzędnego

¨  RelativeLayout
Rozmieszczanie widoków względem układu nadrzędnego ¨  LinearLayout
¨  GridLayout
W przypadku stosowania układu względnego konieczne jest określenie położenia danego
widoku względem innego widoku należącego do układu bądź względem elementu nadrzędnego.
Elementem nadrzędnym widoku jest układ, w którym dany widok jest umieszczony.

Jeśli chcemy, by widok zawsze był umieszczony w konkretnym miejscu ekranu, niezależnie
od jego wielkości i orientacji, to jego położenie powinniśmy określać względem elementu
nadrzędnego. Na przykład poniżej pokazaliśmy, w jaki sposób można zapewnić, aby przycisk
zawsze był wyświetlony w prawym górnym rogu układu:

<RelativeLayout ... > layout_alignParentTop


<Button
Układ zawiera android:layout_width=”wrap_content”
przycisk, zatem android:layout_height=”wrap_content”
układ jest Układ
elementem nadrzędny
android:text=”@string/click_me”
nadrzędnym Widok podrzędny.
przycisku. android:layout_alignParentTop=”true”
android:layout_alignParentRight=”true” />
</RelativeLayout>
layout_alignParentRight

Dwa poniższe wiersze kodu:

android:layout_alignParentTop=”true”
android:layout_alignParentRight=”true”

oznaczają, że górna krawędź przycisku ma zostać wyrównana do górnej


krawędzi układu, a prawa krawędź przycisku — do prawej krawędzi układu.
Przycisk będzie wyświetlany w zadany sposób niezależnie od wielkości
i orientacji ekranu.

Przycisk będzie wyświetlany


w prawym górnym rogu układu Jeśli w układzie zostały zdefiniowane
niezależnie do orientacji wypełnienia, to pomiędzy krawędziami
urządzenia i wielkości ekranu. widoku i ekranu będą widoczne odstępy.

168 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Atrybuty do umiejscawiania widoków ¨  LinearLayout
względem układu nadrzędnego ¨  GridLayout

Poniżej przedstawiliśmy kilka najczęściej używanych atrybutów, które służą do


umiejscawiania widoków względem ich układu nadrzędnego. Aby zastosować
wybrany atrybut, należy go dodać do elementu i przypisać mu wartość ”true”:

android:atrybut=”true”

Atrybut Działanie

android: Wyrównuje dolną krawędź widoku Widok jest wyrównany


do lewej i dolnej
layout_alignParentBottom do dolnej krawędzi elementu nadrzędnego. krawędzi elementu
nadrzędnego.

android: Wyrównuje lewą krawędź widoku


layout_alignParentLeft do lewej krawędzi elementu nadrzędnego.

android: Wyrównuje prawą krawędź widoku


layout_alignParentRight do prawej krawędzi elementu nadrzędnego.

Widok jest wyrównany


do prawej i górnej
android: Wyrównuje górną krawędź widoku krawędzi elementu
layout_alignParentTop do górnej krawędzi elementu nadrzędnego. nadrzędnego.

android: Umieszcza widok pośrodku elementu


layout_centerInParent nadrzędnego, zarówno w poziomie,
jak i w pionie.

android: Umieszcza widok pośrodku elementu


layout_centerHorizontal nadrzędnego w poziomie.

android: Umieszcza widok pośrodku elementu


layout_centerVertical nadrzędnego w pionie.

jesteś tutaj  169


Względem elementów sąsiednich

¨  RelativeLayout
Rozmieszczanie widoków względem innych widoków ¨  LinearLayout
¨  GridLayout
Oprócz rozmieszania widoków względem układu nadrzędnego można je także
rozmieszczać względem innych widoków. To rozwiązanie doskonale sprawdza się,
gdy chcemy, by grupa widoków była wyrównana w taki sam sposób, niezależnie
od wielkości i orientacji ekranu.

Aby rozmieścić jeden widok względem innego, w widoku stanowiącym punkt odniesienia
należy określić jego identyfikator, używając w tym celu atrybutu android:id:

android:id=”@+id/button_click_me”
Składnia ”@+id” nakazuje, by Android dodał identyfikator do pliku R.java jako zasób.
Jeśli w tym wyrażeniu pominiemy znak +, to Android nie doda identyfikatora jako
zasobu, a podczas próby skompilowania aplikacji wystąpią błędy.

Poniżej pokazaliśmy, w jaki sposób można utworzyć dwa przyciski, z których pierwszy
jest umieszczony pośrodku układu, a drugi — poniżej pierwszego:
tu
Używamy tego przycisku jako punka
odniesienia do określenia położeni
imy
<RelativeLayout ... > drugiego przycisku, dlatego mus
określić jego identyfikator.
<Button
android:id=”@+id/button_click_me”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_centerInParent=”true”
android:text=”@string/click_me” />
<Button Drugi przycisk ma być
umieszczony poniżej
android:layout_width=”wrap_content” pierwszego, tak by lewe
android:layout_height=”wrap_content” krawędzie obu przycisków
były wyrównane.
android:layout_alignLeft=”@+id/button_click_me”
android:layout_below=”@+id/button_click_me”
android:text=”@string/new_button_text” />
</RelativeLayout>

Poniższe dwa wiersze kodu:

android:layout_alignLeft=”@+id/button_click_me”
android:layout_below=”@+id/button_click_me”

zapewnią, że drugi przycisk będzie umieszczony poniżej pierwszego, a jego


lewa krawędź będzie wyrównana do lewej krawędzi pierwszego przycisku.

170 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Atrybuty do rozmieszczania widoków ¨  LinearLayout
względem innych widoków ¨  GridLayout

Poniżej przedstawiliśmy kolejne atrybuty, których można używać do rozmieszczania jednych


widoków względem innych. Atrybuty te należy dodawać do elementu, którego położenie
określamy, a ich wartością ma być identyfikator widoku stanowiącego punkt odniesienia:

android:atrybut=”@+id/identyfikator_widoku”
Twój widok jest
umieszczony powyżej.
Atrybut Działanie
android:layout_above Umieszcza widok powyżej widoku stanowiącego
punkt odniesienia.

Widok stanowiący
punkt odniesienia.
android:layout_below Umieszcza widok poniżej widoku stanowiącego
punkt odniesienia.
Twój widok jest
umieszczony poniżej.

android:layout_alignTop Wyrównuje górną krawędź widoku z górną krawędzią


widoku stanowiącego punktu odniesienia.
Wyrównuje górne krawędzie widoków.
Wyrównuje dolne krawędzie widoków.
android:layout_alignBottom Wyrównuje dolną krawędź widoku z dolną krawędzią
widoku stanowiącego punktu odniesienia.

Wyrównuje
android:layout_alignLeft Wyrównuje lewą krawędź widoku z lewą krawędzią lewe
widoku stanowiącego punktu odniesienia. krawędzie
widoków.

Wyrównuje
android:layout_alignRight Wyrównuje prawą krawędź widoku z prawą krawędzią prawe
widoku stanowiącego punktu odniesienia. krawędzie
widoków.

android:layout_toLeftOf Umieszcza prawą krawędź widoku przy lewej krawędzi


widoku stanowiącego punkt odniesienia.
Twój widok jest umieszczony
z lewej strony.

android:layout_toRightOf Umieszcza lewą krawędź widoku przy prawej krawędzi


widoku stanowiącego punkt odniesienia.
Twój widok jest umieszczony
z prawej strony.

jesteś tutaj  171


Marginesy

¨  RelativeLayout
Stosowanie marginesów do oddalania widoków od siebie ¨  LinearLayout
¨  GridLayout
Jeżeli zastosujesz przedstawione wyżej atrybuty do rozmieszczania widoków, układ nie będzie
pozostawiał pomiędzy nimi zbyt dużo wolnego miejsca. Aby powiększyć odstępy między
widokami, należy dodać do widoku jeden lub kilka marginesów.

Na przykład załóżmy, że chcemy wyświetlić w układzie dwa widoki i jednocześnie zapewnić,


aby były one oddalone od siebie o 50dp. W tym celu możemy dodać margines o wielkości
50dp u góry dolnego widoku:

<RelativeLayout ... >


<Button
android:id=”@+id/button_click_me”
... />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/button_click_me”
android:layout_below=”@+id/button_click_me”
android:layout_marginTop=”50dp” Dodanie marginesu
powyżej dolnego 50dp
android:text=”@string/button_below” /> przycisku spowodowało
pojawienie się odstępu
</RelativeLayout> między przyciskami.

Poniżej przedstawiliśmy listę marginesów, których można używać w celu dodawania


do układu odstępów między widokami. Te atrybuty dodaje się do widoku, a następnie
przypisuje wartość określającą wielkość marginesu:

android:atrybut=”10dp”

Atrybut Działanie
android:layout_marginTop Dodaje dodatkowy obszar nad widokiem.

android:layout_marginBottom Dodaje dodatkowy obszar pod widokiem.

android:layout_marginLeft Dodaje dodatkowy obszar z lewej strony widoku.

android:layout_marginRight Dodaje dodatkowy obszar z prawej strony widoku.

172 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
RelativeLayout — podsumowanie ¨  LinearLayout
¨  GridLayout
Zanim przejdziemy do następnego typu układów, podsumujmy jeszcze, w jaki sposób są
tworzone układy względne.

Sposób określania układów względnych


Układ względny tworzymy, używając elementu <RelativeLayout>. Musimy przy tym określić
wysokość i szerokość układu, ale ewentualne wypełnienia są opcjonalne:

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”...>
...
</RelativeLayout>

Widoki można rozmieszczać względem układu lub innych widoków


Położenie poszczególnych widoków w układzie określamy poprzez dodawanie do nich
odpowiednich atrybutów. Te atrybuty mogą określić położenie widoku względem układu
nadrzędnego — na przykład w jego prawym dolnym rogu lub pośrodku. Jednak są także dostępne
atrybuty pozwalające określić położenie jednego widoku względem innego. Widok stanowiący
punkt odniesienia określamy, podając jego identyfikator.

Do widoków można dodawać marginesy, by zwiększać odstępy między nimi


Sam układ nie dodaje do umieszczanych w nim widoków żadnego wolnego obszaru wokół nich.
Obszar ten można jednak dodać, stosując marginesy:

android:layout_marginTop=”5dp”
android:layout_marginBottom=”5dp”
android:layout_marginLeft=”5dp”
android:layout_marginRight=”5dp”

Dotychczas używaliśmy wyłącznie układów względnych, istnieje


jednak także inny, powszechnie używany typ układów: układ liniowy.
Przyjrzymy mu się teraz nieco bliżej.

jesteś tutaj  173


Układ liniowy

¨  RelativeLayout
Układ LinearLayout wyświetla widoki ¨  LinearLayout
w jednym wierszu lub kolumnie ¨  GridLayout

Układ liniowy wyświetla widoki jeden obok drugiego, umieszczając je bądź to w pionie,
bądź w poziomie. W przypadku rozmieszczania elementów w pionie utworzą one
pojedynczą kolumnę, natomiast w przypadku wyświetlania ich w poziomie — jeden wiersz.

Sposób definiowania układu liniowego


Układ liniowy definiujmy, używając elementu <LinearLayout>, pokazanego w poniższym
przykładzie:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent” To są te same atrybuty, których
Aby utworzyć układ używaliśmy już w układzie
android:layout_height=”match_parent” względnym.
liniowy, musisz
użyć elementu android:orientation=”vertical” Widoki mają być rozmieszczane
<LinearLayout>.
...> w pionie.
...
</LinearLayout>

W przypadku układu liniowego obowiązkowe jest zastosowanie atrybutów


android:layout_width, android:layout_height oraz android:orientation.
Pierwsze dwa określają odpowiednio szerokość i wysokość układu (dokładnie tak
samo, jak było w przypadku układu względnego), a trzeci pozwala określić, w jakim
kierunku mają być rozmieszczane widoki.

Widoki będą rozmieszczane w pionie, jeśli zastosujemy atrybut o postaci:

android:orientation=”vertical”
Widoki będą rozmieszczane w poziomie, jeśli zastosujemy atrybut o postaci: Układ liniowy o orientacji
poziomej.
android:orientation=”horizontal”

Układ
liniowy
o orientacji
pionowej.

W przypadku orientacji poziomej


poszczególne widoki tworzą jeden wiersz.

W przypadku orientacji
pionowej poszczególne
widoki tworzą jedną kolumnę.

174 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Układ liniowy wyświetla widoki w kolejności ¨  LinearLayout
ich występowania w kodzie XML ¨  GridLayout

Podczas definiowania układu liniowego poszczególne widoki należy dodawać


do niego w takiej kolejności, w jakiej chcemy, aby były wyświetlane. A zatem
jeśli chcemy, by komponent TextView był umieszczony nad przyciskiem,
to musimy dodać go do układu jako pierwszy:
Jeśli w kodzie XML zdefiniujesz komponent
<LinearLayout ... > TextView przed przyciskiem, to po wyświetleniu
układu będzie on widoczny nad przyciskiem.
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/textView1” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/click_me” />
</LinearLayout>

Tworząc układ liniowy, identyfikatory widoków trzeba określać wyłącznie


w przypadku, gdy będziemy się do nich jawnie odwoływać w kodzie
aktywności. Wynika to z faktu, że układ liniowy określa, gdzie należy Atrybuty
umieścić poszczególne widoki, na podstawie ich kolejności w kodzie
XML. Widoki nie muszą odwoływać się do siebie nawzajem w celu
android:layout_width
określenia miejsca, w którym należy je wyświetlić. i android:layout_height
Podobnie jak w przypadku układu względnego, także w układzie liniowym trzeba obowiązkowo
można określać wymiary poszczególnych widoków, używając atrybutów
android:layout_width i android:layout_height. Poniższy kod: stosować we wszystkich
android:layout_width=”wrap_content” widokach, niezależnie od
oznacza, że szerokość widoku ma jedynie wystarczyć do wyświetlenia typu używanego układu.
jego zawartości — na przykład aby wyświetlić tekst na przycisku
lub w komponencie TextView. Z kolei ten kod:
Mogą one przyjmować
android:layout_width=”match_parent”
wartości warp_content,
oznacza, że widok ma zająć całą dostępną szerokość układu nadrzędnego.
match_parent lub
konkretny rozmiar,
na przykład 16 dp.

jesteś tutaj  175


Zmiana układu

¨  RelativeLayout
Zmieńmy nieco prosty układ liniowy ¨  LinearLayout
¨  GridLayout
Na pierwszy rzut oka układ liniowy może się wydawać bardzo prosty i mało elastyczny.
W końcu jedyne, co potrafi, to organizowanie widoków w ściśle określonej kolejności.
Aby jednak zyskać nieco więcej elastyczności, możemy modyfikować wygląd układu,
korzystając z jego dodatkowych atrybutów. Aby pokazać te dodatkowe możliwości,
spróbujmy nieco zmodyfikować bardzo prosty układ liniowy.

Nasz układ składa się z dwóch pól tekstowych i przycisku. W początkowej


wersji układu wszystkie te widoki są wyświetlone w pionie, jeden pod drugim,
jak pokazaliśmy na poniższym rysunku:

Każdy widok przyjmuje


możliwie jak
najmniejszą wysokość.

Zmienimy ten początkowy układ w taki sposób, by przycisk był


wyświetlony w prawym dolnym rogu układu, a cały pozostały
dostępny obszar zajmowało jedno z pól tekstowych.
Pole tekstowe Treść wiadomości
zostało znacznie powiększone.

Przycisk Wyślij jest teraz


wyświetlany w prawym
dolnym rogu ekranu.

176 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Początkowa wersja naszego układu liniowego ¨  LinearLayout
¨  GridLayout
Nasz układ liniowy składa się z dwóch pól tekstowych i przycisku. Przycisk ma etykietę
o treści Wyślij, natomiast pola tekstowe zawierają odpowiednio następujące teksty
podpowiedzi: Do i Treść wiadomości.

Tekst podpowiedzi pola tekstowego to napis, który jest wyświetlany w polu, gdy jest
ono puste. Ma on stanowić dla użytkownika podpowiedź sugerującą, co należy w pisać
w danym polu. Definiuje się go przy użyciu atrybutu android:hint:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:orientation=”vertical”
tools:context=”.MainActivity” >
Szerokość pól tekstowych
jest równa szerokości układu
<EditText nadrzędnego.
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
Atrybut android:hint określa tekst podpowiedz
sugerujący, co użytkownik powinien wpisa i,
android:hint=”@string/to” /> ć
w danym polu.
Wartości
tych zasobów <EditText
łańcuchowych
zostały android:layout_width=”match_parent”
standardowo
zdefiniowane
android:layout_height=”wrap_content”
w pliku android:hint=”@string/message” />
strings.xml.

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/send” />
</LinearLayout>

Każdy z widoków umieszczonych w układzie będzie miał jak najmniejszą


wysokość — niezbędną do wyświetlenia jego zawartości. A zatem w jaki
sposób możemy zwiększyć wysokość pola do podania treści wiadomości?

jesteś tutaj  177


Przybieramy na wadze

¨  RelativeLayout
Rozciągaaaaamy widok, zwiększając jego wagę ¨  LinearLayout
¨  GridLayout
Wysokość wszystkich widoków w naszym przykładowym układzie jest możliwie
jak najmniejsza — zajmują one w pionie tylko tyle miejsca, ile jest niezbędne
do wyświetlenia ich zawartości. My chcielibyśmy natomiast, by pole przeznaczone
do podania treści widomości zajęło całą wysokość układu dostępną po
wyświetleniu pozostałych widoków.

Chcemy, by pole Treść


wiadomości zajęło całą
wysokość układu dostępną
po wyświetleniu pozostałych
widoków.

W tym celu musimy dodać temu polu tekstowemu nieco wagi (ang. weight).
Dodając widokowi wagę, informujemy, że ma on zająć nieco więcej miejsca
w układzie.

Do określania wagi widoku służy następujący atrybut:

android:layout_weight=”liczba”

gdzie liczba to liczba większa od 0.

W przypadku określenia wagi jakiegoś widoku układ w pierwszej kolejności


upewnia się, czy każdy widok dysponuje dostatecznie dużym obszarem,
koniecznym do wyświetlenia jego zawartości. Innymi słowy: upewnia się, czy każdy
przycisk ma miejsce na wyświetlenie umieszczonego na nim tekstu, każde pole
tekstowe — na wyświetlenie tekstu podpowiedzi, i tak dalej. Następnie układ
określa wielkość pozostałego obszaru i dzieli go proporcjonalnie, rozdzielając
pomiędzy elementy, których waga jest większa od 1.

178 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Dodawanie wagi do widoków ¨  LinearLayout
¨  GridLayout
Chcemy, by pole tekstowe do podawania treści wiadomości zajmowało jak najwięcej
miejsca w układzie. W tym celu dodamy do niego atrybut layout_weight i przypiszemy
mu wartość 1. Ponieważ będzie on jedynym widokiem, w którym określimy wagę,
zostanie on rozciągnięty w pionie i zajmie całą dostępną wysokość układu pozostałą
po wyświetleniu reszty widoków. Poniżej przedstawiliśmy zmodyfikowany kod układu:

<LinearLayout ... >


<Button>
<EditText Do tych elementów <EditText> i
liśm y atry butu layo ut_w eight.
nie doda
sca,
android:layout_width=”match_parent” Zajmą one zatem tylko tyle miej
android:layout_height=”wrap_content” ile jest konieczne do wyświetlenia
ich zawartości.
android:hint=”@string/to” />

<EditText
To jest jedyny
widok w układzie, android:layout_width=”match_parent” Wysokość tego widoku zostanie określona
w którym android:layout_height=”0dp” przez układ liniowy na postawie wartości
określiliśmy wagę. atrybutu layout_weight. Przypisanie atrybutowi
Zostanie on zatem android:layout_weight=”1” layout_height wartości 0 dp jest bardziej
rozciągnięty i zajmie efektywne niż użycie wartości „wrap_content”.
cały wolny obszar android:hint=”@string/message” />
układu, który nie
został zużyty przez
pozostałe widoki. <Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/send” />
</LinearLayout>

Przypisanie polu tekstowemu do podania treści wiadomości wagi 1 oznacza, Domyślnie tekst
że ma ono zająć cały dostępny obszar ekranu, który nie został zajęty przez podpowiedzi, Treść
inne widoki. Dzieje się tak dlatego, że do żadnego innego widoku nie wiadomości, jest
wyświetlany w połowie
dodaliśmy atrybutu layout_weight. wysokości pola tekstowego.
Zajmiemy się nim już
niebawem.
Pole do podania treści
wiadomości ma wagę 1.
Ponieważ jest to jedyny widok
układu, w którym została
określona waga, zostanie
ono rozciągnięte i zajmie
cały dostępny obszar układu
pozostały po wyświetleniu
reszty widoków.

jesteś tutaj  179


Więcej wagi

¨  RelativeLayout
Dodawanie wagi do większej liczby widoków ¨  LinearLayout
¨  GridLayout
W naszym przykładzie atrybut layout_weight dodaliśmy tylko do jednego widoku.
A co by się stało, gdyby takich widoków było więcej?

Załóżmy, że pierwszemu polu tekstowemu, Do, przypiszemy wagę 1, a drugiemu,


Treść wiadomości, przypiszemy wagę 2:

<LinearLayout ... >


...
<EditText
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:layout_weight=”1”
android:hint=”@string/to” />

<EditText
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:layout_weight=”2”
android:hint=”@string/message” />
...
Pole Do ma wagę 1, zatem
</LinearLayout> zostanie mu przydzielona
1/3 pozostałego miejsca
w układzie.

Aby określić, ile dodatkowego miejsca zajmie każdy z widoków,


musimy zacząć od zsumowania wartości atrybutów layout_weight
wszystkich widoków. W tym przypadku suma ta wyniesie 1+2=3.
Wielkość dodatkowego miejsca przydzielonego każdemu widokowi Pole Treść
będzie odpowiadała jego wadze podzielonej przez sumę wszystkich wiadomości ma
wagę 2, więc
wag. Pole Do ma wagę 1, zatem zostanie mu przydzielona 1/3 zostaną mu
pozostałego miejsca w układzie. Pole Treść wiadomości ma wagę 2, przydzielone 2/3
pozostałego miejsca
więc zostaną mu przydzielone 2/3 pozostałego miejsca. w układzie.

180 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Wykorzystanie ciężkości do określania ¨  LinearLayout
miejsca wyświetlania tekstu ¨  GridLayout

Kolejną zmianą, którą musimy wprowadzić, jest przesunięcie tekstu


podpowiedzi wyświetlanego w polu tekstowym do wpisywania treści
wiadomości. Obecnie jest on wyśrodkowany w pionie. Chcemy to jednak
zmienić, tak aby był on wyświetlany u góry pola. Taki efekt możemy uzyskać, Musimy przesunąć
stosując atrybut android:gravity. tekst podpowiedzi ze
środka na samą górę
widoku.
Atrybut android:gravity służy do określania, gdzie wewnątrz widoku ma
być wyświetlana jego zawartość — na przykład gdzie ma być wyświetlany tekst
w polu tekstowym. Jeśli chcemy, by tekst był wyświetlany u góry pola, to cała
sztuka sprowadza się do zastosowania poniższego atrybutu:

android:gravity=”top”
A zatem dodamy teraz atrybut android:gravity do pola tekstowego Treść
wiadomości, tak by prezentowany w nim tekst podpowiedzi był wyświetlony
przy jego górnej krawędzi:
<LinearLayout ... >
...
<EditText
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:layout_weight=”1” Tekst wyświetlany w polu tekstowym
android:gravity=”top” będzie widoczny u góry tego pola.

android:hint=”@string/message” />
...
</LinearLayout>

Teraz tekst podpowiedzi


jest widoczny u góry
pola.
Jazda próbna
Dodanie atrybutu android:gravity do pola tekstowego służącego do
wpisywania treści wiadomości powoduje, że prezentowany w nim tekst
podpowiedzi jest widoczny u góry widoku — dokładnie tak jak chcieliśmy.

Na następnej stronie podaliśmy listę innych wartości, których można


używać w atrybucie android:gravity.

jesteś tutaj  181


Ciężkość

¨  RelativeLayout
Stosowanie atrybutu android:gravity ¨  LinearLayout
— lista wartości ¨  GridLayout

Poniżej przedstawiliśmy kilka kolejnych wartości, których można używać


w atrybucie android:gravity. Wystarczy dodać ten atrybut do wybranego
widoku i przypisać mu jedną z poniższych wartości:

android:gravity=”wartosc”

Wartość Działanie

top Zawartość jest wyświetlona u góry widoku.

bottom Zawartość jest wyświetlona u dołu widoku.

left Zawartość jest wyświetlona z lewej strony widoku.

right Zawartość jest wyświetlona z prawej strony widoku.

center_vertical Zawartość widoku jest wyśrodkowana w pionie.

center_horizontal Zawartość widoku jest wyśrodkowana w poziomie.

center Zawartość widoku jest wyśrodkowana w pionie i w poziomie.

fill_vertical Zawartość wypełnia całą dostępną wysokość widoku.

fill_horizontal Zawartość wypełnia całą dostępną szerokość widoku.

fill Zawartość wypełnia cały dostępny obszar widoku.

Atrybut android:gravity pozwala określić, w którym


miejscu widoku ma być wyświetlana jego zawartość.

182 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Przesunięcie przycisku w prawo ¨  LinearLayout
za pomocą atrybutu layout_gravity ¨  GridLayout

Jest jeszcze jedna modyfikacja, którą musimy wprowadzić w naszym układzie.


Przycisk Wyślij jest obecnie umieszczony w jego lewym dolnym rogu. Musimy
przesunąć go w prawo, tak aby był wyświetlony w prawym, dolnym rogu układu.
Do tego celu zastosujemy atrybut android:layout_gravity.

Atrybut android:layout_gravity pozwala określać, w którym miejscu


obszaru otaczającego widok w układzie liniowym ma być wyświetlany dany
widok. Możemy go zastosować, by na przykład przesunąć widok w prawo lub
wyśrodkować w poziomie. Aby przesunąć przycisk w prawo, wystarczy użyć
atrybutu o następującej postaci:

android:layout_gravity=”right”

Ale dlaczego musimy


używać atrybutu layout_
gravity? Wcześniej używaliśmy
atrybutu layout_alignRight — czy
nie możemy zatem użyć go i tym
razem?

Atrybut android:layout_alignRight można stosować 
wyłącznie w układach względnych

Układy mają kilka wspólnych atrybutów, takich jak android:layout_


width lub android:layout_height. Jednak wiele atrybutów jest
charakterystycznych dla jednego, konkretnego typu układów.

Większości atrybutów, które przedstawiliśmy, prezentując układy


względne, nie można używać w układach liniowych. W układach
liniowych wykorzystywane jest pojęcie ciężkości, dlatego jeśli chcemy
przesunąć widok na prawą stronę układu, musimy użyć poniższego
atrybutu:

android:layout_gravity=”right”

Na następnej stronie zamieściliśmy listę innych wartości, których


można używać w atrybucie android:layout_gravity.

jesteś tutaj  183


Atrybut layout_gravity

¨  RelativeLayout
Inne wartości, których można używać ¨  LinearLayout
w atrybucie android:layout_gravity ¨  GridLayout

Poniżej przedstawiliśmy inne wartości, których można używać w atrybucie


android:layout_gravity. Wystarczy dodać ten atrybut do widoku,
a następnie przypisać mu jedną z poniższych wartości:

android:layout_gravity=”wartosc”

Wartość Działanie

top, bottom, left, right Umieszcza widok u góry, u dołu, z lewej bądź z prawej strony
pojemnika, czyli obszaru, w którym się on znajduje.

start, end Umieszcza widok na początku bądź na końcu pojemnika,


w którym się on znajduje.

center_vertical, center_horizontal Wyśrodkowuje widok w pojemniku w pionie lub w poziomie.

center Wyśrodkowuje widok w pojemniku w pionie i w poziomie.

fill_vertical, fill_horizontal Rozszerza widok, tak by zajmował on całą dostępną wysokość


lub szerokość pojemnika.

fill Rozszerza widok, tak by zajmował on cały dostępny obszar


pojemnika.

Atrybut android:layout_gravity pozwala określać, w którym


miejscu dostępnego obszaru ma być wyświetlany widok.

Atrybut android:layout_gravity określa rozmieszczenie


samego widoku, natomiast atrybut android:gravity
informuje, w którym miejscu widoku należy wyświetlać
jego zawartość.

184 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Kompletny układ liniowy ¨  LinearLayout
¨  GridLayout
Oto kompletny kod naszego układu liniowego:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:orientation=”vertical”
tools:context=”.MainActivity” >

<EditText
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:hint=”@string/to” />

<EditText
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:layout_weight=”1”
android:gravity=”top”
android:hint=”@string/message” />
Zawartość pola do
<Button wprowadzania treści
wiadomości będzie
android:layout_width=”wrap_content” wyświetlana na samej górze
android:layout_height=”wrap_content” tego widoku. Dzięki temu
będziemy mieć naprawdę
android:layout_gravity=”right” dużo miejsca do wpisywania.
android:text=”@string/send” />
</LinearLayout>

Atrybut android:gravity różni się Przycisk Wyślij jest


od atrybutu android:layout_gravity. wyświetlony w prawym
Pierwszy z nich odnosi się do dolnym rogu układu.
zawartości widoku, drugi zaś do
samego widoku.

jesteś tutaj  185


Podsumowanie

¨  RelativeLayout
LinearLayout — podsumowanie ¨  LinearLayout
¨  GridLayout
Poniżej zamieściliśmy podsumowanie informacji o tworzeniu układów liniowych.

Sposób określania układów liniowych


Układ liniowy tworzymy, używając elementu <LinearLayout>. Musimy przy tym określić
szerokość, wysokość oraz orientację układu; podawanie wypełnienia jest opcjonalne:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
...>
...
</LinearLayout>

Widoki są wyświetlane w kolejności dodawania


Podczas definiowania układu liniowego widoki dodajemy do niego w takiej kolejności,
w jakiej chcemy, aby były wyświetlane.

Widoki można rozciągać, używając wagi


Domyślnie wszystkie widoki zajmują tylko tyle miejsca, ile jest niezbędne do wyświetlenia ich
zawartości. Jeśli chcemy, by któryś z widoków zajął więcej miejsca, możemy go rozciągnąć,
używając atrybutu layout_weight:

android:layout_weight=”1”

Położenie zawartości w widoku można określać, używając ciężkości


Położenie zawartości widoku wewnątrz danego widoku możemy określać za pomocą atrybutu
android:gravity. W ten sposób możemy określić na przykład, gdzie wyświetlić tekst wewnątrz
pola tekstowego.

Do określania położenia widoku w otaczającym go obszarze służy


atrybut layout_gravity
Atrybut android:layout_gravity pozwala określać, w którym miejscu obszaru otaczającego
widok w układzie liniowym ma zostać wyświetlony dany widok. Na przykład można go używać,
by przesunąć widok w prawo lub wyśrodkować w poziomie.

To już wszystko, co chcieliśmy napisać na temat układów liniowych. Istnieje jeszcze


jeden typ układów, który poznamy: układ siatki.

186 Rozdział 5.
Interfejs użytkownika

Zaostrz ołówek
Poniżej zamieściliśmy kod XML układu aplikacji Doradca piwny, którą
napisaliśmy w rozdziale 2. Zmień go na układ liniowy, którego wygląd
pokazaliśmy u dołu strony.

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
tools:context=”.FindBeerActivity” >

<Spinner
android:id=”@+id/color”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentTop=”true”
android:layout_centerHorizontal=”true”
android:layout_marginTop=”37dp”
android:entries=”@array/beer_colors” />

<Button
android:id=”@+id/find_beer”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/color”
android:layout_below=”@+id/color”
android:text=”@string/find_beer”
android:onClick=”onClickFindBeer” />

<TextView
android:id=”@+id/brands”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/find_beer”
android:layout_below=”@+id/find_beer”
android:layout_marginTop=”18dp”
najbardziej
android:text=”@string/brands” /> Ten układ nie wygra nagrody dla
czy potrafisz
stylowego układu, ale ciekawe,
</RelativeLayout> kod XML tak,
zmodyfikować przedstawiony tu
by uzyskać taki sam wygląd.

jesteś tutaj  187


Rozwiązanie zadania

Zaostrz ołówek
Rozwiązanie Poniżej zamieściliśmy kod XML układu aplikacji Doradca piwny, którą
napisaliśmy w rozdziale 2. Zmień go na układ liniowy, którego wygląd
pokazaliśmy u dołu strony.

<RelativeLinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
Zmień układ android:layout_width=”match_parent”
na liniowy.
android:layout_height=”match_parent”
android:paddingBottom=”16dp” Układ liniowy musi zawierać atrybut
android:paddingLeft=”16dp” android:orientation. Użyj wartości
android:paddingRight=”16dp” „horizontal”, aby wyświetlać widoki
jeden obok drugiego w poziomie.
android:paddingTop=”16dp”
android:orientation=”horizontal”
tools:context=”.FindBeerActivity” >

<Spinner
android:id=”@+id/color”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignParentTop=”true” Te wiersze nie będą nam już potrzebne.
android:layout_centerHorizontal=”true”
android:layout_marginTop=”37dp”
android:entries=”@array/beer_colors” />

<Button
android:id=”@+id/find_beer”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/color” Te wiersze nie będą nam już potr
zebne.
android:layout_below=”@+id/color”
android:text=”@string/find_beer”
android:onClick=”onClickFindBeer” />

<TextView
android:id=”@+id/brands”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_alignLeft=”@+id/find_beer” zebne.
Te wiersze nie będą nam już potr
android:layout_below=”@+id/find_beer”
Zmień układ android:layout_marginTop=”18dp”
na liniowy.
android:text=”@string/brands” />
</RelativeLinearLayout>

188 Rozdział 5.
Interfejs użytkownika

¨  RelativeLayout
Układ GridLayout wyświetla widoki w siatce ¨  LinearLayout
¨  GridLayout
Układ GridLayout dzieli ekran na siatkę składającą się z wierszy i kolumn
i pozwala umieszczać widoki we wskazanych komórkach:

Każdy Układ GridLayout 
z tych wymaga stosowania 
obszarów
jest
Obejrzyj to! API poziomu 14  
komórką. lub wyższego.

Jeśli planujesz stosowanie układu


siatki, upewnij się, czy Twoje
aplikacje będą używać wersji SDK
obsługującej API poziomu 14
lub wyższego.

Jak jest definiowany układ siatki?


Układ siatki definiuje się podobnie jak układy innych typów,
lecz należy przy tym użyć elementu <GridLayout>:

<GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent” To są te same atrybuty, których
używaliśmy w układach innych
Tu należy użyć android:layout_height=”match_parent” typów.
znaczników
<GridLayout>. android:columnCount=”2” Ten atrybut określa, ile kolumn
ma mieć siatka (w tym przypadk
... > u
będą dwie kolumny).
...
</GridLayout>

Liczba kolumn, z których ma się składać siatka, jest określana w następujący sposób:

android:columnCount=”liczba”

gdzie liczba określa liczbę kolumn. W podobny sposób możemy określić liczbę
wierszy siatki:

android:rowCount=”liczba”

choć w praktyce można ją pominąć i pozwolić, by Android sam ją obliczył na podstawie


liczby widoków umieszczonych w układzie. W takim przypadku Android utworzy tyle
wierszy, ile będzie potrzebnych do wyświetlenia wszystkich widoków.

jesteś tutaj  189


Układ siatki

Dodawanie widoków do układu siatki ¨  RelativeLayout


¨  LinearLayout
Widoki dodajemy do układu siatki podobnie jak dodaje się je do układu liniowego:
¨  GridLayout

<GridLayout ... >

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/textview” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/click_me” />

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:hint=”@string/edit” />

</GridLayout>

Podobnie jak w przypadku układu liniowego, także w przypadku


stosowania układu siatki umieszczane w nim widoki nie muszą
mieć identyfikatorów, chyba że planujemy odwoływać się do
nich w kodzie aktywności. Poszczególne widoki umieszczane
w układzie nie muszą odwoływać się do siebie nawzajem, więc
stosowanie identyfikatorów nie jest niezbędne.

Domyślnie układ siatki rozmieszcza widoki w kolejnych


komórkach w takiej samej kolejności, w jakiej są one zapisane
w kodzie XML. A zatem jeśli nasz układ będzie się składał
z dwóch kolumn, to pierwszy widok zostanie umieszczony
w pierwszej komórce siatki, drugi w drugiej i tak dalej.

Wadą takiego rozwiązania jest to, że usunięcie widoku z układu


może doprowadzić do drastycznej zmiany jego wyglądu.
Rozwiązaniem tego problemu jest określanie, w którym miejscu
układu mają być wyświetlane poszczególne widoki i ile kolumn ma
zajmować każdy z nich.

190 Rozdział 5.
Interfejs użytkownika

Utwórzmy nowy układ siatki ¨  RelativeLayout


¨  LinearLayout
Aby przyjrzeć się temu rozwiązaniu w akcji, utwórzmy układ siatki, który
¨  GridLayout
będzie określał, gdzie należy umieścić poszczególne widoki i ile kolumn
ma zajmować każdy z nich. Nasz układ będzie się składał z komponentu
TextView prezentującego słowo „Do”, pola tekstowego zawierającego tekst
podpowiedzi „Wpisz adres e-mail”, drugiego pola tekstowego zawierającego
tekst podpowiedzi „Treść wiadomości” oraz przycisku z etykietą „Wyślij”:

Ten przykład jest podobny do


przedstawionego wcześniej ukła
du
liniowego, choć różni się od nieg
o
napisem wyświetlonym z lewej
strony pola tekstowego w górnym
wierszu oraz wyśrodkowanym
przyciskiem u dołu.

Oto, co mamy zamiar zrobić:


1 Naszkicować wygląd interfejsu użytkownika i podzielić go na wiersze
i kolumny.
W ten sposób będzie nam łatwiej wyobrazić sobie, jak należy zdefiniować układ.

2 Utworzyć układ wiersz po wierszu.

jesteś tutaj  191


Szkicujemy układ

Zaczynamy od naszkicowania układu ¨  RelativeLayout


¨  LinearLayout
Pierwszym etapem prac nad naszym nowym układem będzie jego naszkicowanie. Dzięki temu
¨  GridLayout
łatwiej nam będzie określić, z ilu wierszy i kolumn ma się on składać, gdzie należy umieścić
poszczególne widoki oraz ile kolumn powinien zajmować każdy z tych widoków.

1. kolumna 2. kolumna

W pierwszym wierszu w pierwszej


kolumnie jest umieszczony napis „Do”,
1. wiersz Do Wpisz adres e-mail a w drugiej kolumnie znajduje się pole
testowe z tekstem podpowiedzi „Wpisz
adres e-mail”.
Treść wiadomości
W drugim wierszu jest umieszczone pole
tekstowe z tekstem podpowiedzi „Treść
2. wiersz wiadomości”. Pole to zaczyna się w pierwszej
kolumnie i rozciąga także na drugą. Musi ono
zająć cały dostępny obszar wiersza.

Trzeci wiersz zawiera przycisk z napisem


„Wyślij”. Przycisk jest wyśrodkowany
3. wiersz Wyślij
w obszarze obejmującym obie kolumny,
co oznacza, że musi zajmować obie kolumny
układu.

Nasz układ siatki musi się składać z dwóch kolumn


Wszystkie widoki będziemy mogli rozmieścić w planowany sposób, jeśli utworzymy
układ siatki składający się z dwóch kolumn:

<GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:columnCount=”2”
tools:context=”.MainActivity” >
</GridLayout>

Skoro zdefiniowaliśmy podstawę układu, możemy zacząć dodawać do niego poszczególne widoki.

192 Rozdział 5.
Interfejs użytkownika

Wiersz 0: dodajemy widoki do określonych wierszy i kolumn ¨  RelativeLayout


¨  LinearLayout
Pierwszy wiersz układu siatki składa się z komponentu TextView umieszczonego
¨  GridLayout
w pierwszej kolumnie oraz pola tekstowego umieszczonego w drugiej kolumnie.
Zacznij od dodania obu tych widoków do układu: Do Wpisz adres e-mail

<GridLayout...>
<TextView W układach siatki można używać
android:layout_width=”wrap_content”
atrybutów android:gravity
android:layout_height=”wrap_content”
android:text=”@string/to” /> i android:layout_gravity.

<EditText
android:layout_width=”wrap_content”
Atrybutu layout_gravity możemy używać
android:layout_height=”wrap_content” także w układach siatki. W tym układzie
zastosowaliśmy wartośc fill_horizontal, gdyż
android:layout_gravity=”fill_horizontal” chcemy, by pole tekstowe zajmowało całą
android:hint=”@string/to_hint” /> szerokość dostępnego obszaru.
</GridLayout>

Teraz użyjemy dwóch kolejnych atrybutów, android:layout_row i android:layout_


column, aby określić, w którym wierszu i której kolumnie układu ma zostać Indeksy wierszy
umieszczony konkretny widok. Indeksy wiersza i kolumny zawsze są liczone od 0,
a zatem aby widok został umieszczony w pierwszej kolumnie pierwszego wiersza, i kolumn są
musimy użyć poniższych atrybutów: numerowane od 0.
Numeracja wierszy i kolumn
android:layout_row=”0” zaczyna się od 0, a zatem te dwa A zatem atrybut
android:layout_column=”0” atrybuty wskazują na pierwszy
wiersz i pierwszą kolumnę. layout_column=”n”
Spróbujmy więc zastosować te atrybuty, by umieścić komponent TextView wskazuje na kolumnę
w kolumnie 0 i pole tekstowe w kolumnie 1.
n+1 w układzie.
<GridLayout...>

<TextView
...
Kolumna 0 Kolumna 1
android:layout_row=”0”
android:layout_column=”0”
android:text=”@string/to” />
Wiersz 0 Do Wpisz adres e-mail

<EditText
...
android:layout_row=”0”
android:layout_column=”1”
android:hint=”@string/to_hint” />
</GridLayout>
jesteś tutaj  193
Scalanie komórek

Wiersz 1: tworzymy widok zajmujący komórki kilku kolumn ¨  RelativeLayout


¨  LinearLayout
W drugim wierszu układu chcemy umieścić duże pole tekstowe,
¨  GridLayout
które będzie zajmowało obszar komórki z pierwszej i z drugiej
kolumny. W ten sposób pole zajmie całą szerokość wiersza.

Aby widok został wyświetlony w kilku kolumnach, najpierw musimy


określić, w której kolumnie ma się on zaczynać. Chcemy, by widok Kolumna 0 Kolumna 1
zaczynał się w pierwszej kolumnie drugiego wiersza, więc określimy
jego położenie, używając poniższych atrybutów:

android:layout_row=”1” Wiersz 1 Treść wiadomości


android:layout_column=”0”

Oprócz tego chcemy, by widok rozciągał się na obie kolumny


wiersza — ten efekt możemy uzyskać, stosując atrybut
android:layout_columnSpan, który jest używany w następujący
sposób:

android:layout_columnSpan=”liczba”

gdzie liczba określa, ile kolumn dany widok ma zajmować. Liczba zajmowanych kolumn: 2
W naszym przypadku zastosujemy atrybut o następującej postaci:

android:layout_columnSpan=”2”

Łącząc to wszystko w jedną całość, uzyskamy taki oto kod


definiujący pole tekstowe do wpisywania treści wiadomości:

<GridLayout...>
<TextView... />
To są widoki dodane na poprzedniej stronie do wiersza 0.
<EditText.../>
<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill” Chcemy, aby widok zajął cały dostępny obszar
i aby umieszczony w nim tekst był wyświetlany
android:gravity=”top” u góry pola.
android:layout_row=”1”
android:layout_column=”0” Widok zaczyna się w kolumnie 0 i obejmuje dwie kolumny.
android:layout_columnSpan=”2”
android:hint=”@string/message” />
</GridLayout>

Skoro załatwiliśmy sprawę dwóch pierwszych wierszy układu,


czas zająć się przyciskiem umieszczonym w trzecim wierszu.

194 Rozdział 5.
Interfejs użytkownika

Wiersz 2: tworzymy widok zajmujący komórki kilku kolumn ¨  RelativeLayout


¨  LinearLayout
Chcemy, by przycisk został wyśrodkowany w obszarze obejmującym obie kolumny wiersza,
¨  GridLayout
jak na poniższym rysunku:

Kolumna 0 Kolumna 2

Wiersz 2 Wyślij

Liczba zajmowanych kolumn: 2

Magnesiki układowe
Napisaliśmy kod, który służy do wyśrodkowania przycisku Wyślij w trzecim
wierszu układu siatki, ale nagły podmuch strącił niektóre magnesiki na ziemię.
Czy możesz odtworzyć kod, używając przedstawionych poniżej magnesików?

<GridLayout...>
<TextView... />
To są widoki, które dodaliśmy już wcześniej.
<EditText.../>
<EditText.../>

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
fill_horizontal
android:layout_row=...............
"0"
android:layout_column=............... "2"
"0"
android:layout_gravity=...............
"1"
"1"
android:layout_columnSpan=...............
"2"

android:text=”@string/send” />
center_horizontal
</GridLayout>

Odpowiedź znajdziesz na stronie 224.


jesteś tutaj  195
Kod układu

Pełny kod układu siatki ¨  RelativeLayout


¨  LinearLayout
<GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
¨  GridLayout
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:columnCount=”2”
tools:context=”.MainActivity” >

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”0”
android:layout_column=”0”
android:text=”@string/to” />

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill_horizontal”
android:layout_row=”0”
android:layout_column=”1”
android:hint=”@string/to_hint” />

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill”
android:gravity=”top”
android:layout_row=”1”
android:layout_column=”0”
android:layout_columnSpan=”2”
android:hint=”@string/message” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”2” Przycisk zajmuje dwie kolumny,
android:layout_column=”0” przy czym zaczyna się w pierwszej
kolumnie drugiego wiersza układu.
android:layout_gravity=”center_horizontal”
android:layout_columnSpan=”2”
android:text=”@string/send” />
</GridLayout>

196 Rozdział 5.
Interfejs użytkownika

GridLayout — podsumowanie ¨  RelativeLayout


¨  LinearLayout
Oto, w jaki sposób należy tworzyć układy siatki.
¨  GridLayout

Jak utworzyć układ siatki?


Układy siatki tworzy się za pomocą elementu <GridLayout>. Musimy przy tym określić liczbę
kolumn układu, do czego służy atrybut android:columnCount. Liczbę wierszy można określić
przy użyciu atrybutu android:rowCount:

<GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”2”
... >
...
</GridLayout>

Określanie, w której kolumnie i którym wierszu ma się znaleźć widok


Wiersz i kolumnę, w których ma się znaleźć dany widok, określamy za pomocą atrybutów
android:layout_row i android:layout_column. Indeksy wierszy i kolumn zaczynają się
od 0, a zatem aby umieścić widok w pierwszym wierszu i pierwszej kolumnie, trzeba użyć
atrybutów o postaci:

android:layout_row=”0”
android:layout_column=”0”

Określanie liczby kolumn zajmowanych przez widoki


Atrybut android:layout_columnSpan służy do określania liczby kolumn, które ma zajmować
dany widok. Na przykład jeśli chcemy, by widok zajmował dwie kolumny, to musimy użyć
poniższego atrybutu:

android:layout_columnSpan=”2”

jesteś tutaj  197


Ćwiczenie

Bądź układem 3
Trzy spośród pięciu układów pokazanych
na tej stronie zostały utworzone przez
układy przedstawione na następnej
stronie. Twoim zadaniem
jest dopasowanie każdego
z trzech układów do ekranów
reprezentujących ich wygląd.

198 Rozdział 5.
Interfejs użytkownika

A <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”3”
tools:context=”.MainActivity” >
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill”
android:layout_columnSpan=”3”
android:text=”@string/hello” />
</GridLayout>

B <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”2”
tools:context=”.MainActivity” >
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill”
android:layout_columnSpan=”2”
android:text=”@string/hello” />
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/hi” />
</GridLayout>

C <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”2”
tools:context=”.MainActivity” >
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”0”
android:layout_column=”0”
android:layout_columnSpan=”2”
android:text=”@string/hello” />
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”1”
android:layout_column=”0”
android:text=”@string/hi” />
</GridLayout>

jesteś tutaj  199


Rozwiązanie

Bądź układem. Rozwiązanie


Trzy spośród pięciu układów pokazanych
na tej stronie zostały utworzone przez
układy przedstawione na następnej
stronie. Twoim zadaniem
jest dopasowanie każdego Żaden z układów
z trzech układów do ekranów nie pozwala
wygenerować
reprezentujących ich wygląd. ekranu o takiej
postaci.

1
A <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”3”
tools:context=”.MainActivity” >
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill”
android:layout_columnSpan=”3” Ten układ
android:text=”@string/hello” /> zawiera tylko
jeden przycisk
</GridLayout> zajmujący cały
obszar ekranu.

3 B <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”2”
tools:context=”.MainActivity” >
<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”fill” Ten przycisk
android:layout_columnSpan=”2” wypełnia prawie
android:text=”@string/hello” /> cały ekran,
zostawiając
<Button jedynie u dołu
android:layout_width=”wrap_content” nieco miejsca na
drugi przycisk.
android:layout_height=”wrap_content”
android:text=”@string/hi” />
</GridLayout>

200 Rozdział 5.
Interfejs użytkownika

4 C <GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:columnCount=”2”
tools:context=”.MainActivity” >

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
Choć przycisk
android:layout_row=”0” zajmuje dwie
android:layout_column=”0” kolumny, to
android:layout_columnSpan=”2” jednak nie
android:text=”@string/hello” /> kazaliśmy go
rozciągnąć
na całą tę
<Button szerokość.
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”1”
android:layout_column=”0”
android:text=”@string/hi” />
</GridLayout>

Układy i komponenty GUI mają wiele wspólnego


Być może zauważyłeś, że układy wszystkich typów mają kilka wspólnych atrybutów.
Niezależnie od typu używanego układu musimy określić jego szerokość i wysokość,
używając do tego celu atrybutów android:layout_width i android:layout_
height. Wymóg określania wymiarów nie ogranicza się jednak do układów
— dokładnie te same dwa atrybuty, android:layout_width i android:layout_
height, muszą być dodawane do wszystkich komponentów GUI.

Dzieje się tak dlatego, że wszystkie układy i komponenty GUI są klasami


dziedziczącymi po klasie View. Przyjrzyjmy się temu zagadnieniu nieco
dokładniej.

jesteś tutaj  201


Widoki

Komponenty GUI są typami pochodnymi klasy View


Przekonałeś się już, że komponenty GUI są typami widoków — okazuje się,
że wszystkie one są klasami pochodnymi klasy android.view.View. Oznacza to,
że wszystkie komponenty GUI, których używamy w interfejsie użytkownika, mają
wspólne atrybuty i zachowania. Wszystkie one mogą być wyświetlane na ekranie
i wszystkie pozwalają określać swoją szerokość i wysokość. Wszystkie komponenty Klasa android.view.View
GUI, których używamy do tworzenia interfejsów użytkownika, dziedziczą te jest klasą bazową wszystkich
podstawowe funkcjonalności i je rozszerzają. komponentów GUI używanych do
tworzenia interfejsów użytkownika
aplikacji.
android.view.View
... Listy typu spinner są
android.widget.TextView bardziej złożonymi typami
jest bezpośrednią klasą pochodnymi klasy View.
pochodną klasy View.

android.widget.TextView android.widget.Spinner
... ...

android.widget.EditText android.widget.Button
... ...

Układy dziedziczą po klasie ViewGroup


Nie tylko komponenty GUI są widokami — klasami pochodnymi klasy View.
Okazuje się, że także widoki są wyspecjalizowanymi typami widoków, Komponent GUI
a nazywamy je grupami widoków. Wszystkie widoki są klasami pochodnymi jest typem widoku,
klasy android.view.ViewGroup. Grupa widoków to wyspecjalizowany widok,
który może zawierać inne widoki. obiektem zajmującym
android.view.View
miejsce na ekranie.
...
Układ jest obiektem
Układy są typami określanym jako grupa
android.view.ViewGroup pochodnymi klasy
ViewGroup. ViewGrup widoków — to widok
... jest klasą pochodną
klasy View. specjalnego typu,
który może zawierać
android.widget. android.widget. android.widget.
GridLayout LinearLayout RelativeLayout
inne widoki.
... ... ...

202 Rozdział 5.
Interfejs użytkownika

Co nam daje bycie widokiem?


Obiekt View zajmuje prostokątny obszar na ekranie. Zawiera on
wszystkie funkcjonalności, których potrzebują widoki, by móc prowadzić
szczęśliwy i użyteczny żywot w Androidowie. Poniżej wymieniliśmy kilka
spośród tych funkcjonalności, które według nas są najważniejsze.

Odczytywanie i ustawianie właściwości Ten schemat przedstawia kilka


metod klasy View, których
możemy używać w kodzie
Za kulisami każdy widok jest obiektem Javy. Oznacza to, że w kodzie aktywności. Ponieważ zostały
aktywności możemy odczytywać i ustawiać wartości jego właściwości. one zdefiniowane w klasie
Na przykład możemy odczytać wartość wybraną z listy rozwijanej lub bazowej View, są dostępne
także we wszystkich widokach
zmienić tekst prezentowany w widoku tekstowym. Konkretne właściwości i grupach widoków.
i metody, jakie są dostępne, zależą od typu widoku.

Aby umożliwić wykonywanie takich operacji, z każdym widokiem można


android.view.View
skojarzyć identyfikator, dzięki któremu będzie można odwoływać się do
danego widoku w kodzie aktywności. getId()
getHeight()
Wielkość i położenie getWidth()
Możemy określać szerokość i wysokość widoku, tak by Android wiedział, setVisibility(int)
jaką ma on mieć wielkość. Oprócz tego możemy określić, czy widok ma
findViewById(int)
mieć jakieś wypełnienie, czy nie.
isClickable()
Po wyświetleniu widoku możemy odczytać jego położenie i faktyczne
wymiary na ekranie. isFocused()
requestFocus()
Obsługa miejsca wprowadzania ...
Android obsługuje przenoszenie miejsca wprowadzania w zależności od
czynności wykonywanych przez użytkownika. Obejmuje to odpowiednie
reakcje na widoki ukryte, usuwane lub wyświetlane.

Obsługa zdarzeń i obiekty nasłuchujące


Każdy z widoków może odpowiadać na zdarzenia. Możemy także
tworzyć obiekty nasłuchujące, które reagują na to, co się dzieje
z widokiem. Na przykład wszystkie widoki mogą reagować na utratę
miejsca wprowadzania, a przyciski (i ich klasy pochodne) mogą reagować
na kliknięcia.

Ponieważ grupa widoków sama jest typem widoku, wszystkie


układy i komponenty GUI dysponują tymi samymi, podstawowymi
możliwościami funkcjonalnymi.

jesteś tutaj  203


Hierarchia widoków

Układ jest tak naprawdę hierarchią widoków


Układy, które definiujemy w kodzie XML, dają nam w rzeczywistości hierarchiczne drzewo
widoków i grup widoków. Na przykład poniżej przedstawiliśmy układ względny zawierający
przycisk i pole tekstowe. Układ względny jest grupą widoków, natomiast przycisk i pole tekstowe
są widokami. Grupa widoków jest elementem nadrzędnym, widoki zaś są jego elementami
podrzędnymi:

<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
... >
Układ względny
<Button
W tym przykładzie ViewGroup
pominęliśmy duże android:id=”@+id/send”
fragmenty kodu
XML. Kluczowe ... />
znaczenie mają tu Przycisk
widoki umieszczone <EditText
w grupie widoków. View View
android:id=”@+id/message”
Pole tekstowe
... />
</RelativeLayout>

Podczas tworzenia aplikacji kod XML układu jest za kulisami przekształcany na obiekt
ViewGroup, który zawiera drzewo obiektów View. W powyższym przykładzie przycisk
zostanie przekształcony na obiekt Button, a pole tekstowe — na obiekt EditText.
Button i TextView to dwie klasy pochodne klasy View.

<Layout>
Układ względny

</Layout>
ViewGroup
layout.xml

Pole tekstowe
Przycisk

View View

To właśnie dzięki temu możemy operować na widokach w kodzie Javy.


Za kulisami wszystkie widoki są bowiem przekształcane do postaci
obiektów typu View.

204 Rozdział 5.
Interfejs użytkownika

Zabawy z widokami
Przyjrzyjmy się najpopularniejszym komponentom GUI. Znasz już kilka z nich, lecz
mimo to przedstawimy je jeszcze raz. Nie będziemy tu prezentować pełnego API
każdego z tych komponentów, a jedynie ich najważniejsze elementy, dzięki którym
będziesz mógł zacząć ich używać.

Widok tekstowy
android.view.View
Widok tekstowy, komponent
...
TextView, służy do wyświetlania
fragmentów tekstu na ekranie.

android.widget.TextView
Definiowanie w kodzie XML ...
Ten typ komponentów definiuje się w kodzie XML za pomocą elementu
<TextView>. Do określenia tekstu wyświetlanego w komponencie używany jest
atrybut android:text, przy czym sam tekst jest zazwyczaj definiowany jako zasób
łańcuchowy:

<TextView
android:id=”@+id/text_view”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/text” />

Interfejs API kontrolki TextView zawiera wiele atrybutów pozwalających


kontrolować jej wygląd, na przykład określać wielkości prezentowanego tekstu.
By zmienić wielkość tekstu wyświetlonego w widoku tekstowym, należy użyć
atrybutu android:textSize w sposób pokazany poniżej:

android:textSize=”14sp”

Wielkość tekstu określa się za pomocą pikseli niezależnych od skali (sp). Piksele
niezależne od skali uwzględniają, czy użytkownik chce używać dużych czcionek,
czy nie. Tekst o wielkości 14sp będzie większy na urządzeniach skonfigurowanych
tak, by używały dużych czcionek, niż na tych, na których mają być używane małe
czcionki.

Stosowanie w kodzie aktywności


Aby zmienić tekst wyświetlany w komponencie TextView, należy użyć fragmentu
kodu o następującej postaci:

TextView textView = (TextView) findViewById(R.id.text_view);


textView.setText(”Jakiś inny tekst”);

jesteś tutaj  205


Edytowalne pola tekstowe

Pola tekstowe android.view.View


...
Przypominają widoki tekstowe, lecz
pozwalają edytować wyświetlany w nich
tekst.
android.widget.TextView
Definiowanie w kodzie XML ...

Pola tekstowe definiuje się w kodzie XML za pomocą elementu <EditText>.


Atrybut android:hint pozwala określić tekst podpowiedzi, dzięki któremu
użytkownik będzie mógł się zorientować, co ma wpisać w danym polu. android.widget.EditText
<EditText ...
android:id=”@+id/edit_text”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:hint=”@string/edit_text” />

Atrybut android:inputType pozwala zdefiniować, jakiego typu dane użytkownik


ma wpisać w polu, dzięki czemu system będzie mu w stanie pomóc. Na przykład jeśli
użytkownik ma wpisać liczbę, to możemy użyć atrybutu o postaci:

android:inputType=”number”

a Android wyświetli klawiaturę numeryczną. Oto kilka naszych ulubionych wartości,


które można przypisać temu atrybutowi:
Kompletną listę dostępnych wartości tego atrybutu
można znaleźć w dokumentacji Androida dla
Wartość Działanie programistów dostępnej w internecie.

phone Wyświetla klawiaturę do wybierania numeru telefonu.


textPassword Wyświetla klawiaturę do wpisywania tekstu, przy czym zawartość pola jest maskowana.
textCapSentences Sprawia, że pierwsze słowo każdego zdania zaczyna się od wielkiej litery.
textAutoCorrect Automatycznie poprawia błędy we wpisywanym tekście.

Definiując pole, można zastosować w nim jednocześnie wiele typów danych — wystarczy rozdzielić je
znakiem pionowej kreski (|). Na przykład aby wpisywane zdania zaczynały się od wielkiej litery,
a tekst był automatycznie poprawiany, należałoby użyć atrybutu o następującej postaci:

android:inputType=”textCapSentences|textAutoCorrect”

Stosowanie w kodzie aktywności


Tekst wpisany w polu tekstowym można pobrać, używając następującego fragmentu kodu:

EditText editText = (EditText) findViewById(R.id.edit_text);


String text = editText.getText().toString();

206 Rozdział 5.
Interfejs użytkownika

Przycisk
Przyciski są zazwyczaj stosowane po to, by aplikacja mogła
wykonać jakąś operację.

android.view.View
Definiowanie w kodzie XML ...
W kodzie XML układów przyciski definiuje się za pomocą elementu <Button>.
Tekst wyświetlany na przycisku jest określany w atrybucie android:text:

<Button android.widget.TextView
android:id=”@+id/button”
...
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/button_text” />
android.widget.Button
...
Stosowanie w kodzie aktywności
Aby przycisk zareagował na kliknięcie, w kodzie XML układu należy dodać
atrybut android:onClick i podać w nim nazwę metody zdefiniowanej w kodzie
aktywności, którą należy wywołać:

android:onClick=”onButtonClicked”

Tę metodę należy zdefiniować w kodzie aktywności w następujący sposób:

/** Metoda wywoływana po kliknięciu przycisku */


public void onButtonClicked(View view) {
// Robimy coś w odpowiedzi na kliknięcie przycisku
}

onButtonClicked()
<Layout>

</Layout>
Aktywność
Układ

jesteś tutaj  207


Przycisk przełącznika

Przycisk przełącznika
Przycisk przełącznika może się znajdować w jednym z dwóch dostępnych stanów,
wybranym przez kliknięcie. android.view.View
...
Tak wygląda przycisk
przełącznika, kiedy Kiedy klikniesz
jest wyłączony. przycisk,
zostanie on
włączony.
android.widget.TextView

Definiowanie w kodzie XML ...

Przycisk przełącznika definiuje się w kodzie XML za pomocą elementu <ToggleButton>.


Dwa atrybuty, android:textOn i android:textOff, służą do określania tekstów
wyświetlanych na przycisku w zależności od jego stanu: android.widget.Button

<ToggleButton ...

android:id=”@+id/toggle_button”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” android.widget.
android:textOn=”@string/on” CompoundButton
android:textOff=”@string/off” /> ...

Stosowanie w kodzie aktywności android.widget.


Jeśli chcemy, by przycisk przełącznika odpowiadał na kliknięcia, do jego kodu XML ToggleButton
musimy dodać atrybut android:onClick. Wartością tego atrybutu musi być ...
nazwa funkcji zdefiniowanej w kodzie aktywności:
To jest dokładnie to samo co
android:onClick=”onToggleButtonClicked” wywoływanie metody w odpowiedzi
na kliknięcie normalnego przycisku.

Metoda zdefiniowana w kodzie aktywności może mieć następującą postać:


/** Metoda wywoływana po kliknięciu przycisku przełącznika */
public void onToggleClicked(View view) {
// Pobieramy stan przycisku przełącznika
boolean on = ((ToggleButton) view).isChecked();
if (on) {
// Przycisk włączony To wywołanie zwraca wartość true, jeśli
} else { przycisk jest włączony, i wartość false,
jeśli jest on wyłączony.
// Przycisk wyłączony
}
}

208 Rozdział 5.
Interfejs użytkownika

Przełącznik
Przełącznik to rodzaj małego suwaka, który działa bardzo podobnie
do przycisku omawianego wcześniej przełącznika.
Przełączniki 
wymagają API 
Obejrzyj to!
Tak wygląda A tak wygląda
przełącznik, kiedy przełącznik, poziomu 14  
jest wyłączony. gdy jest lub wyższego.
włączony.
Jeśli planujesz stosowanie
przełączników w swojej aplikacji,
upewnij się, czy będzie ona używać
Definiowanie w kodzie XML wersji SDK obsługującej API
Przełączniki definiuje się w kodzie XML za pomocą elementów <Switch>. poziomu 14 lub wyższego.
Korzystając z atrybutów android:textOn i android:textOff, można
określać teksty wyświetlane przez przełącznik w zależności od jego stanu.
android.view.View
<Switch ...
android:id=”@+id/switch_view”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android.widget.TextView
android:textOn=”@string/on”
android:textOff=”@string/off” /> ...

Stosowanie w kodzie aktywności android.widget.Button


Jeśli chcemy, by przełącznik odpowiadał na kliknięcia, do jego kodu XML ...
musimy dodać atrybut android:onClick. Wartością tego atrybutu musi być
nazwa funkcji zdefiniowanej w kodzie aktywności:

android:onClick=”onSwitchClicked” android.widget.
CompoundButton
Metoda zdefiniowana w kodzie aktywności może mieć następującą postać: ...
/** Metoda wywoływana po kliknięciu przełącznika */
public void onToggleClicked(View view) {
// Czy przełącznik jest włączony? android.widget.Switch
boolean on = ((Switch) view).isChecked(); ...

if (on) { Ten kod jest bardzo podobny do


metody obsługującej kliknięcia
// Przełącznik włączony przycisku przełącznika.
} else {
// Przełącznik wyłączony
}
}

jesteś tutaj  209


Pola wyboru

Pola wyboru
Pola wyboru umożliwiają prezentowanie użytkownikom grupy opcji. android.view.View
Użytkownicy mogą następnie wybierać z tej grupy dowolne opcje.
...
Każde pole wyboru można zaznaczyć niezależnie od pozostałych.

Oto dwa pola wyboru. Użytkownik


może zaznaczyć mleko, cukier android.widget.TextView
lub oba te pola albo może nie ...
zaznaczać żadnego z nich.

android.widget.Button
...

Definiowanie w kodzie XML android.widget.


Pola wyboru definiuje się w kodzie XML za pomocą elementów <CheckBox>. Tekst CompoundButton
wyświetlany obok danego pola można określić, używając atrybutu android:text: ...

<CheckBox android:id=”@+id/checkbox_milk”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” android.widget.CheckBox
android:text=”@string/milk” /> ...

<CheckBox android:id=”@+id/checkbox_sugar”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/sugar” />

Stosowanie w kodzie aktywności


Do określenia, czy konkretne pole wyboru jest zaznaczone, czy nie, służy metoda
isChecked(). Jeśli pole jest zaznaczone, jej wywołanie zwróci wartość true:

CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox_milk);


boolean checked = checkbox.isChecked();
if (checked) {
// Pole jest zaznaczone — reagujemy odpowiednio
}

210 Rozdział 5.
Interfejs użytkownika

Pola wyboru (ciąg dalszy)


Podobnie jak w przypadku przycisków, istnieje także możliwość reagowania na kliknięcie
pola wyboru. W tym celu należy dodać do kodu XML układu atrybut android:onClick
i przypisać mu nazwę metody zdefiniowanej w kodzie aktywności, którą należy wywołać:

<CheckBox android:id=”@+id/checkbox_milk”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/milk”
android:onClick=”onCheckboxClicked”/>
W tym przypadku metoda onCheckboxClicked()
<CheckBox android:id=”@+id/checkbox_sugar” zostanie wywołana niezależnie od tego,
które pole wyboru zostało kliknięte.
android:layout_width=”wrap_content” W razie potrzeby w każdym polu wyboru
można zastosować inną metodę.
android:layout_height=”wrap_content”
android:text=”@string/sugar”
android:onClick=”onCheckboxClicked”/>

Metoda obsługująca kliknięcia pól tekstowych mogłaby zostać zdefiniowana


w następujący sposób:

public void onCheckboxClicked(View view) {


// Czy kliknięte pole wyboru jest zaznaczone?
boolean checked = ((CheckBox) view).isChecked();

// Określamy, które pole zostało kliknięte


switch(view.getId()) {
case R.id.checkbox_milk:
if (checked)
// Kawa z mlekiem
else
// Czarna jak niebo w bezksiężycową noc
break;
case R.id.checkbox_sugar:
if (checked)
// Słodziutka
else
// Lepiej niech będzie gorzka
break;
}
}

jesteś tutaj  211


Przyciski opcji

Przyciski opcji
Także ten rodzaj przycisków pozwala wyświetlać grupy opcji. Jednak w ich
przypadku użytkownik może w danej chwili wybrać tylko jedną opcję z grupy. android.view.View
...
Zastosuj przyciski
opcji, aby ograniczyć
możliwości użytkownika
do wyboru tylko jednej
spośród dostępnych opcji.
android.widget.TextView
Definiowanie w kodzie XML ...

Definiowanie przycisków opcji należy zacząć od dodania specjalnego typu grupy widoków:
<RadioGroup>. Wewnątrz elementu <RadioGroup> można następnie definiować
poszczególne przyciski opcji, używając w tym celu elementów <RadioButton>: android.widget.Button

<RadioGroup android:id=”@+id/radio_group” ...


android:layout_width=”match_parent”
Możemy wybrać, czy
android:layout_height=”wrap_content” przyciski opcji mają być
wyświetlane w układzie
android:orientation=”vertical”> pionowym czy poziomym. android.widget.
CompoundButton
<RadioButton android:id=”@+id/radio_cavemen” ...
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/cavemen” /> android.widget.
RadioButton
<RadioButton android:id=”@+id/radio_astronauts” ...
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/astronauts” />
</RadioGroup>

Stosowanie w kodzie aktywności


Identyfikator zaznaczonego przycisku opcji można określić za pomocą metody
getCheckedRadioButtonId():

RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radioGroup);


int id = radioGroup.getCheckedRadioButtonId();
if (id == -1){
// Nie wybrano żadnej opcji
}
else{
RadioButton radioButton = (RadioButton)findViewById(id);
}

212 Rozdział 5.
Interfejs użytkownika

Przyciski opcji (ciąg dalszy)


Aby odpowiadać na kliknięcia przycisku opcji, należy dodać do kodu XML układu Grupa widoków
atrybut android:onClick i podać w nim nazwę metody, którą należy wywołać,
zdefiniowaną w kodzie aktywności: zawierająca przyciski
<RadioGroup android:id=”@+id/radio_group” opcji jest klasą
android:layout_width=”match_parent”
android:layout_height=”wrap_content” pochodną klasy
android:orientation=”vertical”> LinearLayout.
<RadioButton android:id=”@+id/radio_cavemen” Można w niej
android:layout_width=”wrap_content” zatem używać tych
android:layout_height=”wrap_content”
android:text=”@string/cavemen” samych atrybutów,
android:onClick=”onRadioButtonClicked” /> które są dostępne
<RadioButton android:id=”@+id/radio_astronauts” w układach liniowych.
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/astronauts”
android:onClick=”onRadioButtonClicked” />
</RadioGroup>

Metodę obsługującą kliknięcia przycisku można zdefiniować w następujący sposób:

public void onRadioButtonClicked(View view) {


RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radioGroup);
int id = radioGroup.getCheckedRadioButtonId();
switch(id) {
case R.id.radio_cavemen:
// Wygrali jaskiniowcy
break;
case R.id.radio_astronauts:
// Wygrali astronauci
break;
}
}

jesteś tutaj  213


Listy rozwijane

Lista rozwijana
Jak już wiesz, komponent Spinner tworzy rozwijaną listę wartości, spośród których android.view.View
użytkownik może wybrać jedną.
...

Listy rozwijanej
używaliśmy android.view.ViewGroup
w rozdziale 2. ...

android.widget.
AdapterView
Definiowanie w kodzie XML ...

Listy rozwijane tworzy się za pomocą elementu <Spinner>. Statyczną tablicę


danych, używanych jako opcje listy, określa się przy użyciu android:entries,
w którym należy podać nazwę tablicy łańcuchów znaków. android.widget.
AbsSpinner
<Spinner Są także inne sposoby
android:id=”@+id/spinner” określania opcji list — poznasz ...
je w dalszej części książki.
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:entries=”@array/spinner_values” /> android.widget.Spinner
...
Tablicę łańcuchów znaków można zdefiniować w pliku strings.xml
w następujący sposób:

<string-array name=”spinner_values”>
<item>jasne</item>
<item>bursztynowe</item>
<item>brązowe</item>
<item>ciemne</item>
</string-array>

Stosowanie w kodzie aktywności


Wartość opcji aktualnie wybranej na liście można pobrać za pomocą metody
getSelectedItem(), a następnie skonwertować do łańcucha znaków
w następujący sposób:

Spinner spinner = (Spinner) findViewById(R.id.spinner);


String string = String.valueOf(spinner.getSelectedItem());

214 Rozdział 5.
Interfejs użytkownika

Widoki obrazów
Jak sama nazwa wskazuje, ten typ widoków służy do wyświetlania obrazów. android.view.View
...

Widok obrazu przedstawia android.widget.ImageView


wskazany obraz. ...

ImageView jest bezpośrednią


klasą pochodną klasy View.
Dodawanie obrazu do projektu
W pierwszej kolejności musimy dodać plik obrazka do projektu jako zasób graficzny.
Jeśli wyświetlimy katalog app/src/main/res projektu, to przekonamy się, że wewnątrz
niego znajduje się katalog o nazwie drawable. Stanowi on domyślne miejsce, w którym
są umieszczane zasoby graficzne. Aby umieścić plik obrazu w tym katalogu, wystarczy
go do niego przeciągnąć i upuścić.

Możemy także używać różnych plików graficznych zależnie od gęstości ekranu


ży
urządzenia. Oznacza to, że możemy wyświetlać obrazy o większej rozdzielczości na Aby utworzyć nowy katalog, nale
wyświet lić stru ktur ę kata logó w
urządzeniach, których ekrany mają większą gęstość, i obrazy o mniejszej rozdzielczości
w widoku Project, zaznaczyć
na urządzeniach z ekranami o mniejszej gęstości. W tym celu w katalogu app/src/main/ katalog res, a następnie wybrać
res projektu należy utworzyć kolejne katalogi drawable dla poszczególnych gęstości opcję File/New…/Android resource
ekranów. Nazwy tych katalogów odpowiadają gęstościom ekranów: directory.

android-ldpi Ekrany o niskiej gęstości, około 120 dpi. W zależności od tego, jaka wersja
Android Studio jest używana, IDE
android-mdpi Ekrany o średniej gęstości, około 160 dpi. może automatycznie utworzyć dla
nas niektóre z tych katalogów.
android-hdpi Ekrany o wysokiej gęstości, około 240 dpi.
android-xhdpi Ekrany o bardzo wysokiej gęstości, około 320 dpi.
android-xxhdpi Ekrany o bardzo, bardzo wysokiej gęstości, około 480 dpi.
android-xxxhdpi Ekrany o ultrawysokiej gęstości, około 640 dpi.

Następnie wystarczy umieścić obrazy o różnej rozdzielczości w odpowiednich katalogach


drawable*, upewniając się przy tym, że obrazy będą miały takie same nazwy. Android
określi, którego z obrazów użyć podczas działania aplikacji, na podstawie gęstości ekranu
używanego urządzenia. Na przykład jeśli urządzenie jest wyposażone w ekran o bardzo
wysokiej gęstości, system użyje obrazów z katalogu drawable-xhdpi.

Jeżeli plik obrazu zostanie dodany tylko do jednego katalogu, to Android będzie go
używał zawsze — na wszystkich urządzeniach. Najczęściej do tego celu używany jest
katalog drawable.

jesteś tutaj  215


Widoki obrazów

Obrazy: kod XML układu


Widok obrazu dodaje się do kodu XML układu, używając elementu <ImageView>.
Do określenia obrazu, który należy w danym widoku wyświetlić, służy atrybut
android:src. Można także zastosować atrybut android:contentDescription, aby
dodać łańcuch znaków opisujący obraz i poprawić w ten sposób dostępność aplikacji:

<ImageView
android:layout_width=”200dp”
android:layout_height=”100dp”
android:src=”@drawable/starbuzz_logo”
android:contentDescription=”@string/starbuzz_logo” />

Wartość atrybutu android:src przyjmuje następującą postać: ”@drawable/nazwa_


obrazu”, gdzie nazwa_obrazu to nazwa pliku obrazu bez rozszerzenia. Zasoby
graficzne są poprzedzane prefiksem @drawable. Prefiks ten informuje system,
że zasób jest umieszczony w jednym lub kilku katalogach drawable.

Stosowanie w kodzie aktywności


Zarówno źródło obrazu, jak i jego opis można ustawić programowo w kodzie
aktywności, używając w tym celu odpowiednio metod setImageResource()
i setContentDescription():

ImageView photo = (ImageView)findViewById(R.id.photo);


int image = R.drawable.starbuzz_logo;
String description = ”To jest logo.”;
photo.setImageResource(image);
photo.setContentDescription(description);

Ten fragment kodu odnajduje zasób graficzny o nazwie starbuzz_logo


przechowywany w którymś z katalogów drawable, a następnie używa go jako źródła
dla widoku o identyfikatorze photo. Aby odwołać się do zasobu graficznego w kodzie
aktywności, należy użyć zapisu R.drawable.nazwa_obrazu, gdzie nazwa_obrazu to
nazwa pliku obrazu (bez rozszerzenia).

216 Rozdział 5.
Interfejs użytkownika

Dodawanie obrazów do przycisków


Oprócz wyświetlania obrazów w widokach typu ImageView można je także
wyświetlać na przyciskach.

Wyświetlanie na przycisku tekstu i obrazu


Aby wyświetlić na przycisku tekst, a po jego prawej stronie obraz, należy
skorzystać z atrybutu android:drawableRight, podając w nim nazwę obrazu:

<Button
android:layout_width=”wrap_content” ej
Wyświetla logo Androida po praw
android:layout_height=”wrap_content” stronie tekstu na przycisku.
android:drawableRight=”@drawable/android”
android:text=”@string/click_me” />

Aby wyświetlić obraz po lewej stronie tekstu, należy użyć atrybutu


android:drawableLeft:

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:drawableLeft=”@drawable/android”
android:text=”@string/click_me” />

Używając atrybutu android:drawableBottom, można wyświetlić obraz


poniżej tekstu na przycisku:

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:drawableBottom=”@drawable/android”
android:text=”@string/click_me” />

I w końcu atrybut android:drawableTop pozwala wyświetlić obraz


nad tekstem na przycisku:

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:drawableTop=”@drawable/android”
android:text=”@string/click_me” />

jesteś tutaj  217


Przyciski z obrazami

Przyciski z obrazami
Przyciski tego typu przypominają normalne przyciski, z tym,
że zamiast tekstu są na nich wyświetlane wyłącznie obrazy.

Definiowanie w kodzie XML android.view.View


Przycisk z obrazem tworzy się, dodając do kodu XML układu element ...
<ImageButton>. Źródło obrazu wyświetlanego na przycisku określa się
za pomocą atrybutu android:src:

<ImageButton android.widget.ImageView
android:id=”@+id/button” ...
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:src=”@drawable/button_icon />
android.widget.
ImageButton
Stosowanie w kodzie aktywności
...
Aby przycisk ImageButton odpowiadał na kliknięcia, do jego kodu XML
należy dodać atrybut android:onClick i podać w nim nazwę metody
zdefiniowanej w kodzie aktywności: Klasa ImageButton
rozszerza klasę ImagView,
android:onClick=”onButtonClicked” a nie Button. Czy to Cię
zaskoczyło?
Tę metodę można zdefiniować w kodzie aktywności w następujący sposób:

/** Metoda wywoływana po kliknięciu przycisku */


public void onButtonClicked(View view) {
// Robimy coś w odpowiedzi na kliknięcie przycisku
}

onButtonClicked()
<Layout>

</Layout>
Aktywność
Układ

218 Rozdział 5.
Interfejs użytkownika

Widoki przewijane
Jeśli do układu dodamy bardzo dużo widoków, to na urządzeniach
z niewielkimi ekranami możemy mieć pewien problem — większość
układów nie udostępnia pasków przewijania pozwalających na przewijanie
ich zawartości. Na przykład jeśli dodamy do układu liniowego kilka dużych
przycisków, to zapewne nie będziemy w stanie zobaczyć ich wszystkich.

Układ liniowy nie udostępnia


pasków przewijania. Kiedy
spróbowaliśmy wyświetlić
w takim układzie siedem
przycisków na naszym
urządzeniu, nie mogliśmy
zobaczyć ich wszystkich.

Aby dodać pionowy pasek przewijania do układu, należy umieścić ten


układ wewnątrz elementu <ScrollView>, jak w poniższym przykładzie:

<ScrollView xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent” Przenieś te atrybuty z początko
wego układu
do elementu <ScrollView>, gdyż
tools:context=”.MainActivity” > to
obecnie głównym elementem pliku on jest
.

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:orientation=”vertical” >
Umieszczenie układu wewnątrz
... elementu <ScrollView>
</LinearLayout> spowodowało dodanie fajnego,
pionowego paska przewijania.
</ScrollView> Teraz użytkownik może już
wyświetlić wszystkie widoki.

Aby dodać do układu poziomy pasek przewijania, należy cały układ


umieścić wewnątrz elementu <HorizontalScrollView>.
jesteś tutaj  219
Wyświetlanie komunikatów

Krótkie komunikaty
Jest jeszcze jeden widżet, który chcielibyśmy przedstawić w tym rozdziale, java.lang.Object
a służy on do wyświetlania tak zwanych tostów (ang. toast). Tosty to zwyczajne,
...
krótkie komunikaty tekstowe wyświetlane na ekranie.

Takie komunikaty mają wyłącznie charakter informacyjny, a użytkownik


nie może prowadzić z nimi żadnych interakcji. Podczas prezentowania tych
komunikatów aktywność jest cały czas widoczna i zachowuje pełne możliwości android.widget.Toast
interakcji. Komunikaty są ukrywane automatycznie po określonym czasie. ...

Stosowanie w kodzie aktywności Jak widać, klasa Toast nie


Komunikaty są wyświetlane w kodzie aktywności. Nie można zdefiniować ich dziedziczy po klasie View,
więc nie jest widokiem. Ale
w układzie. ponieważ zapewnia nam ona
bardzo przydatną możliwość
Do ich tworzenia używana jest metoda Toast.makeText(), do której wyświetlania użytkownikom
przekazywane są trzy parametry: obiekt Context (zazwyczaj jest to referencja krótkich komunikatów,
przemyciliśmy ją do tego
this odwołująca się do bieżącej aktywności), obiekt CharSequence rozdziału.
zawierający treść wyświetlanego komunikatu oraz liczba typu int określająca
czas prezentowania komunikatu. Po utworzeniu obiektu Toast możemy
wyświetlić komunikat na ekranie, używając metody show().

Poniższy fragment kodu pokazuje, jak można utworzyć komunikat i wyświetlić


go na krótki okres czasu:

CharSequence text = ”Uwielbiam tosty!”;


int duration = Toast.LENGTH_SHORT;

Toast toast = Toast.makeText(this, text, duration);


toast.show();

Domyślnie komunikaty są
wyświetlane u dołu ekranu.

220 Rozdział 5.
Interfejs użytkownika

To doskonały moment, żeby przećwiczyć stosowanie widoków poznanych w tym rozdziale.


Utwórz zatem układ, który pozwoli wyświetlić ekran o następującej postaci:

Ćwiczenie
Pewnie nie będzie Ci się chciało
pisać kodu układu tutaj, ale może io?
Stud
poeksperymentujesz w Android

jesteś tutaj  221


Rozwiązanie

Oto jeden z wielu sposobów, na które można utworzyć przedstawiony układ.


Nie przejmuj się, jeśli Twój kod wygląda inaczej, gdyż to ćwiczenie ma wiele rozwiązań.

Ćwiczenie
Rozwiązanie

<GridLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
My zastosowaliśmy układ siatki,
,
android:layout_width=”match_parent” ale nic nie stoi na przeszkodzie
by użyć układu względnego.
android:layout_height=”match_parent”
android:paddingBottom=”16dp”
android:paddingLeft=”16dp”
android:paddingRight=”16dp”
android:paddingTop=”16dp”
android:columnCount=”2” Nasz układ ma dwie kolumny.

tools:context=”.MainActivity” >

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
ujący obie kolumny
android:layout_row=”0” Na górze wyświetlamy tekst zajm
pierwsze go wier sza ukła du.
android:layout_column=”0”
android:layout_columnSpan=”2”
android:text=”@string/message” /> Wszystkie widoki wymagają
łańcuchów znaków, dodanych
do pliku zasobów strings.xml.

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”1” W pierwszej kolumnie drugiego
wiersza umieściliśmy etykietę
android:layout_column=”0” Temperatura.
android:text=”@string/temp” />

222 Rozdział 5.
Interfejs użytkownika

dać użytkownikowi
Zastosowaliśmy przycisk przełącznika, by
gorąca czy zimna;
możliwość określenia, czy herbata ma być
<ToggleButton umieś ciliśm y go w wiers zu 1 i kolum nie 1.

android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”1”
android:layout_column=”1”
android:textOn=”@string/hot”
android:textOff=”@string/cold” />

<CheckBox android:id=”@+id/checkbox_milk”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”2”
android:layout_column=”0”
android:text=”@string/milk” />

<CheckBox android:id=”@+id/checkbox_sugar”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_row=”3”
android:layout_column=”0”
android:text=”@string/sugar” />

<CheckBox android:id=”@+id/checkbox_lemon” Do wyświetlenia poszczególnych


opcji (Mleko, Cukier i Cytryna)
android:layout_width=”wrap_content” zastosowaliśmy pola wyboru. Każe
z nich umieściliśmy w osobnym
android:layout_height=”wrap_content” wierszu układu.
android:layout_row=”4”
android:layout_column=”0”
android:text=”@string/lemon” />
</GridLayout>

jesteś tutaj  223


Jeszcze jedno rozwiązanie

Magnesiki układowe. Rozwiązanie


Napisaliśmy kod, który służy do wyśrodkowania przycisku Wyślij w trzecim
wierszu układu siatki, ale nagły podmuch strącił niektóre magnesiki na ziemię.
Czy możesz odtworzyć kod, używając przedstawionych poniżej magnesików?

<GridLayout...>
<TextView... />
<EditText.../>
<EditText.../>

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”

android:layout_row=...............

android:layout_column=...............

android:layout_gravity=...............

android:layout_columnSpan=...............

224 Rozdział 5.
e
Interfejs użytkownika

Twój przybornik do Androida

Rozdział 5.
Opanowałeś już rozdział 5. i dodałeś 
do swojego przybornika z narzędziami 
znajomość widoków i grup widoków.

    CELNE SPOSTRZEŻENIA

 Wszystkie komponenty GUI są rodzajami widoków.  Za pomocą atrybutu android:layout_gravity


Są to klasy dziedziczące po kasie android.view.View. można określać, w którym miejscu dostępnego obszaru
ma zostać wyświetlony dany widok.
 Wszystkie układy są klasami dziedziczącymi po
android.view.ViewGroup. Grupa widoków to widok  Atrybut android:gravity określa, w którym miejscu
specjalnego typu, który może zawierać inne widoki. widoku ma zostać wyświetlona jego zawartość.
 Plik XML układu jest konwertowany do postaci obiektu  Element <ToggleButton> definiuje przyciski
ViewGroup zawierającego hierarchiczne drzewo przełączników, które udostępniają dwa stany wybierane
widoków. kliknięciem.
 Układ względy określa położenie widoków względem  Element <Switch> definiuje kontrolkę przełącznika,
innych widoków lub względem samego układu która działa analogicznie do przycisku przełącznika.
nadrzędnego. Wymaga ona użycia API poziomu 14 lub wyższego.
 Układ liniowy wyświetla widoki w pionie lub w poziomie.  Element <Checkbox> definiuje pole wyboru.
Orientację układu można określić za pomocą atrybutu  Aby zdefiniować grupę przycisków opcji, w pierwszej
android:orientation.
kolejności trzeba zdefiniować ich grupę, używając
 Układ siatki dzieli ekran na siatkę komórek i pozwala elementu <RadioGroup>. Wewnątrz tej grupy można
określać, w której (lub w których) z nich mają dodawać poszczególne przyciski opcji, definiowane
zostać wyświetlone poszczególne widoki. Liczbę za pomocą elementów <RadioButton>.
kolumn układu można określić za pomocą atrybutu  Aby wyświetlić obraz, należy użyć elementu
android:columnCount. Do określania komórki,
<ImageView>.
w której ma zostać wyświetlony dany widok służą
atrybuty android:layout_row i android:layout_  Element <ImageButton> definiuje przycisk,
column. Za pomocą atrybutu android:layout_ który zamiast tekstu przedstawia obraz.
columnSpan można określać, ile kolumn ma zajmować  Paski przewijania można dodawać do układów
dany widok.
za pomocą elementów <ScrollView>
 Atrybuty android:padding* pozwalają określać, i <HorizontalScrollView>.
jak duże wypełnienie pozostanie przy poszczególnych  Klasa Toast pozwala wyświetlać komunikaty tekstowe.
krawędziach widoku.
 Aby wybrany element zajął więcej miejsca w układzie,
można do niego dodać atrybut android:layout_
weight. Atrybutu tego można używać wyłącznie
w układach liniowych.

jesteś tutaj  225


226 Rozdział 5.
6. Widoki list i adaptery

Zorganizuj się
Rany! Mam tyle pomysłów…
Tylko czy uda mi się je zmienić
w najpopularniejszą
aplikację roku?

Chcesz wiedzieć, jaki jest najlepszy sposób na określenie struktury aplikacji?


Znasz już podstawowe elementy konstrukcyjne używane do tworzenia aplikacji, więc teraz
nadszedł czas, żebyś się lepiej zorganizował. W tym rozdziale pokażemy Ci, jak możesz
przekształcić zbiór pomysłów w niesamowitą aplikację. Zobaczysz, że listy danych mogą
stać się kluczowym elementem projektu aplikacji i że łączenie ich może prowadzić do powstania
aplikacji łatwej w użyciu i zapewniającej ogromne możliwości. Przy okazji zapoznasz się
z obiektami nasłuchującymi i adapterami, dzięki którym Twoja aplikacja stanie się bardziej
dynamiczna.

to jest nowy rozdział  227


Pomysły

Każda aplikacja zaczyna się od pomysłu


Kiedy po raz pierwszy zaświta Ci myśl o napisaniu aplikacji, będziesz
zapewne mieć wiele pomysłów dotyczących tego, co powinna zawierać.

Na przykład szefowie kafeterii Coffeina chcieliby mieć nową aplikację,


by przyciągnąć liczniejszą klientelę do swoich lokali. Poniżej przedstawiliśmy
kilka ich pomysłów na zawartość aplikacji:

łowe
Szczegó żdym
cje o ka
informa
napoju
Lista wszystkich
naszych lokali Adresy i godz
iny
otwarcia
Menu zawierając wszystkich na
e szych
wszystko, co lokali
można u nas zjeś
ć
Lista oferowanych
napojów
Ekran początkowy
e z listą opcji
Szczegółow
je o każdej
informac
nu
pozycji me

To wszystko są pomysły, które użytkownicy aplikacji na pewno


uznają za przydatne. Ale w jaki sposób możemy je wszystkie zebrać
i przekształcić w intuicyjną, dobrze zorganizowaną aplikację?

228 Rozdział 6.
Widoki list i adaptery

Skategoryzuj swoje pomysły — aktywności:


poziom główny, kategoria i szczegóły/edycja
Przydatnym sposobem, który pomoże zaprowadzić porządek w naszych
pomysłach, będzie podzielenie ich na trzy różne typy aktywności: aktywności
poziomu głównego, aktywności kategorii oraz aktywności szczegółów/edycji.

Aktywności poziomu głównego Ekran


Aktywność poziomu głównego zawiera te rzeczy, które początkowy
są najważniejsze dla użytkownika, i pozwala mu na łatwe z listą opcji
przechodzenie do nich. W większości przypadków pierwszą
aktywnością, którą użytkownik zobaczy po uruchomieniu
aplikacji, będzie właśnie aktywność poziomu głównego.

Lista wszystkich
Aktywności kategorii Menu zawierające naszych lokali
Aktywności kategorii prezentują dane należące do wszystko, co
konkretnej kategorii i często przybierają postać list. można u nas Lista
Aktywności tego typu zazwyczaj zapewniają użytkownikom zjeść oferowanych
możliwość przejścia do kolejnej aktywności — szczegółów/
napojów
edycji. Przykładem aktywności kategorii może być lista
wszystkich napojów oferowanych przez kafeterię Coffeina.

Aktywności szczegółów/edycji łowe


Szczegó Adresy i godz
iny
cje
Aktywności typu szczegółów/edycji wyświetlają informa otwarcia
y m Szczegółowe
szczegółowe informacje o konkretnym rekordzie i pozwalają o każd wszystkich
użytkownikom dodawać nowe rekordy. Przykładem napoju informacje naszych lokali.
zycji
aktywności tego typu może być aktywność wyświetlająca o każdej po
szczegółowe informacje o konkretnym napoju. menu

Określiwszy, do której z tych kategorii należą poszczególne


aktywności, możemy ich użyć do utworzenia hierarchii pokazującej,
jak użytkownik będzie nawigował po aplikacji i jej poszczególnych
aktywnościach.

Zastanów się, jaką aplikację chcesz napisać. Jakie aktywności powinna zawierać?
Podziel je na aktywności poziomu głównego, kategorii oraz szczegółów/edycji.
Ćwiczenie

jesteś tutaj  229


Zorganizuj swoje pomysły

Nawigowanie po aktywnościach
Podczas organizowania pomysłów i klasyfikowaniu ich na aktywności
poziomu głównego, kategorii oraz szczegółów/edycji możemy na podstawie
tej klasyfikacji od razu określić, w jaki sposób będzie można poruszać się
po projektowanej aplikacji. Ogólnie rzecz biorąc, chcemy, by użytkownik
przechodził od aktywności poziomu głównego przez aktywności kategorii
do aktywności szczegółów/edycji.

Aktywności poziomu głównego Ekran


początkowy
są na samej górze z listą opcji
To właśnie te aktywności użytkownik
zobaczy jako pierwsze.

Aktywności kategorii
Lista Menu zawierające Lista wszystkich
znajdują się pomiędzy oferowanych wszystko, co naszych lokali
aktywnościami poziomu napojów można u nas zjeść
głównego i aktywnościami
szczegółów/edycji
Użytkownicy będą przechodzili od
aktywności poziomu głównego do
aktywności kategorii. W złożonych
aplikacjach może występować kilka
poziomów kategorii i podkategorii.

Aktywności Szczegółowe Szczegółowe Adresy i godziny


szczegółów/edycji informacje informacje otwarcia
o każdym napoju o każdej pozycji wszystkich
Tworzą one dolną warstwę
menu naszych lokali
hierarchii aktywności. Użytkownicy
będą do nich docierali
z aktywności kategorii.

W ramach przykładu załóżmy, że użytkownik chce wyświetlić szczegółowe


informacje o jednym z napojów dostępnych w kafeteriach Coffeina. W tym celu
uruchamia aplikację, która najpierw wyświetla aktywność poziomu głównego —
ekran powitalny z listą opcji. Następnie użytkownik naciska opcję wyświetlenia listy
napojów. W końcu, aby wyświetlić szczegółowe informacje o konkretnym napoju,
użytkownik musi odnaleźć go na liście i kliknąć.
230 Rozdział 6.
Widoki list i adaptery

Użyj ListViews do nawigowania po danych


W przypadku zastosowania takiej struktury aplikacji będziemy potrzebowali sposobu
pozwalającego na przechodzenie do poszczególnych aktywności. Popularnym rozwiązanie
stosowanym w takich sytuacjach jest widok listy. Widok listy pozwala wyświetlić listę
danych, których następnie można użyć do nawigowania po aplikacji.

A teraz mały przykład. Na poprzedniej stronie napisaliśmy, że w skład aplikacji


dla kafeterii Coffeina wchodzi aktywność kategorii prezentująca listę napojów.
Poniżej pokazaliśmy, jak ta aktywność może wyglądać.

To jest widok ListView


zawierający listę napojów.

Aktywność wykorzystuje widok listy do wyświetlania wszystkich napojów oferowanych


w kafeteriach Coffeina. Aby przejść do konkretnego napoju, użytkownik musi kliknąć
odpowiednią opcję listy, a w efekcie zostaną wyświetlone szczegółowe informacje na
jego temat.

Jeśli klikniesz opcję


Latte na liście ListView,
to aplikacja wyświetli
szczegółowe informacje
na temat latte.

Całą pozostałą część rozdziału poświęcimy na pokazanie, jak


używać widoków list, by zaimplementować aplikację działającą
w opisany powyżej sposób.
jesteś tutaj  231
Witamy w kafeterii Coffeina
część
Napiszemy aplikacjęi dla kafeterii Coffeina
Zamiast tworzyć wszystkie aktywności kategorii i szczegółów/edycji składające
się na całą aplikację dla kafeterii Coffeina, skoncentrujemy się wyłącznie na
napojach. Napiszemy aktywność poziomu głównego wyświetlaną bezpośrednio
po uruchomieniu aplikacji, aktywność kategorii prezentującą listę napojów
oraz aktywność szczegółów/edycji przedstawiającą szczegółowe informacje
o wybranym napoju.

Aktywność poziomu głównego


Kiedy użytkownik uruchamia aplikację, zostanie
wyświetlona aktywność poziomu głównego
stanowiąca punkt wejścia do całej aplikacji.
Aktywność ta będzie zawierała logo kafeterii
Coffeina i listę elementów nawigacyjnych:
Napoje, Przekąski oraz Kafeterie.
Logo kafeterii Coffeina
Gdy użytkownik kliknie któryś z elementów tej i lista opcji. My zajmiemy
się zaimplementowaniem
listy, aplikacja odczyta klikniętą opcję i na jej opcji Napoje.
podstawie uruchomi inną, wybraną aktywność.
Na przykład jeśli użytkownik klinie element
Napoje, to zostanie uruchomiona aktywność
prezentująca listę napojów.

Aktywność kategorii z listą napojów


Ta aktywność będzie uruchamiana, kiedy
użytkownik kliknie opcję Napoje na liście
nawigacyjnej prezentowanej przez aktywność
poziomu głównego. Jej zadaniem jest
wyświetlanie listy wszystkich napojów, które są
dostępne w kafeteriach Coffeina. Użytkownik Wyświetlimy tylko trzy
może kliknąć jeden z napojów, aby wyświetlić napoje, choć w ofercie
kafeterii Coffeina jest ich
szczegółowe informacje na jego temat. na pewno znacznie więcej.

232 Rozdział 6.
Widoki list i adaptery

Aktywność szczegółów napoju


Aktywność napoju jest uruchamiana, kiedy użytkownik
kliknie jedną z opcji listy napojów wyświetlanej przez
aktywność kategorii.

Ta aktywność odpowiada za wyświetlenie szczegółowych


informacji o napoju wybranym przez użytkownika.
Do tych informacji będzie należało zdjęcie napoju,
jego nazwa oraz opis.

Ta aktywność wyświetla
szczegółowe informacje
dotyczące konkretnego
napoju.

Jak użytkownik porusza się po aplikacji?


Użytkownik zaczyna nawigowanie po aplikacji od aktywności poziomu
głównego, a następnie, klikając opcję Napoje, przechodzi do aktywności
kategorii. Gdy kliknie wybrany napój, może przejść do kolejnej
aktywności, prezentującej szczegółowe informacje o napoju.

Kiedy użytkownik
kliknie wybrany
napój, zostaną
Użytkownik klika wyświetlone
element Napoje, informacje o tym
co powoduje napoju.
wyświetlenie listy
napojów.

jesteś tutaj  233


Struktura aplikacji

Struktura aplikacji dla kafeterii Coffeina


Nasza aplikacja składa się z trzech aktywności. Aktywność głównego poziomu nosi
nazwę TopLevelActivity i umożliwia użytkownikowi poruszanie się po aplikacji.
Aktywność DrinkCategoryActivity to aktywność kategorii, która wyświetla
listę napojów. A ostatnia aktywność, DrinkActivity, służy do wyświetlania
szczegółowych informacji o wybranym napój.

Na razie wszystkie informacje dotyczące napoju będą przechowywane w klasie Javy.


W jednym z kolejnych rozdziałów przeniesiemy je do bazy danych, a teraz chcemy
się skoncentrować na implementacji innych aspektów aplikacji, nie zawracając sobie
głowy bazami danych.

1 Podczas uruchamiania aplikacji zostaje wyświetlona aktywność TopLevelActivity.


Ta aktywność korzysta z układu activity_top_level.xml. Jej działanie sprowadza się do wyświetlenia
listy opcji: Napoje, Przekąski oraz Kafeterie.
Nie musimy
tw
specjalnego uk orzyć
2 Na liście prezentowanej przez aktywność TopLevelActivity użytkownik dla aktywnośc ładu
DrinkCategoryAi
klika opcję Napoje.
Już niebawem ctivity.
W efekcie zostaje uruchomiona aktywność DrinkCategoryActivity, się, dlaczego przekonasz
nie jest to
która wyświetla listę napojów. konieczne.

3 Szczegółowe informacje o napoju są zapisane w klasie Drink zdefiniowanej w pliku Drink.java.


Aktywność DrinkCategoryActivity uzyskuje wartości wyświetlane na liście napojów właśnie z tej klasy.

4 Użytkownik klika jeden z napojów wyświetlony na liście w aktywności DrinkCategoryActivity.


W efekcie zostaje uruchomiona aktywność DrinkActivity. Korzysta ona z układu o nazwie activity_drink.xml.

5 Aktywność DrinkActivity pobiera szczegółowe informacje o napoju z klasy zdefiniowanej


w pliku Drink.java.

<Layout>
<Layout>

</Layout>
</Layout>
Ale dlaczego ta
aktywność nie Drink.java activity_drink.xml
activity_top_level.xml wymaga definiowania 5
układu? Dowiesz się
3
tego już niebawem.

1
2 4

TopLevelActivity.java DrinkCategoryActivity.java DrinkActivity.java


Urządzenie

234 Rozdział 6.
Widoki list i adaptery

Oto czynności, które wykonamy


W ramach prac nad naszą aplikacją musimy wykonać kilka czynności:

1 Dodać do projektu klasę Drink i zasoby graficzne.


Ta klasa zawiera szczegółowe informacje o wszystkich
dostępnych napojach. Jeśli chodzi o zasoby graficzne,
to należy do nich logo kafeterii Coffeina i zdjęcia
poszczególnych napojów.

2 Utworzyć klasę TopLevelActivity i jej układ.


To jest punkt wejścia do aplikacji. Aktywność musi wyświetlić
logo kafeterii Coffeina i listę z opcjami do nawigowania po
aplikacji. Oprócz tego po kliknięciu opcji Napoje aktywność
TopLevelActivity musi spowodować uruchomienie
aktywności DrinkCategoryActivity.

3 Utworzyć aktywność DrinkCategoryActivity.


Aktywność DrinkCategoryActivity zawiera listę wszystkich
dostępnych napojów. Po kliknięciu któregoś z napojów ma
ona uruchomić aktywność DrinkActivity.

4 Utworzyć aktywność DrinkActivity.


Aktywność DrinkActivity wyświetla szczegółowe informacje
o napoju klikniętym przez użytkownika na liście w aktywności
DrinkCategoryActivity.

Utworzenie projektu ¨  Dodanie zasobów


Projekt naszej nowej aplikacji możesz utworzyć w dokładnie taki sam ¨  TopLevelActivity
sposób, w jaki tworzyłeś projekty w poprzednich rozdziałach. ¨  DrinkCategoryActivity
¨  DrinkActivity
Utwórz projekt aplikacji o nazwie Coffeina i umieść go w pakiecie
com.hfad.coffeina. Jako minimalną wersję SDK wybierz API
poziomu 15. Utwórz od razu aktywność o nazwie TopLevelActivity
i plik układu o nazwie activity_top_level.xml.

jesteś tutaj  235


Klasa Drink

Klasa Drink ¨  Dodanie zasobów


¨  TopLevelActivity
¨  DrinkCategoryActivity
Zaczniemy od dodania do aplikacji klasy Drink. Drink.java to zupełnie zwyczajny plik ¨  DrinkActivity
zawierający klasę napisaną w Javie, z której aktywności będą pobierać dane o napojach.
Klasa Drink definiuje tablicę trzech napojów, przy czym każdy z nich będzie zawierał
informacje o nazwie napoju, jego opis oraz identyfikator zasobu graficznego z jego zdjęciem.
Dodaj zatem klasę do pakietu com.hfad.coffeina, zapisz ją w projekcie, w katalogu app/src/
main/java, a samej klasie nadaj nazwę Drink. Po wpisaniu kodu klasy zapisz plik.

package com.hfad.coffeina;
Każdy Drink ma nazwę i opis oraz
identyfikator zasobu graficznego. zawiera
public class Drink { Te identyfikatory
zasobów określają zdjęcia napojów,
private String name; do projektu na następnej stronie. które dodamy
private String description;
private int imageResourceId;
drinks to tablica trzech obiektów klasy Drink.
// drinks to tablica obiektów klasy Drink
public static final Drink[] drinks = {
To są zdjęcia new Drink(”Latte”, ”Czarne espresso z gorącym mlekiem i mleczną pianką.”,
napojów. R.drawable.latte),
Zaraz się nimi new Drink(”Cappuccino”, ”Czarne espresso z dużą ilością spienionego mleka.”,
zajmiemy. R.drawable.cappuccino),
new Drink(”Espresso”, ”Czarna kawa ze świeżo mielonych ziaren najwyższej jakości.”,
R.drawable.filter)
}; Konstruktor klasy Drink.

// Każdy Drink ma nazwę, opis oraz zasób graficzny


private Drink(String name, String description, int imageResourceId) {
this.name = name;
this.description = description;
this.imageResourceId = imageResourceId;
}
Coffeina
public String getDescription() {
return description;
} app/src/main

To są metody get, akcesory,


public String getName() { do pobierania informacji ze java
return name; zmiennych prywatnych.
} com.hfad.coffeina

public int getImageResourceId() {


return imageResourceId; Drink.java
}

public String toString() { Łańcuchową reprezentacją


return this.name; obiektu Drink jest nazwa
} napoju.
}

236 Rozdział 6.
Widoki list i adaptery

Pliki graficzne
Kod klasy Drink zawiera trzy zasoby graficzne, które są używane
w informacjach o napojach i mają następujące identyfikatory:
R.drawable.latte, R.drawable.cappuccino oraz R.drawable.
filter. Identyfikator R.drawable.latte odwołuje się do pliku
graficznego o nazwie latte, identyfikator R.drawable.cappuccino do
pliku cappuccino, a identyfikator R.drawable.filter do pliku filter.

Te trzy pliki graficzne musimy teraz dodać do projektu, wraz z logo


kafeterii Coffeina, które będzie używane w aktywności głównego
poziomu. Wszystkie te pliki graficzne można znaleźć w przykładach
dołączonych do książki, które można pobrać z serwera FTP
wydawnictwa Helion — ftp://ftp.helion.pl/przyklady/andrrg.zip.
Zaznacz je, a następnie przeciągnij do katalogu app/src/main/res/
drawable swojego projektu aplikacji kafeterii Coffeina.

Po dodaniu obrazów do projektu musimy zdecydować, czy chcemy


wyświetlać różne obrazy na ekranach o różnej gęstości. My mamy
zamiar zawsze wyświetlać obrazy o tej samej rozdzielczości,
niezależnie od gęstości ekranu, dlatego do projektu dodaliśmy tylko
jeden egzemplarz każdego z niezbędnych pików i umieściliśmy
je wszystkie w jednym katalogu. Jeśli we własnych aplikacjach
zdecydujesz się używać różnych wersji obrazów dla różnych gęstości
ekranów, będziesz musiał umieszczać odpowiednie wersje plików
graficznych w odpowiednich katalogach drawable* (zgodnie
z informacjami podanymi w rozdziale 5.).

Oto cztery pliki graficzne. Dodałeś je


do Android Studio, przeciągając je do
katalogu drawable.

Po dodaniu do projektu Android przypisuje każdemu z nich


identyfikator o postaci R.drawable.nazwa_pliku. Na przykład
plikowi latte.png zostanie nadany identyfikator R.drawable.latte,
który będzie odpowiadał identyfikatorowi zasobu obrazka latte
podanemu w kodzie klasy Drink.

name: „Latte”

description: „Czarne espresso


z gorącym mlekiem i mleczną pianką.”

Drink imageResourceId: R.drawable.latte


Plikowi latte.png
został nadany
Skoro dodaliśmy już do projektu klasę Drink i pliki graficzne, identyfikator
R.drawable.latte.
zajmijmy się implementacją aktywności. Zaczniemy od R.drawable.latte
aktywności głównego poziomu.

jesteś tutaj  237


TopLevelActivity

Układ aktywności głównego poziomu ¨  Dodanie zasobów


¨  TopLevelActivity
składa się z obrazka i listy ¨  DrinkCategoryActivity
¨  DrinkActivity
Podczas tworzenia projektu nadaliśmy naszej aktywności głównego poziomu nazwę
TopLevelActivity, a układ, z którego ona korzysta, zapisaliśmy w pliku activity_top_level.xml.
Musimy teraz zmienić ten układ w taki sposób, by prezentował obrazek i listę.

To jest logo kafeterii Coffeina.


Ten obrazek dodaliśmy do
projektu na poprzedniej stronie.

Statyczna lista opcji.

Sposób wyświetlania obrazów za pomocą komponentów ImageView


znasz z poprzedniego rozdziału. W tym przypadku chcemy wyświetlić
logo kafeterii Coffeina, więc utworzymy komponent ImageView,
dla którego źródłem danych będzie plik graficzny starbuzz_logo.png.

Oto kod definiujący ten komponent w pliku układu:


<ImageView To są wymiary, które chcemy
android:layout_width=”200dp” nadać obrazkowi.

android:layout_height=”100dp” Źródłem obrazka będzie plik starbuzz_logo.png,


który wcześniej dodaliśmy do aplikacji.
android:src=”@drawable/starbuzz_logo”
android:contentDescription=”@string/starbuzz_logo” /> Dodanie opisu zawartości poprawi
dostępność aplikacji.

Stosując w aplikacjach komponenty ImageView, należy dodawać do nich atrybut


android:contentDescription zawierający opis prezentowanego obrazu, takie Coffeina
rozwiązanie poprawia bowiem dostępność aplikacji. W naszym przypadku atrybut ten
zawiera odwołanie do zasobu łańcuchowego, ”@string/starbuzz_logo”. Musimy
app/src/main
zatem dodać ten zasób do pliku strings.xml:

<resources> res
...
<string name=”starbuzz_logo”>Logo kafeterii Coffeina</string> values
<xml>
</resources> </xml>

strings.xml
To już wszystko, co trzeba zrobić, by dodać obraz do układu, zajmijmy się więc listą.

238 Rozdział 6.
Widoki list i adaptery

Użycie widoku listy do wyświetlania listy opcji


Jak już wspominaliśmy wcześniej, widok listy umożliwia wyświetlenie pionowej listy android.view.View
danych, której można użyć do nawigowania po aplikacji. Dlatego też dodamy do
...
układu widok listy prezentujący trzy opcje, których później użyjemy do przechodzenia
do innych aktywności.

Jak zdefiniować widok listy w kodzie XML? android.view.ViewGroup


Widok listy możemy dodać do układu, używając elementu <ListView>. Zawartość ...
tak utworzonego widoku listy określamy za pomocą atrybutu android:entries,
zawierającego identyfikator tablicy łańcuchów znaków. Poszczególne łańcuchy z tej
tablicy zostaną następnie wyświetlone w widoku listy jako lista widoków tekstowych.
android.widget.
Poniżej pokazaliśmy, w jaki sposób można dodać do układu widok listy, którego AdapterView
zawartość zostanie określona na podstawie tablicy łańcuchów znaków o nazwie options: ...
Ten element definiuje widok listy.
<ListView
android:id=”@+id/list_options”
Wartości android.widget.ListView
android:layout_width=”match_parent” wyświetlane
android:layout_height=”wrap_content” na liście zostały ...
zdefiniowane
android:entries=”@array/options” /> w tablicy options.

Samą tablicę możemy zdefiniować w dokładnie taki sam sposób, w jaki robiliśmy
to już wcześniej — dodając odpowiedni element do pliku strings.xml:

<resources>
.... Coffeina
<string-array name=”options”>
<item>Napoje</item> app/src/main
<item>Przekąski</item>
<item>Kafeterie</item> res

</string-array>
values
....
<xml>
</resources> </xml>

strings.xml
W ten sposób w widoku listy zostaną wyświetlone trzy wartości: Napoje,
Przekąski oraz Kafeterie.
@array/options Atrybut entries wypełnia
komponent ListView
<resources> wartościami pobranymi
z tablicy options. Każdy
</resources> z elementów komponentu
Napoje ListView jest widokiem
ListView strings.xml tekstowym.
Przekąski
Kafeterie

jesteś tutaj  239


Kod układu

Kompletny kod układu aktywności głównego poziomu ¨  Dodanie zasobów


¨  TopLevelActivity
¨  DrinkCategoryActivity
Oto pełny kod układu (upewnij się, czy Twój układ będzie dokładnie taki sam jak nasz): ¨  DrinkActivity

<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
Używamy układu liniowego o układzie
android:layout_height=”match_parent” pionowym. Dzięki temu lista opcji zostanie
android:orientation=”vertical” wyświetlona bezpośrednio poniżej logo
kafeterii Coffeina.
tools:context=”.TopLevelActivity” >

<ImageView Coffeina
android:layout_width=”200dp”
android:layout_height=”100dp” app/src/main
android:src=”@drawable/starbuzz_logo”
android:contentDescription=”@string/starbuzz_logo” /> res

layout
<ListView
<xml>
android:id=”@+id/list_options” </xml>

android:layout_width=”match_parent” activity_
top_level.xml
android:layout_height=”wrap_content”
android:entries=”@array/options” />
</LinearLayout>

Jazda próbna
Upewnij się, że wprowadziłeś wszystkie modyfikacje w plikach
activity_top_level.xml i strings.xml. Kiedy uruchomisz aplikację,
powinieneś zobaczyć na ekranie urządzenia logo kafeterii
Coffeina, a pod nim widok listy. Ten widok powinien
prezentować trzy wartości pobrane z tablicy options.

Gdy klikniesz dowolną opcję z tej listy, nic się nie stanie —
To są wartości
wynika to z prostego faktu, że jeszcze nie kazaliśmy widokowi zapisane w tablicy
listy reagować na kliknięcia. Teraz mamy zamiar pokazać, jak options.
sprawić, by widok listy reagował na kliknięcia i uruchamiał inną
aktywność.

240 Rozdział 6.
Widoki list i adaptery

Zapewnianie reakcji ListView na kliknięcia


za pomocą obiektu nasłuchującego Komponent ListView musi wiedzieć,
że aktywność obchodzi to, co się
z nim dzieje.
Elementy widoku listy mogą reagować na kliknięcia poprzez
zaimplementowanie obiektu nasłuchującego (ang. event listener).

Obiekt nasłuchujący pozwala nam oczekiwać na zdarzenia zachodzące


w aplikacji, takie jak kliknięcie widoku, utrata lub uzyskanie miejsca
wprowadzania, a nawet naciśnięcie przycisku sprzętowego na
urządzeniu. Jeśli zaimplementujemy taki obiekt nasłuchujący, możemy Aktywność ListView
określić, kiedy użytkownik wykonał określoną czynność — taką jak
kliknięcie widoku — i zareagować na nią. Komponent ListView informuje aktywność
o tym, że jeden z jego elementów został
kliknięty, aby ta mogła na to zdarzenie
OnItemClickListener oczekuje na kliknięcia zareagować.

Jeśli chcemy, aby elementy listy reagowały na kliknięcia, musimy


zaimplementować obiekt typu OnItemClickListener i jego metodę
onItemClick(). Obiekt nasłuchujący typu OnItemClickListener oczekuje na
zdarzenia związane z kliknięciem, a jego metoda onItemClick() pozwala nam
określić, jak należy na te kliknięcia zareagować. Metoda ta ma kilka parametrów,
których możemy użyć do określenia, który element został kliknięty, na przykład
pobrać referencję do klikniętego widoku, jego położenie na liście (liczone od 0)
czy też nieprzetworzony identyfikator prezentowanych na nim danych.

W przypadku naszej aplikacji chcemy, by po kliknięciu pierwszego elementu


listy, elementu w położeniu o indeksie 0, została uruchomiona aktywność
DrinkCategoryActivity. Jeśli zostanie kliknięty ten element, musimy
utworzyć intencję odwołującą się do aktywności DrinkCategoryActivity. OnItemClickListener jest klasą
Poniżej przedstawiliśmy kod obiektu nasłuchującego: zagnieżdżoną, która jest umieszczona
wewnątrz klasy AdapterView.
ListView jest klasą pochodną klasy
AdapterView.OnItemClickListener itemClickListener = AdapterView.

new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> listView, To widok, który został kliknięty
Napoje to pierwszy element wyświetlony (w naszym przypadku jest to widok listy).
View itemView,
w widoku listy, co oznacza, że znajduje
się on w miejscu o indeksie 0. int position, Te parametry dają nam nieco więcej informacji o tym,
long id) { który element listy został kliknięty i jakie jest jego
położenie na liście.
if (position == 0) {
Intent intent = new Intent(TopLevelActivity.this, DrinkCategoryActivity.class);
startActivity(intent);
Intencja pochodzi z aktywności Ma ona spowodować
} TopLevelActivity. uruchomienie aktywności
} DrinkCategoryActivity.
};

Po utworzeniu obiektu nasłuchującego musimy go dodać do komponentu ListView.

jesteś tutaj  241


setOnItemClickListener()

Dodanie obiektu nasłuchującego do widoku listy ¨  Dodanie zasobów


¨  TopLevelActivity
¨  DrinkCategoryActivity
Po utworzeniu obiektu OnItemClickListener musimy powiązać go z widokiem ¨  DrinkActivity
listy. Służy do tego metoda ListView.setOnItemClickListener(). Ma ona tylko
jeden argument — sam obiekt nasłuchujący:

AdapterView.OnItemClickListener itemClickListener =
new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> listView,
...
}
};
ListView listView = (ListView) findViewById(R.id.list_options);
listView.setOnItemClickListener(itemClickListener); To jest utworzony wc
ześniej
obiekt nasłuchujący.
Dodanie obiektu nasłuchującego do widoku listy ma kluczowe znaczenie, gdyż
zapewnia on, że obiekt ten będzie powiadamiany o kliknięciach elementów
listy. Jeśli tego nie zrobimy, to elementy naszego widoku listy nie będą w stanie
reagować na kliknięcia.

Teraz wiesz już wszystko, co trzeba, by sprawić, że widok listy w aktywności


TopLevelActivity będzie reagował na kliknięcia.

Co się stanie po uruchomieniu kodu?


1 Metoda onCreate() aktywności TopLevelActivity tworzy obiekt OnItemClickListener
i dodaje go do widoku listy.

TopLevelActivity ListView onItemClickListener

2 Kiedy użytkownik klika jeden z elementów widoku listy,


zostaje wywołana metoda onItemClick() obiektu OnItemClickListener.
Jeśli użytkownik kliknie opcję Napoje, to obiekt nasłuchujący OnItemClickListener
utworzy intencję, która uruchomi aktywność DrinkCategoryActivity.

onItemClick() Intencja

ListView onItemClickListener DrinkCategoryActivity


242 Rozdział 6.
Widoki list i adaptery

Kompletny kod aktywności TopLevelActivity ¨  Dodanie zasobów


¨  TopLevelActivity
¨  DrinkCategoryActivity
Poniżej przedstawiliśmy pełny kod aktywności TopLevelActivity, zapisany ¨  DrinkActivity
w pliku TopLevelActivity.java. Zastąp nim kod wygenerowany przez kreator
Android Studio i zapisz wprowadzone zmiany.

package com.hfad.coffeina;
Coffeina

import android.app.Activity;
app/src/main
import android.content.Intent;
import android.os.Bundle;
java
import android.widget.AdapterView;
import android.widget.ListView; Korzystamy z tych dodatkowych klas. com.hfad.coffeina
import android.view.View;
TopLevel
public class TopLevelActivity extends Activity { Activity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top_level);
Tworzymy obiekt
// Tworzymy obiekt nasłuchujący OnItemClickListener nasłuchujący.
AdapterView.OnItemClickListener itemClickListener =
new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> listView,
View v,
Implementujemy jego
int position, metodę onItemClick().
long id) {
if (position == 0) {
Intent intent = new Intent(TopLevelActivity.this,
DrinkCategoryActivity.class);
startActivity(intent);
} Jeśli użytkownik kliknie element
Napoje, to uruchamiamy aktywność
} DrinkCategoryActivity. Utworzymy ją już
za chwilę, więc nie przejmuj się, jeśli
}; Android Studio zacznie Cię ostrzegać,
że jej nie ma.
// Dodajemy obiekt nasłuchujący do widoku listy
ListView listView = (ListView) findViewById(R.id.list_options);
listView.setOnItemClickListener(itemClickListener); Dodajemy obiekt nasłuchujący
} do widoku listy.

}
jesteś tutaj  243
Jesteś tutaj

Dokąd dotarliśmy? ¨  Dodanie zasobów


¨  TopLevelActivity
¨  DrinkCategoryActivity
Dotychczas utworzyliśmy plik Drink.java oraz zaimplementowaliśmy aktywność
¨  DrinkActivity
TopLevelActivity i używany przez nią układ.
Ten plik dodaliśmy
w pierwszej kolejności.

<Layout>
<Layout>

</Layout>
</Layout>
Utworzyliśmy
aktywność Drink.java activity_drink.xml
TopLevelActivity activity_top_level.xml
i jej układ.

TopLevelActivity.java DrinkCategoryActivity.java DrinkActivity.java


Urządzenie

Tą aktywnością zajmiemy
się w następnej kolejności.

Kolejną rzeczą, którą musimy się zająć, będzie utworzenie aktywności


TopLevelActivity, tak by aplikacja mogła ją uruchomić po kliknięciu
opcji Napoje w aktywności TopLevelActivity.

Nie istnieją
głupie pytania

P: Dlaczego musieliśmy tworzyć obiekt nasłuchujący,  O : Atrybutu android:onClick można używać w kodzie XML


żeby elementy komponentu ListView zaczęły reagować  układów wyłącznie w przyciskach lub w innych widokach, które
na kliknięcia? Czy nie wystarczyłoby użyć atrybutu  są klasami pochodnymi klasy Button, takich jak CheckBox lub
android:onClick w kodzie XML układu? RadioButton.
ListView nie jest klasą pochodną klasy Button, zatem w jej
przypadku zastosowanie atrybutu android:onClick nic by nie
dało. To właśnie dlatego musimy zaimplementować własny obiekt
nasłuchujący.

244 Rozdział 6.
Widoki list i adaptery

Poniżej przedstawiliśmy kod aktywności pochodzący z innego projektu. Kiedy użytkownik kliknie jeden
z elementów widoku listy, jej kod ma wyświetlić w komponencie TextView tekst prezentowany w danym
elemencie listy. Czy przedstawiony kod działa zgodnie z założeniami? A jeśli nie działa, to dlaczego?
Ćwiczenie Komponent TextView ma identyfikator text_view, a widok listy list_view.

package com.hfad.ch06_ex;

import android.app.Activity;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.view.View;

public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.text_view);
AdapterView.OnItemClickListener itemClickListener =
new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> listView,
View v,
int position,
long id) {
TextView item = (TextView) v;
textView.setText(item.getText());
}
};
ListView listView = (ListView) findViewById(R.id.list_view);
}
}

jesteś tutaj  245


Rozwiązanie ćwiczenia

Poniżej przedstawiliśmy kod aktywności pochodzący z innego projektu. Kiedy użytkownik kliknie jeden
z elementów widoku listy, jej kod ma wyświetlić w komponencie TextView tekst wyświetlony w danym
elemencie listy. Czy przedstawiony kod działa zgodnie z założeniami? A jeśli nie działa, to dlaczego?
Ćwiczenie Komponent TextView ma identyfikator text_view, a widok listy list_view.
Rozwiązanie

package com.hfad.ch06_ex;

import android.app.Activity;
import android.os.Bundle;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.view.View;

public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.text_view);
AdapterView.OnItemClickListener itemClickListener =
new AdapterView.OnItemClickListener(){
public void onItemClick(AdapterView<?> listView,
View v,
int position,
To jest kliknięty elementy long id) {
listy. To komponent TextView,
więc możemy odczytać TextView item = (TextView) v;
wyświetlany w nim tekst textView.setText(item.getText());
przy użyciu metody getText().
}
};
ListView listView = (ListView) findViewById(R.id.list_view);
}
}

Kod nie działa zgodnie z założeniami, gdyż na jego końcu brakuje wywołania
metody listView.setOnItemClickListener(itemClickListener);.
Z wyjątkiem tego jednego błędu kod jest w porządku.

246 Rozdział 6.
Widoki list i adaptery

Aktywność kategorii wyświetla dane jednej kategorii ¨  Dodanie zasobów


¨  TopLevelActivity
Jak już zaznaczyliśmy wcześniej, DrinkCategoryActivity jest przykładem aktywności
¨  DrinkCategoryActivity
¨  DrinkActivity
kategorii. Aktywności tego rodzaju prezentują dane należące do jednej kategorii, przy czym
bardzo często wyświetlają je w formie listy. Takiej aktywności można użyć, by przejść od
aktywności prezentującej informacje szczegółowe.

W naszym przykładzie użyjemy aktywności DrinkCategoryActivity, by wyświetlić listę


napojów. Kiedy użytkownik kliknie jeden z tych napojów, zostaną wyświetlone szczegółowe
informacje o tym napoju.

Aktywność DrinkCategoryActivity
wyświetla listę napojów. Kiedy
Kiedy użytkownik klinie użytkownik kliknie jeden z nich,
element Napoje, zostanie zostanie uruchomiona aktywność
uruchomiona aktywność DrinkActivity, która wyświetli
DrinkCategoryActivity. szczegółowe informacje
o wybranym napoju.

W tym celu utworzymy aktywność zawierającą pojedynczy widok listy, w którym


wyświetlimy listę wszystkich napojów. Ponieważ nasza aktywność ma zawierać tylko
i wyłącznie widok listy, bez żadnych dodatkowych komponentów GUI, możemy
użyć aktywności specjalnego typu, nazywanej aktywnością listy. Czym zatem jest
ta aktywność listy?

jesteś tutaj  247


ListActivity

ListActivity to aktywność zawierająca ¨  Dodanie zasobów


¨  TopLevelActivity
jedynie listę ¨  DrinkCategoryActivity
¨  DrinkActivity
Aktywność listy to typ aktywności, która została stworzona do
obsługi list. Aktywność tego typu jest automatycznie kojarzona
ListActivity jest klasą
z widokiem listy, dzięki czemu nie musimy go tworzyć samodzielnie. pochodną klasy Activity.
Oto przykładowy wygląd takiej aktywności:

android.app.Activity
...
Aktywność listy
zawsze dysponuje
swoim własnym
widokiem listy, więc
nie musimy go tworzyć
własnoręcznie. Wciąż android.app.ListActivity
musimy dostarczyć getListView()
dane na listę i już
zaraz pokażemy, onListItemClick()
jak to należy robić.
...

Stosowanie aktywności listy do wyświetlania danych kategorii ma dwie


podstawowe zalety:

 Nie musimy tworzyć własnego układu.


Aktywności list definiują używane układy w sposób programowy, zatem
stosując je, nie musimy tworzyć ani utrzymywać żadnych plików układów. ListActivity to
Układ generowany przez te aktywności składa się z pojedynczego widoku klasa pochodna
listy. W kodzie aktywności można odwołać się do tego widoku, wywołując
metodę getListView(). Metoda ta jest bardzo potrzebna, gdyż pozwala klasy Activity,
określić, które dane mają być widoczne na liście. wyspecjalizowana do
obsługi list. Korzysta
 Nie musimy implementować własnych obiektów nasłuchujących.
Klasa ListActivity implementuje obiekt nasłuchujący, który obsługuje ona z domyślnego
zdarzenia kliknięcia elementów listy. A więc zamiast samodzielnie tworzyć
układu zawierającego
taki obiekt i dodawać go do widoku listy, wystarczy zaimplementować
metodę onListItemClick() aktywności. Dzięki temu znacznie łatwiej pojedynczy
można sprawić, że lista będzie reagować na kliknięcia jej elementów.
komponent ListView.
Sam się o tym przekonasz już niebawem, kiedy zastosujemy metodę
onListItemClick() do uruchamiania innej aktywności.

Aktywności kategorii przeważnie muszą prezentować pojedynczy widok listy, którego


użytkownik może użyć, by przejść do szczegółowych informacji o wybranym elemencie,
dlatego doskonale nadają się do takich zastosowań.

A więc jak wygląda kod aktywności listy?

248 Rozdział 6.
Widoki list i adaptery

Jak utworzyć aktywność listy?


Poniżej pokazaliśmy, jak wygląda podstawowy kod aktywności listy. Jak Android Studio może automatycznie
widać, aktywności tego typu są tworzone niemal tak samo jak wszystkie inne. wygenerować dla nas plik układu.
Nie skorzystamy jednak z tej
Skorzystaj teraz z kreatora New Activity, aby dodać do swojego projektu możliwości, gdyż aktywności listy
nową aktywność o nazwie DrinkCategoryActivity, a następnie zastąp jej definiują swój własny układ.
wygenerowany kod kodem przedstawionym poniżej:
package com.hfad.coffeina;
Coffeina
import android.app.ListActivity; Ta aktywność musi dziedziczyć
po klasie ListActivity, a nie
import android.os.Bundle; Activity. app/src/main

public class DrinkCategoryActivity extends ListActivity {


java

@Override
com.hfad.coffeina
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Klasa ListActivity dziedziczy metodę
} onCreate() po klasie Activity. DrinkCategory
Już zaraz dodamy kod tej metody. Activity.java
}
Powyższy kod tworzy prostą aktywność listy o nazwie DrinkCategoryActivity. Ponieważ
jest to aktywność listy, musi dziedziczyć po klasie ListActivity, a nie Activity.

Kolejną różnicą między aktywnościami typu ListActivity a aktywnościami


dziedziczącymi bezpośrednio po klasie Activity jest to, że w przypadku tych pierwszych
nie musimy używać metody setContentView() do określenia układu, którego aktywność
ma używać. Sama aktywność potrafi o to zadbać.

Podobnie jak wszystkie inne aktywności, także aktywności list muszą być zarejestrowane
w pliku manifestu — AndroidManifest.xml. Jest to niezbędne, by mogły być używane
w aplikacji. Podczas tworzenia aktywności listy Android Studio automatycznie dodaje
odpowiedni kod do pliku manifestu.
<application
... >
<activity
android:name=”.TopLevelActivity” To jest pierwsza z aktywności,
android:label=”@string/app_name” które utworzyliśmy.
...
To jest nowa aktywność. Każdej
</activity> aktywności musi odpowiadać wpis
<activity w pliku AndroidManifest.xml.
Coffeina
android:name=”.DrinkCategoryActivity”
android:label=”@string/title_activity_drink_category” >
app/src/main
</activity> <xml>
</application> </xml>

AndroidManifest.xml
Skoro już utworzyliśmy aktywność listy, musimy teraz wypełnić ją danymi.
jesteś tutaj  249
Adaptery

android:entries działa na statycznych tablicach ¨  Dodanie zasobów


¨  TopLevelActivity
łańcuchów znaków zdefiniowanych w strings.xml ¨  DrinkCategoryActivity
¨  DrinkActivity
Kiedy pisaliśmy pierwszą aktywność, TopLevelActivity, mogliśmy powiązać dane
z widokiem listy, używając atrybutu android:entries zapisywanego w kodzie
XML układu. Rozwiązanie to działało, gdyż dane były przechowywane w formie
zasobu, jako statyczna tablica łańcuchów znaków. Tablica ta została zdefiniowana
w pliku strings.xml, dzięki czemu bez problemów mogliśmy się do niej odwołać
w następujący sposób:

android:entries=”@array/options”

gdzie options było nazwą tablicy łańcuchów znaków.


Dane o napojach
muszą pochodzić
Jednak atrybut android:entries można stosować wyłącznie w przypadku, Widok listy należy z tablicy drinks
gdy dane pochodzą ze statycznej tablicy zdefiniowanej w pliku strings.xml. wypełnić danymi zdefiniowanej
o napojach. w klasie Drink.
A co we wszystkich innych sytuacjach? Co zrobić, gdy dane pochodzą
z tablicy utworzonej programowo w kodzie Javy albo z bazy danych?
W takim przypadku zastosowanie atrybutu android:entries nie zadziała. drinks

Jeśli musimy powiązać widok listy z danymi pochodzącymi z innego


źródła niż zasób będący tablicą łańcuchów znaków, to będziemy musieli
zastosować inne rozwiązanie — będziemy musieli napisać w aktywności Drink.java
ListView
kod, który odpowiednio powiąże te dane z widokiem listy. W naszym
przypadku musimy powiązać widok listy z tablicą drinks w klasie Drink.

Jeśli dane nie są statyczne, to należy użyć adaptera


Jeśli w widoku listy musimy wyświetlić dane, które nie pochodzą z zasobu statycznego,
lecz na przykład z tablicy zdefiniowanej w kodzie Javy lub z bazy danych, to musimy
użyć adaptera. Adapter działa jak swoisty most łączący źródło danych z widokiem listy:

ListView Adapter Źródło Naszym źródłem


danych będzie
danych tablica, ale równie
dobrze moglibyśmy
użyć bazy danych lub
usługi internetowej.

Adapter stanowi most łączący widok listy ze źródłem


danych. Adaptery pozwalają widokom list wyświetlać
dane pochodzące z wielu różnych źródeł.

Dostępnych jest kilka różnych rodzajów adapterów. Na razie skoncentrujemy się


na adapterze ArrayAdapter.
250 Rozdział 6.
Widoki list i adaptery

Łączenie widoków list z tablicami za pomocą adaptera ArrayAdapter


ArrayAdapter to typ adaptera, który służy do wiązania tablic z widokami. Można Adapter działa jak
go używać z wieloma klasami pochodnymi klasy AdapterView, czyli na przykład
z widokami list i listami rozwijanymi. most łączący widok,
W naszym przypadku zastosujemy adapter ArrayAdapter do wyświetlenia obiekt typu View,
w widoku listy danych pochodzących z tablicy Drink.drinks. ze źródłem danych.
Utworzymy adapter ArrayAdapter, ArrayAdapter jest
To jest nasz widok listy. aby powiązać widok listy z naszą To jest
tablicą. nasza tablica.
typem adaptera
ListView Array Drink.
wyspecjalizowanego
Adapter drinks do operowania
na tablicach.

Aby skorzystać z adaptera ArrayAdapter, należy go najpierw zainicjować


i dołączyć do widoku listy.

Inicjalizacja adaptera ArrayAdapter wymaga w pierwszej kolejności określenia


typu danych przechowywanych w tablicy, którą chcemy powiązać z widokiem
listy. W wywołaniu konstruktora adaptera ArrayAdapter można podać trzy
parametry: obiekt Context (którym zazwyczaj jest bieżąca aktywność), zasób
układu określającego, jak mają być prezentowane poszczególne elementy tablicy,
oraz samą tablicę.

Poniżej przedstawiliśmy kod, który tworzy adapter do wyświetlania danych typu


Tablica zawiera dane typu Drink.
Drink przechowywanych w tablicy Drink.drinks:

ArrayAdapter<Drink> listAdapter = new ArrayAdapter<Drink>(


this, To jest wbudowany zasób
this odwołuje się do
bieżącej aktywności. android.R.layout.simple_list_item_1, układu. Informuje on adapter, że
Activity jest klasą poszczególne elementy tablicy mają
pochodną klasy Context. Drink.drinks); Tablica być wyświetlane w pojedynczych
widokach tekstowych.

Po utworzeniu adaptera musimy go powiązać z widokiem listy; służy do tego


metoda setAdapter() klasy ListView:

ListView listView = getListView();


listView.setAdapter(listAdapter);
Za kulisami adapter odczyta każdy element tablicy, skonwertuje elementy do
postaci łańcuchów znaków za pomocą metody toString(), a następnie umieści
je w widokach tekstowych. Na koniec wszystkie utworzone w ten sposób widoki
tekstowe zostaną wyświetlone jako odrębne wiersze widoku ListView.
jesteś tutaj  251
Zastosowanie adaptera ArrayAdapter

Dodanie adaptera ArrayAdapter do aktywności ¨  Dodanie zasobów


¨  TopLevelActivity
DrinkCategoryActivity ¨  DrinkCategoryActivity
¨  DrinkActivity
Zmienimy teraz kod aktywności zapisany w pliku DrinkCategoryActivity.java w taki
sposób, by używał adaptera ArrayAdapter do pobierania danych o napojach
zdefiniowanych w klasie Drink. Ten kod umieścimy w metodzie onCreate(),
tak by widok listy był wypełniany danymi podczas tworzenia aktywności.

Poniżej przedstawiliśmy kompletny kod aktywności (zaktualizuj swój plik,


tak aby był taki sam jak nasz, a następnie zapisz zmiany):

package com.hfad.coffeina;

Coffeina
import android.app.ListActivity;
import android.os.Bundle; app/src/main
import android.widget.ArrayAdapter;
import android.widget.ListView; Używamy tych dodatkowych klas. java

com.hfad.coffeina
public class DrinkCategoryActivity extends ListActivity {

DrinkCategory
@Override Activity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ListView listDrinks = getListView();
ArrayAdapter<Drink> listAdapter = new ArrayAdapter<Drink>(
this,
Ten fragment kodu
android.R.layout.simple_list_item_1, wypełnia widok listy
danymi pochodzącymi
Drink.drinks); z tablicy drinks.
listDrinks.setAdapter(listAdapter);
}
}

To wszystkie zmiany, które musisz wprowadzić


w kodzie aktywności, by w widoku listy były
wyświetlane dane napojów pochodzących
z tablicy Drink.drinks.

To są napoje pochodzące
z tablicy Drink.drinks.

252 Rozdział 6.
Widoki list i adaptery

Co się stanie po wykonaniu kodu?


1 Kiedy użytkownik klika opcję Napoje, zostaje uruchomiona aktywność DrinkCategoryActivity.
Ponieważ klasa DrinkCategoryActivity jest aktywnością listy, dysponuje domyślnym układem
zawierającym pojedynczy komponent ListView. Ten układ jest tworzony przez kod Javy w niewidoczny
dla nas sposób, a zatem nie jest definiowany przez kod XML.

DrinkCategoryActivity ViewGroup ListView

2 Aktywność DrinkCategoryActivity tworzy adapter ArrayAdapter<Drink> — adapter,


który operuje na tablicy obiektów Drink.

DrinkCategoryActivity ArrayAdapter<Drink>

3 Źródłem danych dla adaptera ArrayAdapter jest tablica drinks zdefiniowana w klasie Drink.
Adapter używa metody Drink.toString(), by zwrócić nazwę każdego z napojów.

Drink.toString()

DrinkCategoryActivity ArrayAdapter<Drink> Drink.drinks

4 Aktywność DrinkCategoryActivity wywołuje metodę setAdapter(), określając w ten


sposób, że widok listy ma używać utworzonego wcześniej adaptera ArrayAdapter.
Widok listy używa adaptera do wyświetlenia listy nazw napojów.

ListView

Drink.toString()
DrinkCategoryActivity

ArrayAdapter<Drink> Drink.drinks