Vyvíjíme pro Android: První krůčky

Zajímalo vás někdy, jakou má androidí aplikace architekturu? Co to je activity a co view? Jak vytvořit uživatelské rozhraní a jak v Androidu fungují identifikátory? Chcete vědět, jak vypadá androidí manifest? Pokud ano, tak jste tu správně, neboť o tom si budeme povídat ve dnešním článku.

Seriál: Vyvíjíme pro Android (14 dílů)

  1. Vyvíjíme pro Android: Začínáme 15.6.2012
  2. Vyvíjíme pro Android: První krůčky 22.6.2012
  3. Vyvíjíme pro Android: Suroviny, Intenty a jednotky 29.6.2012
  4. Vyvíjíme pro Android: Bližší pohled na pohledy – 1. díl 13.7.2012
  5. Vyvíjíme pro Android: Bližší pohled na pohledy – 2. díl 13.7.2012
  6. Vyvíjíme pro Android: Fragmenty a SQLite databáze 20.7.2012
  7. Vyvíjíme pro Android: Preference, menu a vlastní Adapter 27.7.2012
  8. Vyvíjíme pro Android: Intenty, intent filtry a permissions 3.8.2012
  9. Vyvíjíme pro Android: Content providery 10.8.2012
  10. Vyvíjíme pro Android: Dialogy a activity 17.8.2012
  11. Vyvíjíme pro Android: Stylování a design 24.8.2012
  12. Vyvíjíme pro Android: Notifikace, broadcast receivery a Internet 31.8.2012
  13. Vyvíjíme pro Android: Nahraváme aplikaci na Google Play Store 7.9.2012
  14. Vyvíjíme pro Android: Epilog 14.9.2012

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.

Matěj začal programovat ve třinácti v PHP, pak v JavaScriptu a Lispu. Nakonec si koupil Androida, a tak programuje hlavně pro něj.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 30

Přehled komentářů

Astapov Potreba nativnych aplikacii
Pavel Re: Potreba nativnych aplikacii
G76 Re: Potreba nativnych aplikacii
XMen Re: Potreba nativnych aplikacii
Martin Hassman Re: Potreba nativnych aplikacii
Pavel Re: Potreba nativnych aplikacii
Matěj Konečný Re: Potreba nativnych aplikacii
Matěj Konečný Re: Potreba nativnych aplikacii
jiri.vrany Re: Potreba nativnych aplikacii
dmkil Re: Potreba nativnych aplikacii
razor zbytecne id?
Matěj Konečný Re: zbytecne id?
Matěj Konečný Re: zbytecne id?
razor Re: zbytecne id?
ruups Re: zbytecne id?
P. Jurkovič Re: Vyvíjíme pro Android: První krůčky
Matěj Konečný Re: Vyvíjíme pro Android: První krůčky
ruups Re: Vyvíjíme pro Android: První krůčky
j3 zkousim zkousim a nevim
Pingy Re: zkousim zkousim a nevim
msx Nefunguje externá klávesnica
msx Re: Nefunguje externá klávesnica
Komoi smysl
toor.1 jak přidat....
iwtu Re: jak přidat....
toor.1 Re: jak přidat....
miras Re: jak přidat....
and Re: jak přidat....
GeorgeCZECH Appka padá
martin Re: Appka padá
Zdroj: https://www.zdrojak.cz/?p=3672