Online Android szakkör (DKRMG)
Ez a szótárfüzet alkalmazás projekt második leckéje. Gyorsan összegeznénk, hogy miként fest majd a kész program, mennyit sikerült ebből megvalósítani az előző leckében, illetve milyen munkát tartogat a mostani segédanyag.
Az általunk tervezett program négy Activityből áll.
Az előző leckében majdnem teljesen elkészítettük a menünket, írtunk egy névjegy (about) activityt, illetve lefektettük a SzavakActivity alapjait. Utóbbi ablak egészen pontosan ott tart, hogy az általunk beírt szavakat szépen kilistázza a Szo típusú tömbünkből.
A programunk a mostani állapotában alig pár szót használ, amit a program készítésekor kellett beírnunk a Java kódunkba. Ez nem túl kényelmes, és szélesebb körben gyakorlatilag használhatatlanná teszi az alkalmazást. Tehát ma:
A külső fájlok használata és a kvíz elkészítése a következő leckékre marad.
Biztos nektek is feltűnt, hogy jelenleg ahhoz, hogy új szópárokat adhassunk hozzá az
alkalmazáshoz, közvetlenül a Java forráskódot kell szerkesztgetni, és
mindig újra el kell készíteni (fordítani) az alkalmazást a zöld "Futtatás" gombra kattintva.
Ez egy se nem túl kellemes sem nem gyors, és hosszú távon gyakorlatilag használhatatlan megoldás.
Sokkal jobb lenne, ha az alkalmazás futása közben lehetne beírni új
szavakat, amik rögtön meg is jelennének a listában illetve később már ezeket is
visszakérdezné az alkalmazás. Ehhez kell egy felület, ahol be lehet írni az új szópárokat,
valamint a Java kódot is meg kell írni, ami elvégzi a hozzáadást. Csináljuk is meg!
Először a layoutot csináljuk meg, mégpedig oda, ahol a listát is megjelenítjük (activity_szavak.xml):
A feladat elvégzéséhez próbáld meg a SzavakActvity layoutját a képen ábrázolthoz hasonlóra átalakítani! (Segítség a kép alatt.)
ListView
-t a képernyő aljához
rögzítsük, de foglalja el a képernyő nagyobb részét. Az új komponenseket a lista
fölé, az üres részre tegyük, ahogy a képen is látszik!EditText
(A Text Fields kategória alatt, Plain
text néven találjátok)hun
, az angolénak
az eng
, és a gombnak az add
azonosítót adtuk, így fogunk
rájuk hivatkozni.Sajnos több éves pályafutásunk alatt még nem találkoztunk olyan layout szerkesztővel, ami igazán egyszerűen segíti a megálmodott layout kialakítását. Ez alól az Android Studio szerkesztője sem kivétel, bár általában elég jól használható.
A kalandvágyóbbak megpróbálkozhatnak maguk összeállítani a fenti layoutot. Ehhez az egérrel kattintgatás mellett érdemes az
adott komponens layout:alignComponent
tulajdonságát állítgatni (lásd kép).
Itt az egyes sorok saját:másik
felépítésűek, azaz például a top:bottom = hun
azt jelenti, hogy az adott komponens tetejét (top) a hun
azonosítójú
komponens aljához (bottom) igazítja.
A kevésbé kalandvágyóbbaknak előkészítettünk két layoutot: két nyelv, három nyelv. Ezeket le kell tölteni (jobbklikk -> Link mentése másként), és felülírni velük a projekt mappában az app/src/main/res/layout/activity_szavak.xml fájlt.
Ebben a következő azonosítókat használtuk:
hun
eng
ger
add
szolista
findViewById(R.id.…)
az onCreate-ben.
Ahhoz hogy el tudjuk menteni a beírt szavakat, ki kell tudnunk majd olvasni a
szövegmezők tartalmát. Ilyet már sokszor csináltatok, de szabad puskázni a 3.
leckéből, ott a gondolt számot kezeltük hasonlóan.Most nézzük meg, hogy minek is kéne történnie a gomb megnyomásakor.
Ezeket mind a gomb onClick-jében kell megírni!
Lépésről lépésre:
String magyar = hun.getText().toString();
ujSzo
!
Szo ujSzo = new Szo(magyar, angol);
szavak = new Szo[] {szo1, szo2, szo3};
akkor egy 3 egység hosszú tömböt csináltunk, amibe nem fér bele egy
negyedik szó! Remélhetőleg ezen a ponton mindenkiben felmerül a kérdés: ha a tömbökbe ilyen nehéz új elemeket beilleszteni, törölni pedig elegánsan és gyorsan szinte lehetetlen, akkor milyen más megoldás van helyette?
ArrayList
A jó hír, hogy létezik Java-ban egy a tömbökhöz hasonló, ArrayList
nevű adatszerkezet,
amit pont az ilyen problémákra találtak ki. Az ArrayList
jó tulajdonságait
átvette a tömböktől, a rosszakat pedig igyekezett a legjobban áthidalni. Nézzük meg, mit
tud:
ArrayList<Szo> szavak = new ArrayList<Szo>();
Ez egy kicsit eltér a tömbök deklarációjától, valószínűleg egy kis magyarázatra
szorul. Nézzük meg magát a változódeklarációt:
ArrayList<Szo> szavak;
Látszik, hogy a változó neve továbbra is egyszerűen szavak (bármi más is lehetne) a
típusa viszont furcsa: két részből áll, az ArrayList
megmondja, hogy ez
egy lista lesz, utána a két kacsacsőr között pedig azt kell
megmondani, hogy milyen típusú elemei lehetnek. A létrehozása innentől egyértelmű,
és a táblázat alapján talán még könnyebb megérteni:
Típus | Név | Létrehozás | ||
---|---|---|---|---|
Egy változó | Szo | egyDarabSzo | = new | Szo(); |
Fix méretű tömb | Szo[] | szavakTomb | = new | Szo[10]; |
Változó méretű lista | ArrayList<Szo> | szavakLista | = new | ArrayList<Szo>(); |
Szo szo1 = new Szo("kutya", "dog");
szavak.add(szo1); // hozzáadás egyetlen paranccsal!
Szo azOtosIndexuSzo = szavak.get(5);
int index = 0; // így az első elemet fogja törölni. Itt is a nullás indexű elem az első!
szavak.remove(index); // kiválasztott elem törlése egy paranccsal!
Láthatjuk, hogy a bonyolultabb műveleteknél sokkal kényelmesebb listákat használni. Írjuk is át az alkalmazásunkat listásra!
szavak
változó típusát, ezúttal
Szo[]
helyett ArrayList<Szo>
legyen!szavak = new ArrayList<Szo>();
szavak.add(new Szo("kutya", "dog"));
szavak.add(new Szo("macska", "cat"));
// stb…
szavak.add(ujSzo);
notifyDataSetChanged()
belső függvényt meghívjuk a változtatások után. Tehát: ahhoz, hogy ezt használhassuk, először az adaptert el kell tárolni
egy külön változóba, ami mindenhonnan elérhető:
ArrayAdapter<Szo>
, neve adapter
legyen.ArrayAdapter<Szo> adapter = new ArrayAdapter<Szo>(this, android.R.layout.simple_list_item_1, szavak);
és töröljük ki az elejéről az ArrayAdapter<Szo>
részt.
Figyeld meg, hogy megváltozott az "adapter" változó színe! Ez azért van,
mert eddig úgynevezett lokális változó volt, azaz csak a
függvényen belül létezett, mostantól pedig az Activity
globális változóját jelenti (amit az előbb fent deklaráltunk). Az adapterünk mostantól az osztály összes függvényében elérhető.notifyDataSetChanged()
belső függvényét:
adapter.notifyDataSetChanged();
"Hozzáadva: <az új szó szöveges formája, mint a listában>"
Figyelj, hogy itt most neked kell meghívnod az új szó toString függvényét!
Ha nem emlékszel a Toastokra, akkor puskázz a
gyorssegély lapról.
Ezzel készen is vagyunk! Próbáld ki, adj hozzá pár új szót a listához!
Beírtál egy szót, de már megtanultad, és csak útban van? Vagy esetleg elírtál valamit? Sajnos jelenleg az egytelen lehetőségünk a törlésre, hogy kilépünk a programból, újraindítjuk azt, majd újra bevisszük az összes többi szót. Ez így nem túl használható, bár kétségkívül ha sokszor írjuk be a szópárokat, egy idő után megtanulja az ember őket!
Sokkal jobb lenne, ha egy-egy szót tudnánk törölni a listából anélkül, hogy az egészet előről kéne kezdenünk.
Természetesen erre is van megoldás, nézzük is meg!
Ahhoz, hogy megtudjuk melyik szót kell törölni a listából, valahogy lehetőséget kéne biztosítani a felhasználónak arra, hogy kiválassza a törlendő szót. Android eszközökön általában "hosszú érintésre" (long tap) felugrik egy kis helyi menü, hasonlóan ahhoz, amikor a számítógépen jobb egérgombbal kattintasz valamire. Mi most a helyi menüt (aki utánanézne: context menu a kulcsszó) kihagyjuk, helyette hosszú érintésre rögtön törölni fogunk.
A gombok onClick-jéhez hasonlóan írhatunk egy olyan függvényt, ami az ilyen hosszú érintésekre figyel. Itt is kell valami a Java kódban, ahol megírhatjuk, hogy mi történjen ha a felhasználó rajtatartja az ujját a lista egy során. A szimpla kattintással/érintéssel szemben azonban nem elég egyetlen függvényt megírni, és a layout szerkesztőben hozzárendelni, hanem egy egész picit bonyolultabban kell megoldani.
A SzavakActivity
onCreate
függvényébe másold be az alábbi kódot:
lista.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// a szavak lista position-adik elemére kattintott a felhasználó
// itt kell megírni, hogy mi történjen hosszú érintésre
return true; // ez fontos, ezzel jelezzük a rendszernek, hogy tudomásul vettük a hosszú érintést.
}
});
A fenti sorokból ijesztőnek tűnhetnek, de most elég annyit megérteni belőlük, hogy:
A fenti kód megmondja, hogy a lista
változó OnItemLongClick
eseményénél mi történjen.
Tehát nem elég, hogy hivatkozunk a lista változónkra, de rögtön azt a kódot is ide írjuk be, amit egyébként külön függvénybe helyeznénk.
És most nézzük meg, hogy mit is kell csinálni, a törléshez:
szavak
listából a törlendő szót.Részletesen:
onItemLongClick
függvényt, akkor láthatod, hogy
a harmadik paramétere (int position
) egy szám, mégpedig pont a törlendő szó
indexe a szavak
listában. Szo torlendo = szavak.get(position);
ArrayList
leírásánál szerepelt már!)notifyDataSetChanged()
).Az lenne az igazi, ha a fenti 3-4 lépést már magadtól (esetleg a korábbi kódjaidat felhasználva) sikerülne megírni, de ha esetleg mégis elakadtál volna, vagy csak leellenőriznéd a munkádat, itt egy lehetséges megoldás:
lista.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// keressük ki a törlendő szót
Szo torlendo = szavak.get(position);
// töröljük a szót a listából
szavak.remove(position);
//szóljuk az adapternek, hogy módosult a lista
adapter.notifyDataSetChanged();
rezgoMotor.vibrate(100);
return true;
}
});
Ne felejtsd el, hogy ezt az onCreate-be kell beírnod!
Gratulálunk! Ha idáig eljutottál, az azt jelenti hogy sikerült mindent pontosan követned ebben a nem túl egyszerű, ám annál több helyen elrontható leckében!
Ha minden működik, csinálj pár screenshotot, amin látszik, hogy hozzáadtál pár saját
szót!
Az elkészült projektet csomagold be, és küldd el nekünk a
feltöltő oldalon keresztül!
Hamarosan jelentkezünk a következő leckével, amiben végre megoldjuk, hogy megmaradjanak a szavaink két programfuttatás között is!