Uvod

listener

“Listener pattern” je jedan od najčešće korišćenih paterna koji se koriste pri razvoju aplikacijja. Princip rada se zasniva na tome da u jednoj klasi definišemo dogadjaj i trenutak kada se započinje izvršavanje dogadjaja (“okida dogadjaj”), dok u drugoj klasi definišemo objekat (tzv. listener objekat) koji osluškuje taj dogadjaj i reaguje na njega.

Postoji priličan broj već ugradjenih listener-a u sklopu samog androida, ali pored njih možemo da kreiramo i sopstvene custom listener-e i tako omogućimo da definišemo callback metode za događaje koju su trigerovani i iz drugih delova našeg koda. Custom listeneri se koriste u sledećim slučajevima:

Primer standardnog listenera ugradjenog u operativni sistem

Ovaj patern se često korisi i u sklopu operativnog sistema, a najbolji primer ugradjenog interfejasa u android core je OnClickListener koji je zadužen za “klik” dogadjaj. Ovaj listener je definisan u sklopu View.java klase.

U sklopu View.java klase je definisana setter metoda pod nazivom setOnClickListener(). Pozivanjem ove setter metode u nekoj drugoj klasi se definiše listener objekat koji osluškuje taj dogadjaj, a kroz njega i callback metoda koja reaguje na dogadjaj. U ovom primeru objekat “btnNekiButton” je naslednik View.java klase a samim tim nasledio je sve interfejse uključujući i pomenuti, pa može jednostavno da pozove njegovu setter metodu:

Ili ako prikažemo poznati kod sa “on the fly” kreiranim objektom:

OBJAŠNJENJE:

Instanciranje “on the fly” anonimnog objekta koji implementira interfejs

Anonimna klasa je klasa koja nema ime, stoga kada bi hteli da instanciramo objekat od ovakve klase to bi izgledalo ovako:

Nama je potrebna specifična anonimna klasa, ona koja implementira odredjeni interfejs, stoga iako ne moramo da definišemo ime klase moramo da definišemo koji interfejs da implementiramo. Ako je interfejs definisan u svome fajlu to se postiže ubacivanjem naziva interfejsa ispred zagrada:

Takodje moramo da implementiramo metode interfejsa:

Postupak

Interfejs može da se definiše samostalno u odvojenom fajlu u komunikaciji izmedju dve klase se iz praktičnih razloga definiše u jednoj klasi. U ovim primerima ćemo koristi termine “PrvaKlasa” (mesto gde se definiše interfejs tj. dogadjaj) i “DrugaKlasa” (mesto gde se registruje objekat koji osluškuje i definiše callbackMetoda tj. akcija koja treba da se izvrši nakon “okidanja” dogadjaja).

a) Interfejs (prva klasa):

Kao i kod već pripremljenih listenera u okviru android operativnog sistema i ovde je potrebno prvo kreirati interfejs. Ovaj deo posla je u prethodnom primeru napisan u sklopu androida, dok ćemo u ovome slučaju mi to da uradimo. Interfejs obezbedjuje da se svaki objekat koji implementira interfejs ima callback metodu:

b) Setter metoda (prva klasa)

Pored interfejsa je potrebna setter metoda, čijim se pozivanjem definiše koji objekat je listener (tj. objekat koji osluškuje dogadjaj). Ova metoda nam omogućava da taj objekat definišemo bilo gde, jer je dovoljno da ga prosledimo kao parametar kada pozovemo tu metodu (pogledaj više o tome ovde).

c) Okidanje dogadjaja = pozivanje callback metode (prva klasa)

Pozivanje callback metode se može smatrati kao okidač dogadjaja, stoga negde u okviru klase pozovite callback metodu i na taj način će biti “okinut” prekidač i startovan dogadjaj:

Mada treba izbeći exception:

Celokupni kod iz prve klase možete pregledati ovde.

d) Definisanje objekta koji osluškuje i callback metoda

