V minulém článku jsme nainstalovali ADK, rozchodili jsme ADT plugin v Eclipse, vytvořili jsme si virtuální zařízení a nastavili jsme debugování na skutečném Androidu. Tím jsme překonali tu nejtěžší část a dnes a v příštích dílech to už bude hračka.
Minule jsme vytvořili ukázkovou aplikaci, která šla spustit. Dnes si vysvětlíme, jak je to možné, a naučíme se k tomu plno dalších věcí.
Activity
Hlavní částí programu, bez níž by ani nešel spustit, je tzv. activity. Activity si můžeme velmi zhruba představit jako takový controller či presenter z MVC/P, neboť stejně jako on zařizuje, aby se všechna data, jež získá od nižších vrstev, správně zobrazila uživateli. Tohle přirovnání ale pokulhává v tom, že Android nemá view, jaké z MVC/P známe. Přesnější by bylo říci, že activity je prezentační vrstva aplikace. Každá activity je potomkem třídy Activity
. (Mimochodem, správně jste si všimli, že je to odkaz. Odteď budu na nové třídy odkazovat do API reference.)
Ještě si dovolím upozornit na konvenci, kterou se budu snažit v rámci seriálu dodržovat, co se pojmenovávání tříd týče. V Androidu máme třídu Activity
, ale activity se zároveň říkám všem jejím potomkům, jež slouží jako základní stavební prvek UI aplikace. Použiji-li variantu s velkým písmenem a strojovým písmem, mluvím o třídě, jejích podtřídách anebo konkrétních objektech. Použiji-li variantu s malým, mluvím o obecném stavebním bloku, návrhovém vzoru. To samé platí i pro View
, resp. view, ContentProvider
, resp. content provider, a tak dále. Ale v podstatě se tím nijak moc trápit nemusíte, hlavním důvodem, proč to dělám, je, že použil-li bych vždy variantu s velkým písmenem a strojovým fontem, působilo by to v textu rušivě.
View
Při zachování analogie s webovým vývojem můžeme říct, že view jsou něco jako HTML. Každá activity si vytvoří všechna view, která potřebuje, případně využije XML souboru, kde jsou všechna potřebná view zapsána. Ta potom activity naplňuje daty, mění je, maže atd. Každé view je potomkem třídy View
.
V naprosté většině případů se view specifikují staticky v XML souborech, které jsou ve složce /res/layout
. Každý soubor má nějaké jméno. To se skládá z identifikátoru ( [a-z0-9_]
), který musí začínat malým písmenem, a koncovky .xml
. Ten identifikátor se potom používá v celém projektu jako… identifikátor. O tom si více řekneme v kapitole Identifikátory.
View
má jednu velmi důležitou podtřídu, a to ViewGroup
. Na rozdíl od View
dokáže ViewGroup
obalovat jedno nebo více View
(a tudíž samozřejmě i ViewGroup
). Nejdůležitějšími ViewGroup
jsou třídy končící na Layout
, které se liší tím, jak rozmisťují své potomky. Dnes si ukážeme LinearLayout
, příště se seznámíme s dalšími.
Eclipse nabízí WYSIWYG editor XML souborů obsahujících view. Dle mého názoru je to pro začátečníky medvědí služba, neboť nechápe-li člověk, jak to ve skutečnosti funguje, může se stát, že vytvoří něco, co sice ve WYSIWYG editoru bude vypadat dobře, ale za určitých okolností se to rozbije a chudáku programátorovi způsobí mnoho bezesných dnů. Proto se mu nijak věnovat nebudu a doporučuji si od začátku osvojovat zápis v XML. Editor začněte používat až tehdy, kdy dobře víte, jaké XML chcete vyprodukovat. Pak se ze zlého pána stane dobrý sluha.
Identifikátory
Na chviličku odbočíme od základní architektury aplikace a povíme si něco o identifikátorech v Androidu. Identifikátory jsou v androidu statickými vlastnostmi třídy R
. To je třída, která je generována automaticky na základě obsahu složky res
. Například pomocí R.layout.new_layout
se můžete odkázat na soubor new_layout.xml
ve složce /res/layout
. Tyto vlastnosti obsahují sice jen nějaká čísla, ale prozatím nám stačí vědět, že funkce, které to potřebují, si s tím nějak poradí. Více se o identifikátorech, které jsou velmi spjaté s resources, budeme dozvídat postupně, jak se budeme učit nové věci.
Šup na to
Jsem zastáncem toho, že nejlépe si programátor nové věci osvojí v praxi, takže nastartujte editory, nažhavte klávesnice a jdeme něco vytvořit.
Začneme layoutem…
Mimochodem, všimněte si, že jsem napsal layout s malým l. Tentokrát to znamená XML soubor s definicemi jednotlivých view.
Vytvořte nový projekt přesně tak, jako jsme to udělali minule. Pak klikněte pravým tlačítkem myši na složku layout
, která je podsložkou res
, vyberte New → Other → Android XML Layout File
. Jako root element
zvolte LinearLayout
a do kolonky File
napište jméno souboru, třeba new_layout
. Pak to potvrďte. Tím se vytvoří layoutový soubor new_layout.xml
a static int R.layout.new_layout
s ním svázaný.
V nově otevřeném tabu new_layout.xml
klikněte dole na new_layout.xml
(vedle Graphical layout
), což odhalí samotné XML. To je poměrně jednoduché. Kromě <?xml
deklarace obsahuje ještě tag LinearLayout
. (Ano, je to tak, jména tagů všech frameworkových View
se shodují se jmény jejich tříd. U uživatelských View
je jménem tagu jméno třídy včetně namespace.) Ten má kromě deklarace namespace ještě tři atributy. Atribut orientation
určuje, jestli obsažené elementy budou vedle sebe nebo pod sebou (druhá možná hodnota je tedy horizontal
). Atributy layout_width
respektive layout_height
určují, jak bude to určité View
veliké. Kromě čísla s jednotkou (jednotkám se budeme věnovat příště) může mít speciální hodnoty wrap_content
, což znamená, že View
bude tak veliké, aby obsáhlo svůj obsah, a match_parent
, respektive fill_parent
, což jsou synonyma a obě značí, že se View
roztáhne na rozměry rodiče, v tomto případě tedy na celou obrazovku. Dvě hodnoty jsou proto, že do API 8, tzn. Android 2.2.x, existovalo jen fill_parent
, pak však bylo rozhodnuto, že match_parent
zní lépe. Vzhledem k tomu, že kompilovat budeme vždy na nejnovější verzi API (obě hodnoty se zkompilují stejně, takže i match_parent
funguje na starších Androidech), nemá smysl fill_parent
používat, Eclipse by jen házelo warningy.
Řekněme, že chceme vyrobit aplikaci, která dá uživateli možnost cokoli napsat a potom stisknout tlačítko. Budeme tedy potřebovat nějaké formulářové políčko a nějaké tlačítko. Formulářovému políčku se říká EditText
. Jako potomka elementu LinearLayout
tedy vytvoříme nový element – EditText
.
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edittext" />
EditText
u nastavíme šířku na šířku rodiče a výšku na výšku obsahu. Díky tomu se nám roztáhne, když budeme psát další řádky.
Posledním a nejzajímavějším atributem je id
. Díky němu budeme schopni získat objekt EditText
v naší Activity
. Má zvláštní hodnotu, jíž rozebereme postupně. @
znamená, že hodnotou atributu je nějaký identifikátor, tzn. že se přeloží na vlastnost třídy R
, ale +
za zavináčem určí, že se vytvoří nový identifikátor. Za tím je potom id/
, což znamená, že vytvářený identifikátor se týká množiny identifikátorů id
(což je mimochodem jediná množina, kde se mohou takto nové identifikátory vytvářet). Za lomítkem je potom jméno identifikátoru.
Přidávání je zrovna trochu komplikovanější. Abyste lépe pochopili, jak zavináč funguje, ukážeme si malou tabulku, jak se budou jednotlivé atributy překládat na vlastnosti třídy R
.
Hodnota atributu | Vlastnost R |
---|---|
@id/my_id |
R.id.my_id |
@value/hello_world |
R.value.hello_world |
@layout/my_layout2 |
R.layout.my_layout2 |
@+id/my_new_id |
R.id.my_new_id , které se ale nejprve vytvoří |
Nejsou to všechna kouzla, která se dají v atributech v XML vyrábět, ale nám to prozatím bohatě stačí. Více si povíme, až se například budeme bavit o stylování.
Máme tedy EditText
, ale ještě musíme přidat tlačítko. Použijeme třídu Button
. Tu umístíme hned pod EditText
.
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Sem klikni"
android:onClick="buttonClicked" />
Rozměrové atributy jsou jasné. Do atributu text
jsme vložili řetězec "Sem klikni"
, což je prasárna, ale dnes to ještě kvůli jednoduchosti přežijeme. Příště už správně text vyčleníme do resources a sem půjde jen identifikátor. Atribut onClick
určitě jméno metody Activity
, která bude dané View
obsahovat. Ta metoda musí být definovaná jako public void buttonClicked(View view)
. Ani tento atribut bych nedoporučil použít u nějakého složitějšího projektu, neboť vnáší závislosti, které se nedají na první pohled odhalit. Ale přidání posluchače události je pro dnešní projekt
zbytečně složité.
Mimochodem, atributem onClick
se dostáváme k jedné věci, která se objevila v komentářích k minulému článku, a to nastavování Target SDK version
. Tvrdil jsem totiž, že nevidím důvod, proč nastavovat starší target než ten nejnovější. Honza nabídl zajímavý protinázor, který stojí za zmínku. Ale nejdříve si musíme něco objasnit.
S tím jak vycházejí nové verze SDK se samozřejmě API mění. Některé věci přibývají, některé jsou naopak zavrženy (více o API levelech a jejich rozdílech v Android API Levels). A novinky jsou dvojího typu.
Ty první se týkají pouze kompilátoru. Jde například o výše zmíněné fill_parent
versus match_parent
. Pokud použijete target nižší než 8, match_parent
jakoby neexistuje. Pokud použijete 8 a vyšší, při použití fill_parent se objeví deprecated warning. I když ale použijete match_parent, bude to fungovat i na nižších verzích Androidu.
Druhé novinky se týkají samotných zařízení. Jde o přidávání nových API pro přístup k hardwaru, API pro práci s novými designovými filozofiemi (fragmenty, action bar atd.), jejichž podpora závisí na konkrétním zařízení, na němž aplikace běží. Pokud máte však jako target nastavenou verzi, v níž daná API není dostupná, Eclipse vyhodí chybu, že danou věc nezná.
Z tohoto pohledu se může jevit jako výhodnější používat jako target co nejnižší čísla. Můj osobní názor na to je jiný, já si hlídám, jaké věci používám a větší cenu pro mě má to, že se kód validuje proti nejnovějším standardům, a tudíž je modernější
.
Každý má ale svobodu rozhodnout se sám, já se rozhodně nesnažím klást nějaká dogmata, věci, které jsou jen mým zvykem, tak označím. Snažím se však o to, aby následování mých rad vývoj nezkomplikovalo a pomohlo začít. S postupem času a dalším studiem si samozřejmě každý vytvoří vlastní názor a vlastní návyky, o nichž nebude možné tvrdit, že jsou špatné.
A ještě bych měl asi vysvětlit, jak jsme se k tomuto dostali přes atribut onClick
. Tento atribut byl totiž přidán až v API 4. Abych se přiznal, nevím, do kterého ze dvou šuplíků novinek patří. A je mi to celkem jedno, neboť panuje obecné přesvědčení, že API 4 (tzn. Android 1.6) je nejnižší verze, na níž se má ve většině případů smysl zaměřovat. Ve verzích nižších, kromě jejich nerozšířenosti, existuje několik bugů a nekompatibilit, jež vývoj komplikují.
Layout máme hotový a jdeme se vrhnout na Activity
.
…a skončíme activitou
Activity jsme vytvářeli, mluvili jsme o nich, mnohokrát jsme je zmínili, ale vlastně pořádně nevíme, jak fungují.
Každá Activity
musí být potomkem třídy android.app.Activity
. Dále je dobrým zvykem dávat jejímu názvu příponu -Activity
. Všechny Activity
mají stejný životní cyklus, o němž si trochu povíme za chvilku a pořádně v některém z dalších dílů, a musejí být zapsané v manifestu.
Manifest?
Pamatujete, jak jsme minule měnili Target SDK version
a Min SDK version
? To byl manifest. V manifestu musejí být zapsané všechny activity (jakož i content providery, broadcast receivery atd., ale o tom si povíme až za dlouho a také si řekneme, že tomu tak vlastně úplně není). Úplně nejjednodušší způsob, jak zaregistrovat novou Activity
, který vám (vytvoříte-li hlavní Activity
při vytváření projektu bude stačit, je otevřít AndroidManifest.xml
, dole vybrat Application
, sjet dolů, kliknout na Add...
, vybrat Activity
, vedle Name
kliknout na Browse...
, napsat jméno vaší Activity
, vybrat ji ze seznamu, potvrdit a uložit.
U Activity
, kterou jsme vytvořili při zakládání nového projektu, je o tohle a ještě o něco navíc (nastavení všeho tak, aby se při kliknutí na ikonu v launcheru spustila) postaráno.
Životní cyklus
V Androidu my sami nevytváříme instance našich activity, o to se stará framework, a tak je na místě otázka, jak tedy docílíme zavolání našich inicializačních metod. Všechny activity mají stejný životní cyklus, kdy se v závislosti na důležitém životním milníku volají jednotlivé metody. Více si povíme někdy příště, dnes (a ještě dlouho) nás bude zajímat jen metoda onCreate(Bundle savedInstanceState)
, jež se volá při vytvoření activity. Parametru si zatím nemusíme všímat. (Pro zvědavce: Obsahuje Bundle
s různými hodnotami, které jsme my (a nejen my) uložili při zničení předchozí instance Activity
kvůli nedostatku paměti tak, aby se mohla obnovit ve stejném stavu a uživatel si ničeho nevšiml.)
V onCreate
je důležité zavolat metodu super.onCreate(savedInstanceState)
a doporučuji dělat to na prvním místě, pokud nemáte pořádný důvod, proč to udělat jinak. Když zavoláte tuto metodu, máte už skoro vystaráno.
Ještě je ale potřeba zavolat metodu setContentView
. Ta přebírá buď objekt View
, anebo identifikátor nějakého layoutu. V naprosté většině případů použijete ten identifikátor. Podíváte-li se na svou automaticky vytvořenou Activity
, vidíte, že jako parametr je R.layout.main
. Protože jsme si vytvořili nový layout, předáme metodě jeho identifikátor ( R.layout.new_layout
). Tím už máme povinnosti v onCreate
hotové úplně, ale samozřejmě tam můžete přidat cokoliv dalšího. My zkusíme přidat následující řádek:
Toast.makeText(this, "Vyplňuj a klikej co hrdlo ráčí!", Toast.LENGTH_LONG).show();
Objekt Toast
je nejjednodušším informačním dialogem v Androidu. Je to takový ten (v čistém Androidu) černý obdélník s informačním textem, který se na chvilku objeví a pak zase zmizí.
Statická metoda makeText
přijímá tři parametry: Context
, v němž se zatím nebudeme hrabat a smíříme se s tím, že Activity
je jeho potomkem, a tudíž můžeme předat this
, String
, což je text k zobrazení (a prasárna, od příště budeme místo řetězce předávat identifikátor textu uloženého v resources) a informaci, na jak dlouho má být informace zobrazena. To je buď Toast.LENGTH_SHORT
, nebo Toast.LENGTH_LONG
. Vrací objekt Toast
, na němž zavoláme metodu show
.
Pokud jste nezapomněli, u tlačítka v layoutu jsme nastavili atribut android:onClick="buttonClicked"
, tím pádem ještě v naší Activity
musíme vyrobit veřejnou metodu s tímto názvem. V těle třeba zobrazíme informaci a úspěšném kliknutí.
// Nezapomeňte přidat View do importů (Ctrl + Shift + O)
public void buttonClicked(View button) {
Toast.makeText(this,
"Úspěšně jsi provedl/a realizaci kliknutí na tlačítko.",
Toast.LENGTH_SHORT).show();
}
Tím je naše aplikace hotová a můžeme ji spustit.
Časté problémy
Stejně jako minule si zde ukážeme řešení na některé problémy, které by vás mohly potkat.
Nezobrazuje se mi Toast
.
Zavolal/a jste metodu show
?
Vytvořil/a jsem si layout, ale nezobrazuje se mi v Activity
Předal/a jste správný identifikátor metodě setContentView
v onCreate
příslušné Activity
?
Procvičování
- Vyzkoušejte si, jak se chovají dnes představené
View
s jinými parametry. - Zkuste vytvořit několik různých tlačítek a každému přidejte posluchače kliknutí.
Závěr
Dnes jsme si ukázali naprosté základy architektury androidí aplikace a jednu jednoduchou jsme si naprogramovali, příště si ji pořádně rozšíříme tak, aby se při kliknutí na tlačítko spustila nová Activity
, která zobrazí obsah EditText
u. Jako vždy budu moc rád za všechny reakce a jako vždy vám poskytnu ke stažení ukázkové zdrojové kódy dnešní aplikace.
Tip na konec
Pro debugovací zprávy používejte místo Toast
objekt android.util.Log
a jeho metodu d(String tag, String message)
, jež vytiskne své parametry do panelu LogCat
( Window → Show View → LogCat
). Ještě se tomu někdy budeme věnovat podrobněji.
Přehled komentářů