aplikacija za uČenje matematike na ureĐajima s … · 2017-09-11 · firebase auth, realtime...
TRANSCRIPT
SVEUČILIŠTE U ZAGREBU
FAKULTET ELEKTROTEHNIKE I RAČUNARSTVA
ZAVRŠNI RAD br. 5156
APLIKACIJA ZA UČENJE MATEMATIKE
NA UREĐAJIMA S OPERACIJSKIM
SUSTAVOM ANDROID
Sara Brozović
Zagreb, lipanj 2017.
Sadržaj
Uvod ...................................................................................................................................... 1
1. Platforma Firebase.......................................................................................................... 2
1.1 Realtime Database ....................................................................................................... 2
3. Opis funkcionalnosti i korisničkog sučelja .................................................................... 7
3.1. Opis funkcionalnosti ............................................................................................... 7
3.2. Opis korisničkog sučelja ......................................................................................... 7
4. Implementacijski detalji aplikacije............................................................................... 18
4.1. Navigacija po aplikaciji ........................................................................................ 18
4.2. Kartice ................................................................................................................... 19
4.3. Oznake .................................................................................................................. 20
4.4. Implementacijski detalji igre ................................................................................ 22
Zaključak ............................................................................................................................. 27
Literatura ............................................................................................................................. 28
Sažetak ................................................................................................................................. 29
Ključne riječi: .................................................................................................................. 29
Summary .............................................................................................................................. 30
Keywords: ....................................................................................................................... 30
Uvod
U ovom završnom radu je opisana funkcionalnost, implementacija i korištenje
aplikacije za učenje matematike na uređajima s operacijskim sustavom Android. Aplikacija
Simple Math je podijeljena u dva dijela.
Prvi dio je namijenjen za učenje matematike. U ovom dijelu aplikacije se nalaze odabrana
poglavlja iz matematike, koja će biti obrađena: jednadžbe, funkcije, derivacije i integrali.
Svako od tih poglavlja je podijeljeno u četiri manja dijela kako bi korisniku bilo lakše pratiti
gradivo. Korisnik ne mora prolaziti gradivo po redu nego može prvo naučiti onaj dio gradiva
koji ga više zanima. Nakon što korisnik prođe neki dio gradiva uvijek se može vratiti na to
isto gradivo bez da sadržaj bude obrisan ili zaključan.
Drugi dio aplikacije je igra pomoću koje korisnik može vidjeti koliko je dobro usvojio
gradivo koje se obradilo u prvom dijelu aplikacije. Korisnik igru igra s još jednim
korisnikom aplikacije. Jedan korisnik izabire drugog korisnika s kojim želi igrati, a taj drugi
korisnik mora prihvatiti poziv kako bi se pokrenula igra. Kada igra počne oba igrača dobiju
tri nasumična pitanja na koja moraju točno i brzo odgovoriti kako bi osvojili bodove i
pobijedili svog suigrača. Pitanja pokrivaju cijelo gradivo koje se nalazi u aplikaciji te se
preporuča da korisnik prvo prođe cijelo gradivo kako bi postigao dobar rezultat u igri.
1. Platforma Firebase
U sklopu završnog rada korišten je poslužitelj Firebase. Firebase je tehnologija koja
omogućava izradu mobilnih i web aplikacija bez programiranja na strani poslužitelja kako
bi se razvoj pokazao bržim i lakšim. S Firebaseom ne moramo raditi „over-provisioning“ (U
računalstvu, „provisioning“ znači opremanje računala, virtualnog stroja ili Cloud instance sa
svime što je potrebno za pokretanje IT usluga. U ovom slučaju, odredbe uključuju CPU
jezgre, RAM, kapacitet tvrdog diska, operativni sustav i mrežnu propusnost. „Over-
provisioning“ znači opremiti računalo, virtualni stroj ili Cloud instancu sa svime više nego
što je potrebno, za svaki slučaj) poslužitelje koji pružaju veću količinu podataka ili graditi
REST API-je. Firebase poduzima sve potrebne korake: pohranjivanje podataka,
potvrđivanje korisnika i primjenjuje pravila pristupa. Podržava web, iOS, OS X i Android
klijente. Aplikacije koje koriste Firebase mogu jednostavno koristiti i kontrolirati podatke,
bez razmišljanja o tome kako će podaci biti pohranjeni i usklađeni. Nema potrebe za
pisanjem koda poslužitelja ili za implementaciju složenog poslužiteljskog okvira kako bi se
aplikacija započela s Firebaseom.
Firebase se sastoji se od komplementarnih funkcionalnosti koje programeri mogu
kombinirati ovisno u njihovim potrebama. Za development su to: Firebase Cloud Messaging,
Firebase Auth, Realtime Database, Firebase Storage, Firebase Hosting, Firebase Test Lab
for Android, Firebase Crash Reporting. U aplikaciji je za spremanje korisničkih imena,
pitanja koja će se postaviti u igri i dodatne informacije koje su potrebne za igru, korišten
Realtime Database.
1.1 Realtime Database
Realtime Database je baza podataka u stvarnom vremenu. Usluga omogućuje aplikacijskim
programerima API koji omogućuje sinkronizaciju aplikacijskih podataka među klijentima i
pohranjuje ih na Firebase Cloud. Postoje biblioteke koje omogućuju integraciju s
aplikacijama Android, iOS, JavaScript, Java, Objective-C, Swift i Node.js. Razvojni
programeri koji koriste bazu podataka u stvarnom vremenu mogu osigurati svoje podatke
korištenjem sigurnosnih pravila na strani poslužitelja.
Umjesto tipičnih HTTP zahtjeva, Firebase Realtime baza podataka koristi sinkronizaciju
podataka - svaki put kada se podaci mijenjaju, svi povezani uređaji primaju ažuriranje u
milisekundama.
Firebase aplikacije mogu se koristiti čak i kada su izvan mreže, jer Firebase Realtime
Database SDK zadržava podatke na disku. Kada se ponovno uspostavi veza, klijentski uređaj
prima sve izmjene koje je propustio, sinkronizirajući ga s trenutnim statusom poslužitelja.
Realtime Databaseu može se pristupiti izravno s mobilnog uređaja ili web preglednika.
Nema potrebe za aplikacijskim poslužiteljem. Sigurnost i provjera valjanosti podataka
dostupni su putem Firebase Realtime Security Database Security Rules koja se temelje na
izrazima koji se izvršavaju prilikom čitanja ili pisanja podataka. Ako su integrirani s Firebase
Authentication, programeri mogu definirati tko ima pristup podacima u bazi podataka i kako
im može pristupiti.
Firebase podaci su zapisani preko Firebase Database reference, a mogu se preuzeti tako da
se na referencu prikvači asinkroni slušatelj (Listener). Slušatelj se aktivira jednom na
početno stanje i nakon toga svaki put kada dođe do promjene u bazi podataka.
Za čitanje i pisanje podataka u bazu potrebna je instanca DatabaseReference.
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Pomoću metode setValue() u bazu podataka se mogu spremiti različite vrste podataka,
uključujući i Java objekte. Kada spremite objekt, odgovori svih dohvatitelja (gettera) bit će
spremljeni kao djeca (Children) neke lokacije (u ovom primjeru „message“).
Za čitanje podataka i slušanje promjena u bazi podataka koristi se ValueEventListener. On
se doda u DatabaseReference korištenjem metode addValueEventListener().
Korištenjem metode onDataChange () bi se moglo pročitali statički snapshot sadržaja na
određenoj stazi. Ta se metoda aktivira jednom kada se slušatelj postavi i ponovno svaki put
kada se podaci, uključujući djecu (Children), mijenjaju.
ValueEventListener playersListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get AvailablePlayers object and use the values to update the UI
AvailablePlayers value = dataSnapshot.getValue(AvailablePlayersActivity.class);
// ...
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Getting AvailablePlayers failed, log a message
Log.w(TAG, "loadAvailablePlayers:onCancelled", databaseError.toException());
// ...
}
};
mReference.addValueEventListener(playersListener);
Slušatelj prima DataSnapshot koji sadrži podatke na određenoj lokaciji u bazi podataka u
vrijeme događaja. Pozivanje getValue() na snapshot vraća vrijednost Java objekta. Ako
nema podataka, pozivanje getValue () vraća null.
Kada se radi s listama, aplikacija bi trebala slušati događaje za djecu umjesto vrijednosti
događaja koji se koriste za pojedinačne objekte.
Događaji za djecu pokreću se kao odgovor na specifične operacije koje se događaju djeci
čvora iz operacije kao što je dodavanje novog djeteta pomoću metode push () ili dijete koje
se ažurira putem metode updateChildren(). Za slušanje događaja na djecu potrebno je dodati
ChildEventListener na DatabaseReference.
ChildEventListener ima metode onChildAdded(), onChildChanged(), onChildRemoved(),
onChildMoved() i onCancelled().
Metoda onChildAdded() vraća listu elementa ili sluša na dodavanje novog elementa. Ovaj
povratni poziv pokreće se jednom za svako postojeće dijete, a zatim svaki put kada se novo
dijete doda na navedenu stazu. DataSnapshot koji je proslijeđen slušatelju sadrži podatke o
novom djetetu.
Metoda onChildChanged()aktivira se na promjene elemenata koje se dogode u listi. Ovaj
događaj se aktivira svaki puta kada se dijete čvora izmijeni. Aktivira se i u slučaju da se neko
od djece unutar djeteta izmjeni. DataSnapshot proslijeđen slušateljima događaja sadrži
ažurirane podatke za dijete.
Metoda onChildRemoved() se aktivira kada se neki od elemenata liste obriše. DataSnapshot
koji je proslijeđen na povratni poziv događaja sadrži podatke o uklonjenom djetetu.
Metoda onChildMoved() se aktivira kada dođe do promjena u redoslijedu elemenata liste
koja se promatra. Ovaj događaj se pokreće svaki put kad je onChildChanged () aktivirao
ažuriranje koje uzrokuje preusmjeravanje djeteta.
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// A new comment has been added, add it to the displayed list
Comment comment = dataSnapshot.getValue(Comment.class);
// ...
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
// A comment has changed, use the key to determine if we are displaying this
// comment and if so displayed the changed comment.
Comment newComment = dataSnapshot.getValue(Comment.class);
String commentKey = dataSnapshot.getKey();
// ...
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
// A comment has changed, use the key to determine if we are displaying this
// comment and if so remove it.
String commentKey = dataSnapshot.getKey();
// ...
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
// A comment has changed position, use the key to determine if we are
// displaying this comment and if so move it.
Comment movedComment = dataSnapshot.getValue(Comment.class);
String commentKey = dataSnapshot.getKey();
// ...
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load comments.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
Slušatelji se mogu obrisati pomoću metode removeEventListener().
3. Opis funkcionalnosti i korisničkog sučelja
3.1. Opis funkcionalnosti
Aplikacija sadrži odabrana poglavlja iz matematike. Korisnik može odabrati neko od
poglavlja i unutar njega može odabrati jednu od manjih cjelina. Aplikacija sadrži četiri
poglavlja: Equations, Functions, Derivations i Integrals. Svako poglavlje sadrži četiri
cjeline koje detaljnije obrađuju poglavlje. Sveukupno je se u aplikaciji nalazi šesnaest
cjelina. Svaka cjelina sadrži četiri dijela. U dva dijela objašnjava se gradivo odabrane cjeline,
a u druga dva dijela nalaze se primjeri vezani za tu cjelinu.
U aplikaciji se nalazi višekorisnička igra pomoću koje korisnici mogu provjeriti usvojenost
gradiva koje je objašnjeno u aplikaciji. Korisnik se prijavljuje u igru. Nakon prijave korisnik
može odabrati suigrača s kojim želi igrati igru. Suigrač može odbiti ili prihvatiti poziv koji
mu je uputio korisnik. Ako prihvati obojici se pokreće igra. Igra se sastoji od tri pitanja
vezana uz gradivo koje je objašnjeno u aplikaciji. Korisnik mora dati odgovor na svako
pitanje. Svako pitanje ima samo jedan točan odgovor. Kada igrači odgovore na sva pitanja
mogu vidjeti svoj i suigračev rezultat igre.
3.2. Opis korisničkog sučelja
Postoje dva načina navigacije po aplikaciji. Prvi način je preko bočne navigacije
(NavigationDrawer), a drugi način je odabirom gumba koji se nalaze na određenim
ekranima.
Slika 1: UseCase dijagram navigacije po aplikaciji
Ulaskom u aplikaciju otvara se početni zaslon Home koji sadrži gumbe pomoću kojih
možemo odabrati koji dio aplikacije želimo koristiti, učiti gradivo ili igrati igru. Početni
zaslon je fragment koji uz gumbe Lessons i Game sadrži dodatne opcije (OptionsMenu) i
bočnu navigaciju. Dodatne opcije sadrže element Settings kojim možemo mijenjati postavke
aplikacije. Bočna navigacija služi za navigaciju po svim dijelovima aplikacije.
Slika 2: Početni zaslon Slika 3: Bočna navigacija
Bočna navigacija je konceptualno podijeljena u tri grupe elemenata. Prva grupa je navigacija
po glavnim dijelovima aplikacije: početna stranica Home, Lessons i Game. Druga grupa su
sva poglavlja koja se obrađuju u aplikaciji. Do svih njih se može doći pritiskom na gumb
Lessons koji se nalazi na početnom zaslonu ili pritiskom na element Lessons u bočnoj
navigaciji. Ako pojedinačno želimo doći do nekog poglavlja najbrži način je odabirom tog
elementa koji se nalazi u drugoj grupi elemenata u bočnoj navigaciji. Treća grupa elemenata
sadrži element Settings pomoću kojeg možemo mijenjati postavke aplikacije.
Odabirom gumba Lessons ili elementa u bočnoj navigaciji otvara se fragment Lessons koji
kao i fragment Home sadrži dodatne opcije i bočnu navigaciju. Fragment Lessons također
sadrži gumbe Chapter 1: Equations, Chapter 2: Functions, Chapter 3: Derivations i
Chapter 4: Integrals pomoću kojih možemo doći u nekih od fragmenata Equations,
Functions, Derivations ili Integrals.
Slika 4: fragment Lessons
Odabirom gumba Chapter 1: Equations ili elementa Equations u bočnoj navigaciji otvara
se fragment Equations koji kao i fragmenti Home i Lessons sadrži dodatne opcije i bočnu
navigaciju. Fragment Lessons sadrži prozor za prikaz teksta (TextView) unutar kojeg je
kratki uvod u to što je to jednadžba i gumb Next. Klikom na gumb Next otvara se nova
aktivnost u kojem se nalaze manje cjeline o jednadžbama.
Na isti način su oblikovani i fragmenti Functions, Derivations i Integrals do kojih se može
doći odabirom gumba Chapter 2: Functions, Chapter 3: Derivations, Chapter 4: Integrals
ili odabirom elemenata Functions, Derivations, Integrals u bočnoj navigaciji.
Slika 5: fragment Equations
Ako korisnik odabere gumb Game ili element Game u bočnoj navigaciji, otvara mu se
fragment Game. U fragmentu Game korisnik u prozoru za unos teksta (EditText) upisuje
korisničko ime koje će biti prikazano ostalim korisnicima koji su registrirani u igri.
Ako korisnik odabere u dodatnim opcijama element Settings ili element Settings u bočnoj
navigaciji otvara se fragment Settings. U njemu korisnik može unutar padajućeg izbornika
(Spinner) odabrati želi li omogućiti zvuk Enable ili onemogućiti zvuk Disable unutar
aplikacije. Odabirom gumba Change željeni odabir unutar padajućeg izbornika će biti
primijenjen na aplikaciju.
Slika 6: fragment Game Slika 7: fragment Settings
Nakon što se odabere gumb Next u nekom od fragmenata Equations, Functions, Derivations
ili Integrals otvara se aktivnost LessonActivity unutar koje se nalaze kartice (CardView).
Svako od poglavlja Equations, Functions, Derivations ili Integrals ima svoj vizualno
jedinstveni raspored (Layout) unutar kojega su četiri kartice. Svaka od četiri kartice
predstavlja jednu manju cjelinu unutar poglavlja koje je odabrano. Poglavlje Equations
sadrži kartice Polynomial, Trigonometric equations, Exponential equations i Logaritmic
equations. Odabirom jedne od kartica prelazi se u novu aktivnost gdje se detaljnije obrađuje
odabrana cjelina.
Sveukupno aktivnost LessonActivity sadrži 16 kartica (4x4).
Slika 8: CardViewActivity Slika 9: CardViewActivity
Nakon što se odabere jedna od kartica otvara se nova aktivnost TabLessonActivity. U njoj
se nalaze oznake (Tab) koje se mogu dodirom pomicati lijevo-desno. Oznaka First part
sadrži gradivo prvog dijela cjeline. U njoj se nalazi tekst popraćen matematičkim formulama
i slikama. Oznaka Example one sadrži primjer ili više njih koji se odnose na gradivo s oznake
First part. Oznaka Second part sadrži drugi dio gradiva cjeline čiji tekst je također popraćen
matematičkim formulama i slikama. Oznaka Example two sadrži primjer koji se odnosi na
gradivo koje je obrađeno u oznaci Second part.
Sveukupno aktivnost TabLessonActivity vizualno ima 64 oznake (4x4x4).
Na slikama 10., 11., 12. i 13. su prikazane oznake koje se otvore kada se odabere poglavlje
Equations te se nakon toga odabere kartica Polynomial.
Slika 10: Oznaka First part Slika 11: Oznaka Example one
Slika 12: Oznaka Second part Slika 13: Oznaka Example two
Ako korisnik odabere gumb Game unutar fragmenta Home ili element Game u bočnoj
navigaciji otvara se fragment Game gdje upisuje korisničko ime. Nije moguće odabrati
korisničko ime nekog drugi korisnika koji je trenutno prijavljen u igru. Ako korisnik upiše
korisničko ime koji se već koristi aktivirat će mu se skočna obavijest (Toast) s upozorenjem
da mora odabrati drugo korisničko ime. Korisnik ne može dva puta za redom koristiti isto
korisničko ime. Korisnik također ne može imati prazan niz kao korisničko ime.
Nakon što korisnik zadovolji sve uvjete i odabere gumb Play otvara mu se nova aktivnost u
kojoj se nalazi reciklirajuća lista (RecyclerView) s imenima svih ostalih korisnika koji su
trenutno prijavljeni u igru.
Slika 15: Popis svih igrača
Korisniku je omogućena odjava iz igre klikom na element QUIT u dodatnim opcijama.
Također ako klikne na gumb za izlaz iz aplikacije (gumb Home) ili gumb za nazad (Back)
se također odjavljuje iz igre i njegovo ime se briše iz Firebase baze podataka.
Slika 16: Sekvencijski dijagram igre
Ako trenutni korisnik želi igrati s nekim od prijavljenih korisnika klikom na korisnikovo ime
u reciklirajućoj listi se odabranom korisniku šalje zahtjev za igrom. Dok trenutni korisnik
čeka na odgovor, njegova reciklirajuća lista je „zaključana“ kako ne bi mogao odabrati
nekog drugog igrača za igru dok mu prvi igrač kojeg je odabrao ne odgovori. Odabranom
korisniku otvara se skočni prozor (popUp) s pitanjem Do you want to play with: ime prvog
korisnika. Ako odabrani korisnik odabere gumb „No“ odgovor se prosljeđuje trenutnom
korisniku i otključava mu se reciklirajuća lista. Ako odabrani korisnik odabere gumb Yes
onda oba korisnika ulaze u igru. Otvara im se nova aktivnost GameActivity u kojem igrači
dobiju pitanja na koja moraju odgovoriti. Pitanja dolaze jedno po jedno. Postoji brojač u
gornjem desnom kutu koji broji koliko je vremena trebalo igraču da odgovori na sva pitanja.
Brojač postoji iz razloga da se u slučaju kada je po broju bodova izjednačeno zna tko je bio
brži, pa je onda taj igrač pobjednik.
Slika 17: Jedno od pitanja u igri
Nakon što oba igrača odgovore na sva pitanja otvara se nova aktivnost koja prikazuje broj
bodova svakog od igrača. Ako broj bodova nije isto kod oba igrača vremena igrača neće biti
prikazana. Vrijeme će biti prikazano jedino ako su igrači igrali neriješeno u bodovima. Ako
su i vremena jednaka onda je izjednačeno.
Slika 18: Rezultat igre za različit broj bodova Slika 19: Jednako bodova i vrijeme
4. Implementacijski detalji aplikacije
4.1. Navigacija po aplikaciji
Bočna navigacija je napravljena pomoću fragmenata. Princip fragmenata je da svaki
fragment mora imati roditeljsku aktivnost u kojem se implementiraju svi dijelovi koda koji
su zajednički za sve fragmente. Također u roditeljskoj aktivnosti mogu biti implementirane
metode koje koristi pojedini fragment. Jedan od takvih primjera bi bio slučaj kada gumb
unutar fragmenta ima metodu onClick. Npr. gumb Lessons unutar fragmenta Home sadrži
onClick metodu openLessons.
<Button
….
android:fontFamily="casual"
android:onClick="openLessons"
android:text="@string/nav_lessons"
….
/>
Kada gumb ima ovako definiranu metodu onClick onda je najbolje da se ta metoda definirana
u roditeljskoj aktivnosti. U ovom slučaju fragmentu Home, a i fragmentima Lessons, Game,
Equations, Functions, Derivations, Integrals, Settings roditeljska aktivnost je MainActivity,
tako da je metoda openLessons() definirana unutar aktivnosti MainActivity.
public void openLessons(View view){
navItemIndex = 1;
CURRENT_TAG = TAG_LESSONS;
loadHomeFragment();
}
Ako se na gumb postavi metoda setOnClickListener() onda se taj kod može napisati unutar
fragmenta.
View layout = inflater.inflate(R.layout.fragment_equations, container, false);
btn_next = (Button) layout.findViewById(R.id.button_equations_next);
btn_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), LessonActivity.class);
intent.putExtra("Lesson_tip", "Equations");
startActivity(intent);
}
});
Aktivnost MainActivity pamti koji je fragment trenutno otvoren i označava ga unutar bočne
navigacije sivom bojom. Elementi unutar bočne navigacije su napravljeni kao meni (Menu)
activity_main_drawer unutar kojeg su podijeljeni u tri grupe. Dodani su u raspored
activity_main preko prozora za navigaciju (NavigationView) kao:
app :menu="@menu/activity_main_drawer"
te im se pristupa pomoću id-a od prozora za navigaciju nav_view.
Pošto su elementi bočne navigacije napravljeni kao meni, unutar aktivnosti MainActivity se
postavlja metoda setNavigationItemSelectedListener() na prozor za navigaciju, te se
klikom na neki od elemenata mijenjaju fragmenti.
4.2. Kartice
Svaka od četiri sličice koje su vidljive kod ulaska u aktivnost LessonAcivity (do sad
smo ih zvali kartice) je zapravo jedan linearni raspored (LinearLayout) unutar kojeg se nalazi
kartica (CardView). Unutar kartice se nalazi relativni raspored (RelativeLayout), a unutar
njega se nalazi prozor za prikaz slike (ImageView) i prozor za prikaz teksta (TextView).
Prozor za prikaz slike sadrži slike s brojevima 1., 2., 3., i 4., a prozor za prikaz teksta sadrži
ime manjih cjelina unutar poglavlja.
<android.support.v7.widget.CardView>
<RelativeLayout>
<ImageView/>
<TextView/>
</RelativeLayout>
</android.support.v7.widget.CardView>
Sve četiri kartice su postavljene unutar reciklirajuće liste koja se uključuje u glavni raspored
activity_lesson.
<include layout="@layout/content_main_card"/>
Reciklirajuća lista ima svoj posebno izrađen adapter LessonAdapter koji sadrži listu modela
Lesson koja sadrži informacije o pojedinoj kartici. Adapter puni reciklirajuću listu s
podacima. Adapter sadrži i slušatelja koji se aktivira odabirom jedne od kartica.
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(v.getContext(),TabLessonActivity.class);
intent.putExtra("CardTitle", title.getText().toString());
v.getContext().startActivity(intent);
} });
Reciklirajuća lista koristi metodu recikliranja svojih elemenata pomoću ViewHoldera. U
jednom trenutku se u memoriji uređaja ne nalaze svi elementi liste nego samo oni koji su
vidljivi, a kada dodatni elementi postaju vidljivi, oni preuzimaju onaj dio memorije koji su
koristili oni elementi koji su neposredno prije maknuti iz vidljivog stanja.
4.3. Oznake
Oznake su napravljene pomoću fragmenata. TabLessonActivity je roditeljska
aktivnost fragmentima First part, Example one, Second part i Example two.
Da bi bila omogućena navigacija po oznakama korišten je ViewPagerAdapter koji
nasljeđuje FragmentStatePagerAdapter. Ovaj adapter nadjačava metodu
FragmentStatePagerAdaptera
@Override
public Fragment getItem(int position) {}
koja mijenja fragmente ovisno o tome što korisnik odabere.
Glavni raspored activity_tab_lesson u sebi sadrži alatnu traku (Toolbar), raspored oznaka
(TabLayout) i prozor za listanje (ViewPager).
<LinearLayout>
<include layout="@layout/tab_lesson_tool_bar"/>
<android.support.design.widget.TabLayout/>
<android.support.v4.view.ViewPager></android.support.v4.view.ViewPager>
</LinearLayout>
Alatna traka prikazuje ime cjeline koja se obrađuje.
Raspored oznaka služi za navigaciju po oznakama. Na njemu se može odabrati oznaka koja
se želi otvoriti, te je također označena oznaka koja se trenutno prikazuje.
Slika 14: Raspored oznaka (TabLayout)
Prozor za listanje služi za prikaz rasporeda fragmenta koji je trenutno odabran.
Roditeljska aktivnost TabLayoutActivity inicijalizira alatnu traku, ViewPagerAdapter, te
povezuje prozor za listanje s ViewPagerAdapterom.
Fragmenti imaju svoje rasporede koji sadrže prozore za prikaz teksta, prozore za prikaz
matematičkih formula (MathView) i po potrebi prozore za prikaz slika. Prozor za klizanje
(ScrollView) je postavljen kao roditeljski raspored (ParentLayout) kako bi se omogućio
pregled sadržaja ako je sadržaj veći od ekrana uređaja.
<ScrollView>
<LinearLayout>
<android.support.constraint.ConstraintLayout>
<TextView/>
<TextView />
<TextView />
<ImageView/>
<io.github.kexanie.library.MathView/>
<io.github.kexanie.library.MathView/>
<io.github.kexanie.library.MathView/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</ScrollView>
Svaki fragment unutar sebe inicijalizira prozore za prikaz teksta, prozore za prikaz
matematičkih formula i prozore za prikaz slika, te unosi u njih potrebne podatke.
4.4. Implementacijski detalji igre
Za implementaciju igre korištena je Firebaseova usluga Realtime Database.
Realtime Database se koristi za provjeru i spremanje korisničkih imena, za samu
komunikaciju između igrača, te za spremanje bodova i vremena za svakog od igrača.
Kako bi se znalo kada se neki entitet spremi u bazu podataka na Cloudu, koristimo
ChildEventListener i ValueEventListener. ChildEventListener sadrži metode
onChildAdded(), onChildChanged(), onChildRemoved(), onChildMoved() i onCancelled()
koje je potrebno nadjačati.
Metoda goChooseTeammate(), koja se nalazi u aktivnosti MainActivity, je metoda koja se
poziva kada se pritisne gumb Play u fragmentu Game. Metoda provjerava da li u bazi
podataka postoji korisničko ime koje je korisnik upisao, te provjerava da li je korisnik prošli
put kada se prijavljivao koristio isto korisničko ime. Ako postoji to korisničko ime u bazi
podataka ili je korisnik već koristio to korisničko ime, ispisuje se poruka da korisnik mora
odabrati drugačije ime. Metoda goChooseTeammate() također provjerava da li je korisničko
ime prazno. U slučaju da je, ispisuje se poruka korisniku i ne poduzima se ništa dok korisnik
ne upiše neko korisničko ime. Kako bi provjerili da li se korisničko ime poklapa s nekim od
imena u bazi podataka, korišten je ChildEventListener, točnije metoda onChildAdded() koja
stvara i puni listu sa svim korisničkim imena koja se nalaze bazi podataka. Također je
implementirana metoda onChildRemoved() koja iz liste svih korisničkih imena u bazi
podataka briše onaj element kojeg više nema u bazi podataka. Razlog je taj što tako u slučaju
da se neki korisnik odjavio iz igre novi korisnik može odmah koristiti njegovo korisničko
ime.
Nakon što se zadovolje svi potrebni uvjeti za korisničko ime, to korisničko ime se upisuje u
bazu podataka pomoću metode writeNewUsername().
public void writeNewUsername(String userId, String username){
AvailablePlayers player = new AvailablePlayers(username);
mDatabase.child("players").child(userId).setValue(player);
}
Nakon što je ime upisano u bazu podataka prelazi se u novu aktivnost
AvailablePlayersActivity u kojoj se nalazi reciklirajuća lista s popisom svim korisničkih
imena iz baze podataka, naravno ime trenutnog korisnika nije prikazano u tom popisu jer
nam smisla da korisnik može odabrati sam sebe za igru.
Reciklirajuća lista ima svoj posebno izrađen adapter AvailablePlayersAdapter koji sadrži
listu modela AvailablePlayers koja sadrži informacije o pojedinom igraču, tj. njegovo
korisničko ime. Adapter puni reciklirajuću listu s podacima. Za ovu reciklirajuću listu postoji
i posebno izrađen ItemDecoration DividerItemDecoration koji nasljeđuje
RecyclerView.ItemDecoration. DividerItemDecoration dijeli elemente reciklirajuće liste
tako da svaki element ima svoju „kućicu“ u reciklirajućoj listi. Reciklirajuća lista sama po
sebi na nikakav način vidljivo ne odjeljuje svoje elemente, tako da u slučaju da se želi između
elemenata staviti crte koje će ih razdijeliti potrebno je napraviti poseban ItemDecoration za
tu reciklirajuću listu.
Za ovu reciklirajuću listu postoji razred RecyclerTouchListenerAvailablePlayers koji
implementira sučelje RecyclerView.OnItemTouchListener. On se aktivira kada igrač klikne
na neki od elemenata u reciklirajućoj listi, a napravljen iz razloga da se reciklirajuća lista
„smrzne“ kada igrač odabere nekog suigrača za igru. Tako igrač ne može odabrati nekog
drugog igrača za igru dok mu prvi igrač kojeg je odabrao ne odgovori.
Za dodavanje elementa u reciklirajuću listu poziva se metoda addData() koja postavlja
slušatelja ValueEventListener. Implementira se njegova metoda onDataChange() koja za
korisničko ime u bazi podataka provjerava da li jednako korisničkom imenu trenutnog
korisnika, ako je, preskače ga. Ako nije dodaje se u listu availableplayersList.
U aktivnosti AvailablePlayersActivity postoje dva slušatelja ChildEventListener. Prvi sluša
promjene koje se odvijaju s korisničkim imenima igrača, dok se drugi koristi za
„komunikaciju“ između igrača i za samu igru. Slušatelj ChildEventListener za korisnička
imena ima implementiranu metodu onChildRemoved() koja se koristi kako bi se ime
korisnika, koji se odlučio odjaviti iz igre, moglo maknuti iz liste trenutnih igrača i napraviti
promjena u reciklirajućoj listi igračima koji su još u igri. Slušatelj ChildEventListener za
„komunikaciju“ i igru implementira metodu onChildAdded() koja prati što se upisuje u bazi
podataka kada dva igrača „komuniciraju“. Kada jedan igrač odluči da želi ući u igru s drugim
igračem stvori se dijete (Child) unutar baze koji u sebi sadrži sve potrebne informaciju koje
igrači razmjenjuju između sebe. Za početak, ime djeteta je korisničko ime igrača kojeg je
trenutni igrač odabrao da igra s njim. Kada slušatelj shvati da se radi o tom igraču
(odabranom igraču), igraču iskoči skočni prozor s pitanjem da li želi igrati s igračem koji je
njega odabrao za igru. U slučaju da ne želi u bazu se sprema odgovor ne. Slušatelj igrača
koji je birao, shvati da je odgovor „ne“ i aktivira mu skočnu obavijest u kojem piše da
izabrani igrač ne želi igrati s njim te traži da odabere nekog drugog igrača za igru. Međutim
ako odabrani igrač odgovori „da“, aktiviraju se slušatelji od oba igrača i otvara im se
aktivnost GameActivity. Postoji i jedna dodatna situacija u aktivnosti
AvailablePlayersActivity, a ta je kada korisnik odluči da ipak ne želi igrati. Korisnik može
odabrati opciju Quit u gornjem desnom kutu i tako izaći iz igre. Ako to odabere poziva se
metoda koja briše njegovo korisničko ime iz baze podataka, te se aktiviraju slušatelji kod
ostalih korisnika koji onda pozivaju metodu removeItem() koja osvježava njihove
reciklirajuće liste tako da više ne prikazuju korisničko ime korisnika koji više nije u igri.
public void removeItem(int position){
availableplayersList.remove(position);
availableplayersAdapter.notifyItemRemoved(position);
availableplayersAdapter.notifyDataSetChanged();
}
U slučaju da korisnik ne želi izaći na opciju Quit, pokrivene su i sve ostale opcije na koje
korisnik može izaći iz trenutne aktivnosti ili čak iz aplikacije, tako da ne postoji način na
koji bi korisnik izašao iz igre, a da njegovo korisničko ime ostane u bazi podataka.
Postoji poseban protokol u slučaju da igrač želi izaći iz igre nakon što je odabrao suigrača
za igru. Ako se to dogodi igračevo korisničko ime se briše iz baze podataka i u bazu podataka
se sprema posebna poruka „odustao“ koju prepoznaje slušatelj od odabranog igrača te mu se
makne skočni prozor i aktivira skočna obavijest s porukom da je prvi igrač izašao iz igre.
Ako odabrani igrač želi igrati i igrač koji je birao nije izašao iz igre, otvara se aktivnost
GameActivity u kojoj će se obojici postaviti 3 nasumična pitanja iz baze podataka.
U aktivnosti GameActivity se koristi slušatelj ValueEventListener. Preko njegove metode
onDataChange() se iz baze podataka dobave sva pitanja.
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> map = dataSnapshot.getChildren();
for (DataSnapshot el : map) {
questionsMap.put(el.getKey(), (Object)el.getValue());
}
createGame(); }
Nakon što se mapa guestionsMap napuni sa svim pitanjima, poziva se metoda
createGame().
private void createGame(){
findThreeRandomNumbers();
createQuestionsList();
if(mapOfquestionThatWillBeAsked.size()!=0){
generateQuestion();
mapOfquestionThatWillBeAsked.remove(currentKey);
}
createCounter();
startCounter();
}
Unutar metode createGame() poziva se metoda findThreeRandomNumbers() koja od broja
pitanja koja se nalaze u mapi questionsMap nalazi tri različita nasumična broja. Ti će se
brojevi koristiti kao bi se odabralo tri pitanja koja će se postaviti igračima. Metoda
createQuestionsList() kreira listu od tri pitanja koja će biti postavljana igračima. Metoda
generateQuestion() prvo pitanje u listi stavlja u prozor za prikaz teksta i njezine odgovore
nasumičnim redoslijedom stavlja u radijske gumbe (RadioButton). Svi radijski gumbi se
nalaze unutar jedne radijske grupe (RadioGroup). Tako se može označiti samo jedan radijski
gumb, a razlog je taj što pitanja nisu postavljena tako da imaju više odgovora.
Nakon što se generiralo prvo pitanje kreira i pokreće se brojač (Timer) koji broj vrijeme koje
je potrebno igraču da završi cijelu igru.
Nakon što igrač odabere jedan od odgovora klikom na gumb Answer provjerava se da li je
odgovor na pitanje točan. Ako je odgovor točan, ukupna suma njegovih bodova se povećava
za 10, inače ostaje ista. Nakon toga se generira novo pitanje iz liste.
Ako igrač nije odabrao niti jedan odgovor, a kliknuo je na gumb Answer ili Finish (bit će
objašnjen kasnije), aktivira se skočna obavijest s porukom da mora odabrati jedan odgovor.
Kada se postavi treće pitanje gumb Answer se sakrije i na njegovo mjesto se postavi gumb
Finish. Razlog je taj što gumb Finish ima drugačiju funkcionalnost od guma Answer.
if(mapOfquestionThatWillBeAsked.size()==1){
buttonAnswer.setVisibility(View.GONE);
buttonAnswer.setClickable(false);
buttonFinish.setVisibility(View.VISIBLE);
buttonFinish.setClickable(true);
}
Kada igrač odabere odgovor na treće pitanje i klikne na gumb Finish zaustavlja se brojač i
provjerava se da li je odgovor na pitanje točan. Nakon toga se suma bodova i vrijeme
spremaju u bazu podataka, te se otvara aktivnost ResultsOfGameActivity.
Aktivnost ResultsOfGameActivity ima dva slušatelja ChildEventListener. Jedan sluša dijete
„score“, a drugi dijete „time“. Kod oba slušatelja je implementirana metoda onChildAdded()
koja za svakog od igrača sprema u varijable this_players_score i teammates_score dohvaća
iz baze podataka bodove koje su igrači ostvarili, te u varijable this_players_time i
teammates_time sprema vrijeme završetka igre svakog od igrača.
Aktivnost ResultsOfGameActivity ima dva slušatelja ValueEventListener. Jedan sluša dijete
„score“, a drugi dijete „time“. Kod oba slušatelja je implementirana metoda
onDataChange() koja provjerava da li su vrijeme i broj bodova upisani kod oba igrača. U
slučaju da nisu, znači da jedan od igrača nije još završio igru. Igraču koji je završio igru
prikazuje se prozor za prikaz teksta u kojem piše Please wait…. Nakon što su oba igrača
završila igru poziva se metoda calculateScores() koja izračunava koji od igrača je
pobjednik. Ako oba igrača imaju isto broj bodova prikazuje se prvo broj bodova i vrijeme
trenutnog igrača, a onda broj bodova i vrijeme suigrača. Međutim ako se broj bodova
razlikuje onda se vremena oba igrača ne prikazuju.
Nakon što igrači pogledaju rezultat klikom na gumb Ok poziva se metoda exit() koja ih
odjavljuje se iz igre, briše njihove bodove i vrijeme iz baze podataka i vraća ih na početni
ekran.
public void exit(View view){
if(child.equals(players_username)){
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit().putString("used_
username", players_username).commit();
mDatabase.child("communication").child(players_username).removeValue();
}
finish();
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
U metodi exit() se sprema ime trenutnog igrača u SharedPreferences tako da igrač ne može
opet koristiti isto korisničko ime kada ponovo želi igrati.
Zaključak
Zadatak ovog rada bio je oblikovati i programski izvesti aplikaciju za učenje
matematike na pametnim telefonima s operacijskim sustavom Android. Razvijena aplikacija
je trebala sadržavati dio gdje se objašnjava gradivo pojedinih poglavlja iz matematike, te
igru u kojoj se korisnici mogu međusobno natjecati. Igra sadrži pitanja iz gradiva koje je
obrađeno u prvom dijelu aplikacije. Komunikacija između korisnika i sama igra su
napravljeni korištenjem platforme Firebase.
Rad na aplikaciji je bio zanimljiv, ali i na nekim dijelovima zahtjevan. Podrška za razvoj
operacijskog sustava Android je bila vrlo dobra. Malo je bilo problema oko toga što za istu
stvar (vizualno) postoji više implementacijskih rješenja, pa je trebalo prvo proučiti sva
rješenja tako da se mogu odabrati ona koja najviše odgovara ovoj aplikaciji. A što se tiče
podrške za Firebase, ona je vrlo dobra i za ovu aplikaciju je korištena Realtime Database
komponenta.
Literatura
[1] Firebase Realtime Database,
https://firebase.google.com/docs/database/
[2] Android Sliding Menu using Navigation Drawer,
http://www.androidhive.info/2013/11/android-sliding-menu-using-navigation-
drawer/
[3] Android working with Card View and Recycler View,
http://www.androidhive.info/2016/05/android-working-with-card-view-and-
recycler-view/
[4] Derivative Rules,
https://www.mathsisfun.com/calculus/derivatives-rules.html
[5] How to find local extrema with the first derivative test,
http://www.dummies.com/education/math/calculus/how-to-find-local-extrema-
with-the-first-derivative-test/
[6] How to find local extrema with the second derivative test,
http://www.dummies.com/education/math/calculus/how-to-find-local-extrema-
with-the-second-derivative-test/
[7] Integration by Substitution,
http://www.mathportal.org/calculus/integration-techniques/integration-by-
substitution.php
[8] Integration by Parts,
http://www.mathportal.org/calculus/integration-techniques/integration-by-parts.php
[9] Purple Math,
http://www.purplemath.com/modules/
Sažetak
U ovom radu napravljena je aplikacija za učenje matematike na uređajima s
operacijskim sustavom Android. Aplikacija ima teorijski dio gdje je objašnjeno gradivo iz
matematike, te interaktivni dio gdje se korisnici mogu međusobno natjecati. Interaktivni dio
je ujedno test s kojim korisnici mogu provjeriti usvojeno znanje jer se u igri postavljanu
pitanja iz gradiva koje je obrađeno u teorijskom dijelu aplikacije. Interaktivni dio je
napravljen pomoću platforme Firebase.
Ključne riječi: Operacijski sustav Android, aplikacija za Android, Java, Firebase,
Firebase Realtime Database, aktivnost, fragmenti, slušatelji, bočna navigacija, dodatne
opcije, klase, kartice, oznake, reciklirajuća lista
Summary
The result of this work is an Android application for learning matematics. The application
has a theoretical part where users can learn selected parts of matematics. Application also
has interactive part where users can compete with each other. The interactive part is also a
test where users can check there knowledge about topics that were explaned in theoretical
part. The questions in the game are about topics that were explaned in theoretical part of the
app. The interactive part was created using Firebase platform.
Keywords: Android Operating System, Android Application, Java , Firebase, Firebase
Realtime Database, Activity, fragments, Listeners, NavigationDrawer, OptionsMenu, class,
CardView, Tab, RecyclerView