Svi dosadašnji delovi se kreiraju u jednoj klasi koja je napravilia dogadjaj dok se ovaj deo koda nalazi u nekoj drugoj klasi i zadužen je da u toj drugoj klasi definiše listener objekat koji osluškuje dogadjaj. Definisanjem listener objekta praktično definišemo i callback metodu koja reaguje na dogadjaj. Postoji više načina da se to izvede:

d1) Definisanje listener objekta i callback metode kroz setter metodu

Definisanje callbackMetode može da se izvrši na dva načina u zavisnosti šta je listener.

d1.a) Listener = anonimni objekat

U ovome slučaju definisanje callback metode se vrši pozivanjem setter metode i prosledjivanjem “anonimnog” objekta koji implementira interfejs a samim tim i callback metodu. Da bi mogli da pozovemo metodu iz druge klase potrebno je da je pozovemo preko objekta prve klase ili objekta koji implementira interfejs (više o “objekatKojiImplementiraInterfejs” pogledajte ovde).

Objekat koji prosledjujemo kroz parametar kreiramo kao instancu anonimne klase koja implementira interfejs (kako se “on the fly” kreira objekat od anonimne klase pogledajte ovde).

d1.b) Listener = Cela druga klasa

U ovome slučaju cela klasa implemetira interfejs, pa je potrebno da se cela klasa registruje kao osluškivač tako što se kroz setter metodu prosledi klasa koristeći ključnu reč “this” a zatim override callbackMetoda:

d2) Definisanje listener objekta i callback metode kroz konstruktor

Ako je listener izrazito bitan za samu klasu onda se setter metoda zameni konstruktorskom metodom same klase:

U drugoj klasi kreiramo objekat na osnovu konstruktorske metode PrveKlase, tako što kroz parametar prosledjujemo “on the fly” kreiran anonimni objekat koji implementira listener:

d3) Definisanje listener objekta i callback metode kroz lifecycle metodu

Ovaj pristup se koristi kod komunikacije izmedju fragmenta i aktivnosti.

Fragment

Postupak u okviru fragmenta je sličan postupku (kodu) iz PrvaKlase, tako da se u okviru fragmenta nalaze sva tri prethodno opisana koraka: kreiranje interfejsa, setter metode i okidanje dogadjaja.

Aktivnost

Kod Aktivnosti se postupak delimično razlikuje od DrugeKlase jer mora da se u sklopu metode onFragmentAttach() proveri da li aktivnost implementira interfejs. Tek kada smo sigurni da aktivnost implementira interfejs onda se definiše da aktivnost postane listener objekat. Overajdovanjem callbackMetode se definiše akcija nakon okidanja dogadjaja:

Takodje postoji i drugi način kada se u okviru samog fragmenta proverava da li aktivnost implementira interfejs ili ne. Tada se ceo kod provere izvršava u fragmentu u okviru metode onAtach() (pogledajte ceo kod ovde).

NAPOMENA:
Ukoliko treba da se registruje više od jednog listenera onda je potrebno prilagoditi kod da radi sa nizom listener-a:

Pogledajte ceo novi kod ovde.

Komunikacija Fragment – Aktivnost

U ovom primeru će biti objašnjena interakcija izmedju fragmenta i aktivnosti. U okviru fragmenta kreiranja interfejs i vrši “okidanje” custom dogadjaja, dok se u aktivnosti defiše telo callback-a tj. reakcija na izvršenje tog custom dogadjaja iz fragmenta.

MyListFragment

Aktivnost:

Komunikacija Fragment – Fragment

Komunikacija izmedju njih se može ostvariti na dva načina:

Pošto je u ovome članku tema listener patern, u narednom primeru ćemo prikazati kako se radi komunikacija izmedju dva fragmenta koristeći listenere iako je korišćenje deljenog “ViewModel-a” jednostavniji pristup.
Princip rada je sledeći: poruka iz PrvogFragmenta se šalje u Aktivnost, nakon čega Aktivnost kroz callbackMetodu šalje poruku DrugomFragmentu.

PrviFragment

