Če aplikacije iz prejšnjega dela nimate, si jo lahko naložite s spletne strani https://github.com/koncinar/moj-mikro-android
Pogledi
Vsaka vizualna komponenta aplikacije je v osnovi pogled – to pomeni, da razširja razred android.view.View ter ima metode, ki omogočajo prikaz te komponente na zaslonu naprave. Se pa te komponente v grobem delijo na dva dela:
1. razporeditve: to so pogledi, ki vsebujejo druge poglede (razporeditve ali končne komponente) in jih razporejajo po zaslonu, na primer v vrsto, razpredelnico itd.
2. končne komponente: to so konkretne komponente, ki jih vidimo na zaslonu, kot so besedilo, okence za vnos besedila, gumb, drsnik, fotografija itd.
V prvem delu Male šole smo že spoznali razporeditev LinearLayout, ki komponente razporedi v vodoravno ali navpično vrsto. Prav tako smo spoznali tri končne poglede: TextView, ki prikazuje besedilo, EditText, v katerega lahko vpišemo besedilo, ter Button, ki je seveda gumb. LinearLayout je te komponente razporedil v navpično vrsto in tako smo dobili našo preprosto aplikacijo.
Zdaj pa bomo našo aplikacijo močno razširili ter ob tem konkretno spoznali nove razporeditve in končne komponente.
Razporeditev TabLayout
Ta razporeditev je zelo uporabna za kompleksnejše aplikacije, saj na vrhu napravi zavihke, s katerimi preklapljamo med posameznimi stranmi naše aplikacije. To razporeditev lahko zasledimo v mnogih aplikacijah na Android Marketu.
Je pa ta razporeditev po drugi strani najtežja za uporabo. Razporeditev mora biti korenska, torej je ne moremo uporabiti znotraj druge razporeditve, vsebino zavihkov pa moramo določiti v kodi. Zdaj implementirajmo to v našo aplikacijo.
Ustvarimo torej novo datoteko s postavitvijo, ki bo vsebovala razporeditev TabLayout. V Idei z desnim gumbom kliknimo na mapo layout (znotraj mape res) in izberimo New > Layout resource file. Za ime datoteke vpišimo moj_mikro_android, kliknimo ok in Idea nam sama ustvari .xml datoteko z razporeditvijo. Privzeto nam že zapiše razporeditev LinearLayout, kar pa nam ne ustreza. Zato preimenujmo korensko značko v TabHost. Širino in višino že ima določeno, dodati moramo le še natančno takšen id: android:id="@android:id/tabhost" (oznaka @android:id predstavlja vnaprej določene Android identifikatorje).
Znotraj značke TabLayout moramo dodati značko LinearLayout, ki zasede celotno širino in višino ter ima lastnost android:orientation=“vertical“, nato pa znotraj LinearLayouta dodamo znački TabWidget in FrameLayout. Prva predstavlja zavihke, zato ji določimo identifikator android:id=“@android:id/tabs“, zasede naj celotno širino, v višino pa naj zgolj objame vsebino. Druga značka predstavlja vsebino zavihka, zato ji določimo identifikator android:id=“@android:id/tabcontent“, zasede pa naj celotno širino in višino.
Zdaj moramo v Java kodi ustvariti zavihke in njihove vsebine. Znotraj mape src kliknimo z desnim gumbom na paket com.example in izberimo New > Android Component. Odpre se nam okno za ustvarjanje nove aktivnosti. Pri Name: napišimo MojMikroAndroidActivity (ime Java razreda za to aktivnost), za Kind: izberimo Activity (to pomeni, da bomo ustvarili aktivnost), pri Label: napišimo Moj Mikro Android (to bo ime aktivnosti v zagonskem meniju naprave) ter obkljukajmo možnost Mark as startup Activity (to pomeni, da bo ta aktivnost prikazana v zagonskem meniju naprave). Ustvaril in odprl se nam je javanski razred, ki skrbi za ozadje prikaza elementov. Vendar pa TabLayout potrebuje posebno aktivnost, zato extends Activity spremenimo v extends TabActivity (ne pozabimo, morebitne napake v kodi odpravljamo s pritiskom tipk Alt+Enter).
Kot smo napravili že v prvem delu Male šole, moramo tudi zdaj v kodi podati povezavo do datoteke s postavitvijo:
setContentView(R.layout.moj_mikro_android);.
Zdaj je vse pripravljeno, da dodamo svoj prvi zavihek. To napravimo z naslednjo kodo znotraj metode onCreate:
TabHost tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("my_tab")
.setIndicator(getString(R.string.my_info))
.setContent(new Intent(this, MyActivity.class)));
Ta koda doda nov zavihek z id-jem my_tab, na katerem bo pisalo besedilo, prebrano iz R.string.my_info (ne pozabimo v strings.xml dopisati besedila za zavihek, na primer »Moji podatki«), vsebina zavihka pa bo aktivnost MyActivity – kar smo ustvarili v prvem delu Male šole.
Zdaj moramo le še v datoteki AndroidManifest.xml popraviti značko <activity android:name=“MyActivity“ …, znotraj katere moramo izbrisati celotno značko intent-filter, saj želimo, da je edina zagonska aktivnost aplikacije nova aktivnost z zavihki. Moramo pa zdaj popraviti tudi Idejin zaganjalnik naše aplikacije, da bo pognal aktivnost z zavihki – v Idei kliknemo Run > Edit Configurations, nato pa v Android Application > MojMikroAndroid pri Launch izberemo com.example.MojMikroAndroidActivity.
Zdaj lahko poženemo našo aplikacijo in vidimo en zavihek z vsebino. Zdaj pa dodajmo še enega.
Razporeditev ListView
To je zelo preprosta razporeditev, ki prikaže seznam podatkov. Zaradi enoličnosti prikaza nam ni treba določiti ničesar drugega kot to, kako bo bila videti posamezna vrstica. Če želimo prikazati na primer seznam regij v Sloveniji, napravimo najprej novo datoteko s postavitvijo – z desnim gumbom kliknemo na mapo layout in izberemo New > Layout resource file, napišemo ime regions_list_item in v ustvarjeni datoteki samodejno generiran LinearLayout zamenjamo z:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16dp"/>
To pomeni, da bo vsaka vrstica seznama zgolj prikazovala besedilo, ki bo veliko 16 dp, okoli njega pa naj bo še 10 dp prostora – da ne bo majhno in natlačeno.
Nadalje moramo ustvariti seznam regij. Odpremo datoteko strings.xml in v značko resources dodamo značko string-array, ki predstavlja seznam besedil:
<string-array name="all_regions">
<item>Dolenjska</item>
<item>Gorenjska</item>
…
</string-array>
Zdaj moramo povezati ta seznam s prikazom. Z desnim gumbom kliknemo na paket com.example in izberemo New > Android Component. Ustvarimo novo aktivnost z Name: RegionsListActivity, Kind: Activity, Label in Mark as setup activity pa pustimo prazno oziroma neobkljukano. To nam ustvari in odpre aktivnost, ki pa jo moramo spet prirediti – namesto extends Activity moramo napisati extends ListActivity. Zdaj pa povejmo tej aktivnosti, kako naj napolni podatke. V metodo onCreate dodajmo naslednjo kodo:
setListAdapter(new ArrayAdapter(this,
R.layout.regions_list_item,
getResources().getStringArray(R.array.all_regions)));
R.layout.regions_list_item je povezava do postavitve, ki smo jo ustvarili prej, getResources().getStringArray(R.array.all_regions) pa nam iz datoteke strings.xml prebere seznam regij z imenom all_regions.
Pogled je pripravljen, treba je samo še dodati nov zavihek. V datoteki MojMikroAndroidActivity.java v metodi onCreate dodamo naslednjo kodo:
tabHost.addTab(tabHost.newTabSpec("regions_tab")
.setIndicator(getString(R.string.regions))
.setContent(new Intent(this, RegionsListActivity.class)));
Pojasnila niso potrebna, saj je koda enakovredna kodi za prvi zavihek, tako da lahko zdaj ponovno poženemo aplikacijo in že vidimo drugi zavihek.
Razporeditev TableLayout
Ta razporeditev zloži elemente v razpredelnico, torej v vrstice in stolpce, kar ustvari pregledno in enakomerno postavitev. V primeru polj, ki jih je treba izpolniti, je ta razporeditev uporabna, saj so lahko na ta način naslovi polj v prvem stolpcu, vnosna polja pa v drugem, kar bo pomenilo, da bodo vsa vnosna polja poravnana med seboj. To razporeditev uporabimo tako, da najprej zapišemo značko TableLayout in vanjo zapišemo eno ali več značk TableRow, ki predstavljajo vrstice v razpredelnici. V te značke pa potem zapišemo posamezne elemente (na primer TextView, Button itd.). Vsak od teh elementov se bo nato pojavil v svojem stolpcu.
Pa poglejmo, kako je to videti v praksi, in sicer bomo uredili zavihek Osebni podatki. Odprimo datoteko main.xml, ki predstavlja postavitev tega zavihka. Najprej korensko značko LinearLayout preimenujmo v TableLayout ter odstranimo lastnost android:orientation="vertical", ki je ta razporeditev ne potrebuje. Nato pa prva dva podelementa obkrožimo z značko TableRow, druga dva pa prav tako, da dobimo takšno obliko zapisa:
<TableLayout … >
<TableRow>
<TextView … />
<EditText … />
</TableRow>
<TableRow>
<Button … />
<TextView … />
</TableRow>
</TableLayout>
Tako smo dobili razpredelnico z dvema vrsticama (dve znački TableRow) in dvema stolpcema (v obeh značkah TableRow sta dva elementa). V predogledu že lahko vidimo razporeditev v razpredelnici, vidimo pa tudi, da je drugi stolpec čisto ozek, tako da vnosnega polja skoraj ni. To je posledica tega, da se stolpci vedno zmanjšajo na najmanjšo možno širino, v drugem stolpcu pa sta le prazna EditText in TextView. Ker mi želimo, da se drugi stolpec razširi čez celotno preostalo širino, bomo znački TableLayout dodali lastnost android:stretchColumns="1". Sem zapišemo z vejico ločen seznam indeksov stolpcev, za katere želimo, da se raztegnejo. Indeksi se začnejo z 0, zato moramo za raztegovanje drugega stolpca napisati št. 1.
V predogledu že lahko vidimo boljšo postavitev stolpcev, lahko pa zdaj tudi preizkusimo aplikacijo v emulatorju, preden se lotimo končnih komponent.
Komponenta TextView
To je najbolj osnovna komponenta, brez katere si aplikacije praktično ne moremo predstavljati. Namenjena je prikazu besedila. V datoteko s postavitvijo (na primer main.xml) jo dodamo tako, da napišemo značko <TextView … />, besedilo te komponente lahko določamo na dva načina, odvisno od statičnosti/dinamičnosti:
1. Če je besedilo statično, ga določimo tako, da v datoteki s postavitvijo tej znački dodamo lastnost android:text="..." s konkretnim besedilom oziroma še bolje s povezavo do ustreznega besedila v datoteki strings.xml.
2. Če pa je besedilo dinamično – to pomeni, da ga določamo v odvisnosti od kakšnih spremenljivk –, pa ga določimo tako, da v datoteki s postavitvijo znački določimo identifikacijo (na primer android:id="@+id/message"), nato pa v Java kodi poiščemo to komponento in ji določimo besedilo: ((TextView) findViewById(R.id.message)).setText("...").
Oba načina smo že spoznali in uporabili v prvem delu Male šole.
Komponenta EditText
Ta komponenta je namenjena vnosu besedila in je v bistvu razširitev komponente TextView. Uporabnik bo na to komponento kliknil in vanjo vpisal svoje besedilo. V datoteko s postavitvijo jo dodamo tako, da napišemo značko <EditText … /> (ne pozabimo identifikatorja, na primer »name_input«), vsebino te komponente preberemo z metodo ((EditText) findViewById(R.id.name_input)).getText(). Če pa želimo, da aplikacija vnaprej ali pa naknadno napiše kakšno besedilo v to komponento, to storimo popolnoma enako kot s komponento TextView.
Komponenta Button
Tudi to je ena od komponent, brez katerih ni aplikacij, saj je osnova za sodelovanje z uporabnikom. Tudi ta komponenta je razširitev komponente TextView, njen tekst pa predstavlja besedilo, ki se prikaže na gumbu. V datoteko s postavitvijo jo dodamo z značko <Button … />. Gumb pa je neuporaben brez akcije, ki se izvede ob pritisku nanj, in to dodamo v Java kodi:
findViewById(R.id.ok_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
…
}
});
Opazimo lahko, da ta komponenta ne potrebuje pretvorbe ((Button) findViewById(R.id.ok_button)), to pa zato, ker je metoda setOnClickListener že na razredu View, kar v bistvu pomeni, da lahko vsakemu vizualnemu elementu dodamo metodo, ki se zažene ob kliku nanj.
Akcijo ob kliku na gumb napišemo znotraj metode onClick.
Komponenti RadioButton in RadioGroup
SLIKA: „RadioButton.png“
Ti komponenti nam omogočata, da uporabnik izbere eno od podanih možnosti. Vsak RadioButton predstavlja eno od možnosti izbire, vse značke RadioButton pa se morajo nahajati znotraj značke RadioGroup, ki pove, da lahko uporabnik izbere zgolj eno od teh opcij – podznačk.
Dodajmo to komponento zdaj v našo kodo. V datoteki main.xml dodajmo novo vrstico razpredelnice, in sicer na drugo mesto. Napišimo torej novo značko TableRow, vanjo dodajmo komponento TextView z besedilom »Spol:«, v drugi stolpec (torej druga značka v trenutni TableRow) pa dodajmo naslednjo kodo:
<RadioGroup android:id="@+id/gender_radio_group"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton android:id="@+id/gender_radio_male"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="moški"/>
<RadioButton android:id="@+id/gender_radio_female"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ženski"/>
</RadioGroup>
To bo v drugi stolpec dodalo možnost izbire spola. Z lastnostjo android:orientation="horizontal" povemo, da naj bodo te možnosti predstavljene v vodoravni vrsti. Vsem trem komponentam smo določili identifikacijo, da bomo lahko v Java kodi nato prebrali besedilo izbrane možnosti:
int checkedGenderId = ((RadioGroup) findViewById(R.id.gender_radio_group)).getCheckedRadioButtonId();
CharSequence selectedGender = ((RadioButton) findViewById(checkedGenderId)).getText();
Komponenta Spinner
Ta komponenta da uporabniku, podobno kot prejšnja, možnost izbire ene od možnosti, vendar je ta bolj uporabna, če je možnosti več in bi prikaz vseh naenkrat zavzel preveč prostora na zaslonu. Ta komponenta namreč prikazuje le izbrano možnost, seznam vseh pa dobimo, če kliknemo nanjo.
Dodajmo to komponento za izbiro regije uporabnika (seznam regij smo že definirali). Najprej dodamo novo vrstico na predzadnje mesto – pred vrstico z gumbom in tekstom. Vanjo na prvo mesto dodamo komponento z besedilom »Regija:«, na drugo mesto pa komponento:
<Spinner android:id="@+id/regions_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Vsebine te komponente ne moremo določiti v datoteki s postavitvijo, ampak jo moramo določiti v Java kodi. Odprimo datoteko MyActivity.java in na konec metode onCreate dodajmo:
ArrayAdapter<CharSequence> regionsAdapter =
ArrayAdapter.createFromResource(this, R.array.all_regions, android.R.layout.simple_spinner_item);
regionsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
((Spinner) findViewById(R.id.regions_spinner)).setAdapter(regionsAdapter);
Ta koda je sicer videti zelo kompleksna, vendar sta v njej le dve spremenljivki – prva je R.array.all_regions, ki je povezava do seznama regij, določenega v datoteki strings.xml, druga pa je R.id.regions_spinner, ki je povezava do komponente, kateri moramo določiti seznam.
Komponenta CheckBox
To je zelo preprosta komponenta, ki uporabniku omogoča zgolj da/ne izbiro. Če komponenta ni izbrana in uporabnik klikne nanjo, postane izbrana in obratno.
Dodajmo jo v našo aplikacijo. V main.xml dodajmo vrstico na predzadnje mesto in v prvi stolpec dodajmo komponento TextView z besedilom »Verjameš v nezemljane:«, v drugi stolpec pa zapišimo komponento:
<CheckBox android:id="@+id/aliens_checkbox"/>
Ta komponenta ne potrebuje drugih lastnosti kot identifikacijo, lahko pa bi ji dodali še lastnost android:text="...", kar bi izpisalo besedilo poleg polja za označitev – to je uporabno, ko bomo imeli več teh komponent na kupu.
Ali je ta komponenta izbrana ali ne, potem v kodi izvemo na naslednji način:
((CheckBox) findViewById(R.id.aliens_checkbox)).isChecked();
Komponenta SeekBar
Ta komponenta uporabniku omogoča izbiro števila med 0 in n. Komponenta je videti kot drsnik, ki ga lahko uporabnik premika levo ali desno in s tem določi želeno vrednost. Ker je to početje dokaj nenatančno, je dobro, da se drsnik razteza čez čim večjo površino, zato ga bomo raztegnili čez oba stolpca. Napravimo torej dve novi vrstici na predzadnjem mestu. V prvo dodajmo v prvi stolpec komponento TextView z besedilom »Izkušnje z Androidom:«, v drugi stolpec pa dodajmo še eno komponento TextView brez besedila, ki bo namenjena prikazu izbrane izkušnje:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/android_experiences_display"/>
V drugo vrstico razpredelnice pa dodajmo samo eno komponento:
<SeekBar android:id="@+id/android_experiences_seek_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_span="2"/>
Lastnost android:layout_span="2" pove, da naj se ta komponenta razteza čez dva stolpca, kar v našem primeru pomeni čez ves zaslon. Zdaj pa moramo določiti še maksimalno vrednost drsnika in dodati možnost, da se prikaže izbrana možnost.
Najprej v datoteko strings.xml, podobno kot smo določili seznam regij, dodamo še seznam izkušenj: »Prvič slišim«, »Imam napravo«, »Napredni uporabnik«, »Razvijalec začetnik« in »Napredni razvijalec«. Seznamu dajmo identifikacijo »all_android_experiences«.
Zdaj pa v datoteki MyActivity.java znotraj metode onCreate najprej preberimo seznam izkušenj, nato pa določimo funkcionalnost drsnika:
final String[] all_experiences = getResources().getStringArray(R.array.all_android_experiences);
SeekBar experiencesSeekBar = (SeekBar) findViewById(R.id.android_experiences_seek_bar);
experiencesSeekBar.setMax(all_experiences.length – 1);
experiencesSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
((TextView) findViewById(R.id.android_experiences_display)).setText(all_experiences[i]);
}
public void onStartTrackingTouch(SeekBar seekBar) { }
public void onStopTrackingTouch(SeekBar seekBar) { }
});
Pa pojasnimo malo zgornjo kodo. V vrstici experiencesSeekBar.setMax(all_experiences.length - 1); določimo maksimalno vrednost drsnika, ki naj bo enaka maksimalnemu indeksu seznama izkušenj – ker se indeksi začnejo z 0, je to dolžina seznama minus 1. Z metodo experiencesSeekBar.setOnSeekBarChangeListener nato dodamo razred, ki posluša za spremembami drsnika, v implementaciji tega razreda pa v metodi onProgressChanged določimo akcijo, ki se izvede, ko se vrednost drsnika spremeni. Drugi parameter, ki ga ta metoda sprejme, je izbrana vrednost, tako da lahko v prikazovalnik besedila z identifikacijo android_experiences_display zapišemo ustrezno besedilo – all_experiences[i].
Zdaj lahko znova poženemo našo aplikacijo in preverimo, kaj smo napravili.
Zaključek
Drugo poglavje Male šole razvijanja Android aplikacij je tako zaključeno. Aplikacijo smo zelo razširili z raznimi komponentami, tako s preprostimi kot tudi zahtevnimi. Z vsem tem postaja aplikacija kompleksnejša, hkrati pa tudi uporabnejša.
Naslednjič bomo poskrbeli še za shranjevanje in nalaganje podatkov – da jih ne bo treba vstavljati ročno vsakič, ko aplikacijo poženemo, posvetili pa se bomo tudi oblikovanju, da bo naša aplikacija še bolj prijetna na pogled.
Do takrat pa ste vabljeni, da se še sami poigrate s kodo, poskušate povezati komponente, ki smo jih danes dodali, morda izpisati bolj podrobno sporočilo uporabniku, napolnjeno še s preostalimi podatki itd. Izvorno kodo aplikacije iz tega članka boste spet lahko našli na spletnem naslovu www.logica.pro/mojmikro, kjer bodo na voljo tudi dodatni posnetki zaslona, pojasnila, nasveti ter prostor za vaša vprašanja in pojasnila.
Moj mikro, april 2012 | Rok Končina