Vytvoríme si prvú aplikáciu: kocku! Vždy, keď klikneme na tlačidlo, sa vygeneruje náhodné číslo, ktoré si zobrazíme.
Predpokladáme, že máte nainštalované Android Studio a všetky súčasti. Ak nie, nasledujte kapitolu Ako začať?
V tejto fáze by sme mali mať nainštalovanú Javu i Android Studio. Poďme programovať! Každá androidová aplikácia potrebuje mať v Android Studiu nový projekt. Ako ho vytvoriť?
Jedna z možností je využiť možnosť Start a new Android Studio project z úvodnej obrazovky Welcome to Android Studio.
Pri konfigurovaní projektu potrebujeme nastaviť:
V ďalšom kroku nastavíme minimálnu verziu platformy, ktorú bude naša aplikácia podporovať. Čím nižšia je podporovaná verzia, tým viac starších zariadení bude môcť spustiť vašu aplikáciu, a na druhej strane, vyššie verzie poskytujú pestrejšie možnosti, prípadne moderný dizajn a widgety. Voľba platformy je často marketingovým rozhodnutím, ale ak sa rozhodnete pre Android 4.0.3, ktorý vo februári 2015 podporuje vyše 90% zariadení, neurobíte chybu.
Ak nemáte k dispozícii požadovanú platformu (štandardne je nainštalovaný len Android 5.0), môžete ju doinštalovať po dokončení sprievodcu a zmeniť v aplikácii dodatočne.)
Aktivita v Androide predstavuje analógiu jedného okna, či „obrazovky“ z aplikácií na stolných počítačoch. Bežné aplikácie potrebujú aspoň jednu aktivitu, bez nich by totiž nemali prakticky žiaden spôsob komunikácie s používateľom.
V tejto úvodnej fáze vytvoríme prázdnu aktivitu.
Pre každú aktivitu potrebujeme nastaviť jej vlastnosti. V tejto fáze ponecháme návrhy od Android Studia.
Po dobehnutí sprievodcu vytvorí Android Studio kostru aplikácie, s ukážkovou aktivitou.
Na prvý pohľad vyzerá kostra projektu komplikovane: vidno viacero adresárov
(res
, java
) s mnohými položkami. Jadro projektu je však jednoduché, stačí
jednotlivým položkám priradiť význam.
Ako sme už spomenuli, aktivita predstavuje okno, či formulár alebo obrazovku známu z „veľkých“ aplikácií. V rámci projektu pozostáva aktivita z dvoch zložiek: z súboru rozloženia (layout file) vo formáte XML, ktorý definuje vizuál a rozloženie prvkov aktivity a zo skamaráteného Java kódu, ktorý aktivitu oživí.
Layouty aktivít reprezentované súbormi XML sa nachádzajú v priečinku res/layout
.
V kostre appky vidíme ukážkový súbor activity_main.xml
:
XML vyzerá na prvý pohľad divne, ak ste s ním nikdy nepracovali, o to viac, že využíva niektoré zriedkavejšie koncepty (napr. menné priestory). Uľahčíte si život, ak si prečítate niektorý z tutoriálov k písaniu XML.
<RelativeLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<TextView android:text="@string/hello_world" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
V layoutovom súbore vidíme dva elementy: komponent pre statický text (niekde nazývaný
label) reprezentovaný XML značkou <TextView>
, obsiahnutý v elemente
<RelativeLayout>
čo je štandardný algoritmus pre automatické rozkladanie komponentov
po displeji.
Každý widget má svoj názov v XML súbore layoutu, s ktorým je spárovaná trieda v jazyku Java.
Vďaka layoutom vieme naškálovať widgety na ľubovoľné rozmery displeja: od hodiniek až po televízory.
V Androide existuje štandardný relatívny layout, ktorý rozkladá widgety podľa pravidiel v duchu „nech tlačidlo OK je pod textovým políčkom s heslom“. Neskôr si ukážeme aj lineárny layout, ktorý ukladá widgety buď pod seba alebo vedľa seba, či mriežkový layout, ktorý ukladá widgety do tabuľky.
O chvíľu si navrhneme svoj vlastný layout, pretože ukážka je síce pekná, ale pre našu aplikáciu nevhodná.
Oživenie aktivity je reprezentovaná kódom v Jave. Android Studio nám predgenerovalo vzorovú aktivitu.
V celom materiáli sa predpokladá, že ovládate základy programovania v Jave. Ak nie, jestvuje množstvo online i offline materiálov či kurzov, ktoré vám ukážu správnu cestu.
Kód vyzerá nasledovne:
package sk.upjs.ics.android.kocka;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Vidíme, že aktivita je trieda dediaca od ActionBarActivity
, ktorá
prekrýva niektoré z jej metód. V ukážke máme nagenerované tri metódy, ale
namiesto ich vysvetľovania urobíme trik: ponecháme len onCreate()
a ostatné dve metódy zmažeme.
Tieto dve metódy slúžia na obsluhu lišty akcií, ktorú v našej aplikácii nebudeme potrebovať.
onCreate()
¶Aktivita sa tak zredukuje na:
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Metóda onCreate()
predstavuje analógiu konštruktora triedy, v ktorej
sa očakáva jej inicializácia. V tomto prípade sa zavolá rodičovská implementácia,
nasledovaná inicializáciou layoutu.
Metóda onCreate()
je prvá z metód životného cyklu aktivity, s ktorou
sa stretneme. Životný cyklus je veľmi dôležitý pre korektné chovanie
aktivity, pretože jeho nedodržanie povedie buď k padajúcej alebo neslušnej
appke.
setContentView()
, resources a prepojenie XML a kódu¶Volanie setContentView()
je miestom, kde sa magicky načíta XML súbor s
layoutom a widgety sa sprístupnia kódu. Čo však znamená podivná konštrukcia
R.layout.activity_main
?
setContentView(R.layout.activity_main);
Namiesto toho, aby ste v aktivite pátrali po akejsi ceste v súborovom systéme, z ktorej načítate XML, sa používa skvelá finta.
V každej aplikácii existuje špeciálna trieda s ľúbezným názvom R
, ktorá
sprístupňuje resources, teda všakovaké prostriedky aplikácie, ktoré
netvoria jej kód. Medzi resources patrí rozloženie aktivít, prípadné obrázky,
zvuky alebo pomocné súbory.
Vďaka triede R
sa nikdy nepomýlite pri uvádzaní názvu, pretože máte k
dispozícii automatické dopĺňanie kódu.
V našom prípade znamená konštrukcia R.layout.activity_main
odkaz na
layoutový súbor s názvom activity_main.xml
. Metóda setContentView
ho
automaticky nájde a spracuje, bez toho, aby sme pohli prstom.
Načítanie XML súboru sa často nazýva inflating (nafúknutie), pretože z optimalizovaného binárneho súboru sa nafúkne, ako balón, XML súbor, a z neho jednotlivé widgety.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="sk.upjs.ics.android.kocka" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
K manifestu sa budeme priebežne vracať. Zatiaľ vidíme, že obsahuje jedinú
aktivitu, kde je uvedený názov triedy (.MainActivity
) a niektoré
ďalšie špecifiká.
Míľnik: spustenie aplikácie ====================´====== Ešte sme nenaprogramovali ani riadok kódu, ale už teraz môžeme spustiť aplikáciu. Samozrejme, nemá nič spoločné s cieľom, ale vieme si overiť korektnú funkčnosť Android Studia a prepojenie s emulátorom.
Nezabudnite si nainštalovať alternatívny emulátor Genymotion a prepojiť ho s Android Studiom.
Keď sme pochopili základné princípy aplikácie, dopracujme kocku, teda navrhnime používateľské rozhranie a doprogramujme obslužný kód.
...či skôr rozloženie widgetu. Používateľské rozhranie bude pozostávať z jediného widgetu typu tlačidlo, na ktoré sa bude dať nielen klikať, ale aj pozerať výsledok.
V layoutovom súbore odstráňme ukážkový TextView
a namiesto neho
vložme tlačidlo reprezentované elementom <Button>
.
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:id="@+id/diceButton"
android:layout_width="144dp"
android:layout_height="144dp"
android:text="Roll"
android:onClick="onDiceButtonClick"
android:background="@android:color/holo_blue_dark"
android:textColor="@android:color/white"
android:textSize="28sp"
android:gravity="center"
android:layout_centerInParent="true"
/>
</RelativeLayout>
V tlačidle nastavme nasledovné vlastnosti reprezentované atribútmi v
XML, ktoré patria do menného priestoru s prefixom android
.
Widget je vo väčšine prípadov povinný uviesť svoj textový identifikátor, čo platí najmä v prípade, že sa naň chceme odkázať v kóde. Má špecifický formát:
@+id/diceButton
Čítame to zľava doprava: @
znamená odkaz, či referenciu na id
entifikátor,
kde za lomkou uvedieme ľubovoľný reťazec. A čo znamená plus? Určuje deklaráciu
identifikátora, pretože bez nej by si kompilátor myslel, že sa odkazujeme
na existujúci identifikátor, čo nie je pravda.
Identifikátory widgetov sú totiž unikátne vzhľadom na celý projekt. Je preto vhodné ustanoviť konzistentnú mennú konvenciu. V praxi vidieť príklady:
diceButton
: predpona popisuje názov, prípona určuje typ widgetubutton_dice
: použité sú malé písmená s podtržníkom s vymeneným poradímbtnDice
: stará konvencia z Delphi využívajúca prefixy pre názvy komponentov.Atribúty android:layout_width
a android:layout_height
udávajú šírku a výšku
tlačidla v rámci aktivity. V tomto prípade využijeme odhadnutý rozmer
144 dp, čo sú zhruba 3 centimetre.
Merná jednotku dp
v Androide reprezentuje device independent pixel teda
bod nezávislý od hustoty displeja zariadenia. Na zariadení s hustotou 160dpi
je 1 dp rovnako veľký ako 1 px, a na odlišných hustotách sa veľkosť primerane
naškáluje. Ak potrebujete definovať veľkosť ľubovoľného komponentu, dp
je
prakticky jediná odporúčaná merná jednotka, ktorý zaručí približne rovnakú
veľkosť na všetkých zariadeniach. Typicky platí, že 48 dp má na zariadení
veľkosť 7--10 mm, čo je najmenšia odporúčaná veľkosť, na ktorú dokáže používateľský prst ešte rozumne ťapnúť.
Viac informácií o merných jednotkách možno nájsť na StackOverflow.com.
V iných situáciách sa využíva konštanta match_parent
,
ktoré znamenajú, že rozmer má vyplniť celú šírku, resp. výšku rodiča.
Alternatívnou konštantou je wrap_content
, ktorá nastaví primeraný rozmer,
teda štandardnú výšku, alebo šírku dostatočnú na zobrazenie textu a podobne.
Text na tlačidle je jednoduchý: využijeme atribút text
.
android:text="Roll"
Atribút onClick
predstavuje názov metódy v Java kóde, ktorá sa zavolá
po kliknutí na tlačidlo. V našom prípade, kde sme určili
android:onClick="onDiceButtonClick"
sa čaká, že v kóde bude metóda
s nasledovnou hlavičkou:
public void onDiceButtonClick(View v)
V rámci tejto metódy obslúžime kliknutie na tlačidlo, ale o tom o chvíľu.
Hodnota android:background
nastaví farbu pozadia tlačidla. V tomto prípade
využijeme odkaz na systémový resource. Android totiž definuje viacero
konštánt (nielen pre farby), ktoré môžeme využiť. Reťazec @android:color/holo_blue_dark
čítame zľava doprava nasledovne: @
hovorí, že ide o odkaz (referenciu),
android
indikuje systémovú konštantu. color
reprezentuje resource typu
farba a text za lomkou je systémový názov farby tmavomodrá z palety nazvanej Holo.
Farba popredia (textu) sa riadi podobnou filozofiou. V tomto prípade využijeme systémový odkaz na bielu farbu.
Android Studio ponúka automatické dopĺňanie vrátane náhľadu konkrétnej farby.
Veľkosť textu môžeme nastaviť atribútom textSize
. U nás sme zvolili veľkosť
28 sp.
Merná jednotka sp reprezentuje scaled point, čo je veľkosť, ktorá je
približne rovnaká na každom zariadení bez ohľadu na rozlíšenie displeja, jeho
hustotu či fyzické rozmery, ale berie do úvahy systémové nastavenie veľkosti
písma. Veľkosť písma v Androide sa vždy odporúča uvádzať v sp
.
Atribút gravity
pomerne prekvapivo nastavuje priťahovanie (gravitáciu)
obsahu tlačidla, teda textu, k okrajom. Nastavená hodnota center
umiestni text do vodorovného i zvislého stredu.
Nepleťme si atribút gravity
s podobným layout_gravity
, ktorý centruje
widget v rámci rodičovského layoutu.
Ak nastavíme android:layout_centerInParent="true"
, vycentrujeme
widget v rodičovi.
Vrhnime sa na úpravu hlavnej aktivity. Plán práce bude jednoduchý
onDiceButtonClick()
,
ktorú musíme doimplementovať.Na hod kockou vytvoríme pomocnú metódu, ktorá využíva klasickú javácku
triedu java.util.Random
a jej metódu nextInt()
, ktorou vrátime číslo medzi nulou a parametrom.
private int getRandomRoll() {
Random random = new Random();
return random.nextInt(5) + 1; // (0--5) + 1 = 1--6
}
Ak chceme nastavovať text tlačidla, potrebujeme mať objekt, na ktorom to
dosiahneme. A ako sme spomínali pri layoutových súboroch, každý widget
je reprezentovaný jednak elementom v XML a jednak triedou v jazyku Java.
V prípade tlačidla to znamená, že máme element <Button>
a triedu
android.widget.Button
.
Odkiaľ získame objekt pre toto tlačidlo?
Vieme, že metóda setContentView()
nafukuje XML súbor a spracováva ho.
Presnejšie to znamená, že za oponou sa nafúkne
XML do hierarchie widgetov, ktorá sa dostane pod správu objektu aktivity.
Získať ľubovoľný hotový, nafúknutý a nakonfigurovaný objekt z tejto hierarchie
môžeme volaním metódy findViewById()
findViewById()
.¶Metóda findViewById()
očakáva identifikátor widgetu a vráti jeho objekt.
Identifikátor je kvôli korektnosti reprezentovaný premennou v resource triede
R
a vracia Object
, ktorý následne pretypujeme na korektný typ widgetu.
Medzi androiďácky folklór patrí inicializácia widgetov v metóde onCreate()
.
Všetky widgety, ktoré chceme využívať v kóde, vytiahneme pomocou metódy
findViewById()
a priradíme ich do inštančných premenných. Ukážka pre náš
kód:
public class MainActivity extends ActionBarActivity {
private Button diceButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
diceButton = (Button) findViewById(R.id.diceButton);
}
...
}
Volanie metódy findViewById()
je pri rozsiahlych mnohowidgetových
aktivitách výpočtovo náročné. To je dôvod, prečo sa deje raz, pri vytvorení
inštancie aktivity, teda v metóde onCreate()
.
Nastavenie textu je potom malina. Dopracujeme očakávanú metódu
pre obsluhu kliku tlačidla, získame náhodný hod a text na tlačidle
určíme cez metódu setText()
. Samozrejme, Java je silne typovaný
jazyk, čo je dôvod, prečo musíme previesť číslo na reťazec pomocou
folklórnej metódy Integer.toString()
.
public void onDiceButtonClick(View view) {
int randomRoll = getRandomRoll();
diceButton.setText(Integer.toString(randomRoll));
}
Kompletný kód aktivity len zhŕňa, čo sme vytvorili
package sk.upjs.ics.android.kocka;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.*;
import android.widget.Button;
import java.util.Random;
public class MainActivity extends ActionBarActivity {
private Button diceButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
diceButton = (Button) findViewById(R.id.diceButton);
}
public void onDiceButtonClick(View view) {
int randomRoll = getRandomRoll();
diceButton.setText(Integer.toString(randomRoll));
}
private int getRandomRoll() {
Random random = new Random();
return random.nextInt(5) + 1; // (0--5) + 1 = 1--6
}
}
Aplikácia je hotová a môžeme ju spustiť! Klikajte na tlačidlo a sledujte, ako sa číslo náhodne mení.
Blahoželám, vaša prvá aplikácia je hotová! Čo s ňou ďalej?
\app\build\outputs\apk\app-debug.apk
sa nachádza súbor
.apk
reprezentujúci hotovú aplikáciu, ktorý môžete cez USB kábel
napchať do telefónu, alebo zavesiť na web a nechať ho stiahnuť používateľmi.
Tento postup však vyžaduje vypnutie nepodpísaných zdrojov aplikácií.