Aktivnost

DrugiFragment

Na sličan način se rešava komunikacija izmedju Dialoga i Aktivnosti.

Komunikacija Adapter – Aktivnost

U ovome primeru se standardno definiše interfejs, a zatim i setter metoda, kao i okidanje dogadjaja u okviru adapter klase. Iako se u primerima na netu često može naći da se okidanje dogadjaja vrši u okviru “onBindViewHolder() metode, preporuka je da se okidanje dogadjaja vrši u okviru ViewHolder klase tj. u okviru njenog konstruktora.

Adapter

Nakon definisanja interfejsa, potrebno je u fragmentu ili aktivnosti koja koristi adapter registrovati listener i definisati callback metodu:

Fragment ili Aktivnost

Ovde ćemo definisati anonimni listener objekat “on the fly” i u njemu definisati akciju na okidanje dogadjaja:

Ovo je moglo da se uradi i na drugi način: kada Aktivnost implementira interfejs, nekon čega je dovoljno da se overajduje callback metoda za definisanje akcije nakon okidanja dogadjadja.

×

Instanciranje anonimnog objekta koji implementira interfejs “on the fly”:

Anonimna klasa je klasa koja nema ime, stoga kada bi hteli da instanciramo objekat od ovakve klase to bi izgledalo ovako:

Nama je potrebna specifična anonimna klasa, ona koja implementira odredjeni interfejs, stoga iako ne moramo da definišemo ime klase moramo da definišemo koji interfejs da implementiramo. Ako je interfejs definisan u svome fajlu to se postiže ubacivanjem naziva interfejsa ispred zagrada:

Takodje moramo da implementiramo metode interfejsa:

Komunikacje izmedju dve klase:

Kada je interefejs potreban za komunikaciju samo izmedju dve klase onda se on obično definiše i kreira u okviru tzv. prve klase te je potrebno pozvati interfejs preko prve klase:

Pored ovoga je potrebno da implementiramo sve abstraktne metode ovog interfejsa:

U ovom primeru smo koristeći “anonimnu klasu” kreirali “anonimniListenerObjekat” (tj. objekat koji osluškuje), ali “listenerObjekat” ne mora da bude anoniman može da se sačuva u nekoj promenjivoj i da se koristi više puta:

×

Instanciranje anonimnog objekta koji implementira interfejs “on the fly”:

Anonimna klasa je klasa koja nema ime, stoga kada bi hteli da instanciramo objekat od ovakve klase to bi izgledalo ovako:

Nama je potrebna specifična anonimna klasa, ona koja implementira odredjeni interfejs, stoga iako ne moramo da definišemo ime klase moramo da definišemo koji interfejs da implementiramo. Ako je interfejs definisan u svome fajlu to se postiže ubacivanjem naziva interfejsa ispred zagrada:

Takodje moramo da implementiramo metode interfejsa:

×

×

×

Ako je “objekatKojiImplementiraInterfejs” naslednik View klase onda može da bude ubačen u View druge klase kao “custom view”.

1) Ubačen statički (direktno u .xml)

Ako je “objekatKojiImplementiraInterfejs” ubačen statički (kao custom view u layout) onda ga targetiramo kao običian view koristeći metodu indViewById():

2) Ubačen programirano
2.a) Kreiranjem objekta PrveKlase

“listenerObjekat” je ustvari anonimni objekat kreiran “on the fly”:

2.b) DrugaKlasa implementira interfejs

U slučaju da cela klasa implementra interfejs onda se koristi:

×

Prva klasa

Druga klasa

×

Kod ovog pristupa u okviru Aktivnosti ne definišemo koji objekat će biti listeneru, već to radimo u sklopu samog fragmenta kroz metodu onAttach(). Tako da listener objekat postaje bilo koja aktivnost na koju se nakači fragment a da pri tom implementira interfejs:

U aktivnosti koja implementira interfejs se samo override callbackMetoda da bi definisali akciju po okidanju dogadjaja:

Podelite:

Ostavite komentar