Uvod
“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:
- Komunikacija izmedju fragmenta i aktivnosti
- Komunikacija izmedju dva fragmenta preko aktivnosti
- Komunikacija izmedju adaptera i aktivnosti
- Komunikacija izmedju dijaloga i aktivnosti
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.
1 2 3 |
public static interface OnClickListener() { void onClick(View view); } |
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:
1 |
btnNekiButton.setOnClickListener(objekatKojiOsluškuje); |
Ili ako prikažemo poznati kod sa “on the fly” kreiranim objektom:
1 2 3 4 5 6 |
btnNekiButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // Ovde se definiše šta se dešava kad "okine" dogadjaj } }); |
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:
1 |
new { } |
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:
1 |
new NazivInterfejsa () { }; |
Takodje moramo da implementiramo metode interfejsa:
1 2 3 4 5 6 |
new NazivInterfejsa () { @Override public void nekaCallBacKMetoda() { // kod koji izvšava akciju nakon okidanja dogadjaja } }; |
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:
1 2 3 |
public interface NekiInterfejs { void callbackMetoda(); } |
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).
1 2 3 4 5 |
private NekiInterfejs mListener; public void setListener(NekiInterfejs value){ this.mListener = value; } |
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:
1 |
mListener.callbackMetoda(); |
Mada treba izbeći exception:
1 2 3 |
if(mListener != null){ mListener.callbackMetoda(); } |
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).
1 |
objekatKojiImplementiraInterfejs.setListener(anonimniObjekatKojiImplementiraInterfejs) |
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).
1 2 3 4 5 6 |
objekatKojiImplementiraInterfejs.setListener(new PrvaKlasa.NekiInterfejs() { @Override public void callbackMetoda() { // reakcija na okinut dogadjaj } }); |
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:
1 2 3 4 5 6 7 8 9 |
PrvaKlasa objekatKojiImplemetiraInterfejs = new PrvaKlasa(LayoutInflater.from(this), null); objekatKojiImplemetiraInterfejs.setListener(this); . . . @Override callbackMetoda () { // Neka akcija nakon okidanja dogadjaja } |
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:
1 2 3 4 5 6 7 8 9 10 11 |
public class PrvaKlasa { public interface NekiInterfejs { void callbackMetoda(); } private NekiInterfejs mListener; // Konstruktor prve klase umesto setter metode: public PrvaKlasa (NekiInterfejs listener) { this.mListener = listener; } } |
U drugoj klasi kreiramo objekat na osnovu konstruktorske metode PrveKlase, tako što kroz parametar prosledjujemo “on the fly” kreiran anonimni objekat koji implementira listener:
1 2 3 4 5 6 |
PrvaKlasa objekatOdPrveKlase = new PrvaKlasa(new PrvaKlasa.NekiInterfejs() { @Override public void callbackMetoda() { // reakcija na okinut dogadjaj }); }); |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class NekiFragment extends Fragment { public interface NekiInterfejs { void callbackMetoda(); } private NekiInterfejs mListener; public void setOnNekiInterfejsListener(Activity activity) { mListener = activity; } . . . public void nekaMetoda() { // Okidanje dogadjaja: mListener.callbackMetoda(); } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public static class MainActivity extends Activity implements NekiFragment.OnHeadlineSelectedListener{ . . . @Override public void onAttachFragment(Fragment fragment) { if (fragment instanceof NekiFragment) { NekiFragment nekiFragmentObjekat = (NekiFragment) fragment; // Definisanje aktivnosti (this) kao listener objekta: nekiFragmentObjekat.setOnNekiInterfejsListener(this); } } . . . // Definisanje akcije kada se okine dogadjaj @Override public void callbackMetoda() { // Neka akcija koja se izvršava po okidanju 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
// OVAJ DEO OSTAJE ISTI: public interface NekiInterfejs { void callbackMetoda(); } // STARO: private NekiInterfejs mListener; // NOVO: Umesto mListener (najčešće sa nazivom "mListener") se koristi lista objekata (mListeners) private final ArrayList<NekiInterfejs> mListeners = new ArrayList<>(); // STARO: public void setListener(NekiInterfejs value){ this.mListener = value; } // NOVO: U setter metodi se sada dodaje novi listener u Array public void registerListener (NekiInterfejs value){ if(!mListeners.contains(value)) mListeners.add(value); } // NOVO: Nova metoda koja izbacuje listener kada više ne treba iz Array-a public void unregisterListener (NekiInterfejs value){ if(mListeners.contains(value)) mListeners.remove(value); } // STARO okidanje dogadjaja je pozivanje callbackMetode: mListener.callbackMetoda(); // NOVO sada za svaki listener iz niza mora da se pozove metoda stoga je potrebna nova metoda koja sadrži petlju public void pozivanjeCallbackMetode(){ for(NekiInterfejs listener : mListeners){ listener.callbackMetoda(); } } // Sada umesto pozivanja callback metode za okidanje dogadjaja pozivamo metodu pozivanjeCallbackMetode() pozivanjeCallbackMetode(); |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import android.support.v4.app.Fragment; public class MyListFragment extends Fragment { // Promenjiva listener je objekat koji predstavlja instancu fragmenta private OnItemSelectedListener listener; // Definisanje interfejsa tj. dogadjaja koje će fragment koristii za komunikaciju public interface OnItemSelectedListener { //callback metoda čijim se pozivanjem kasnije okida dogadjaj public void onRssItemSelected(String link); } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnItemSelectedListener) { // listener objekat će biti cela aktivnost na koju se zakači fragment listener = (OnItemSelectedListener) context; } else { throw new ClassCastException(context.toString() + " must implement MyListFragment.OnItemSelectedListener"); } } // Unutar ove metode se "okida" dogadjaj iz toga razloga pozivanje ove metode odmah okida dogadjaj public void onSomeClick(View v) { listener.onRssItemSelected("some link"); } } |
Aktivnost:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Activity implementira interfejs definisan u fragmentu public class RssfeedActivity extends AppCompatActivity implements MyListFragment.OnItemSelectedListener { DetailFragment fragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rssfeed); fragment = (DetailFragment) getSupportFragmentManager().findFragmentByTag(Detailfragment.TAG)); } // Implementiranje metode iz interfejsa i u okviru nje akcije koja treba da se izvrši nakon okidanja dogadjaja @Override public void onRssItemSelected(String link) { if (fragment != null && fragment.isInLayout()) { fragment.setText(link); } } } |
Komunikacija Fragment – Fragment
Komunikacija izmedju njih se može ostvariti na dva načina:
- Koristeći aktivnost
- Koristeći deljeni “ViewModel” (pogledajte više o ovome u članku: “Komunikacija izmedju fragmenata koristeći ViewModel i LiveData”.
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public class PrviFragment extends Fragment { // Interfejs public interface OnPrviFragmentListener { void onMessageFromPrviFragment(String text); } // Interfejs objekat tzv. listener private OnPrviFragmentListener mListener; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_prvi, container, false); Button button = v.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String message = "Hello, Blue! I'm Green."; // Okidanje dogadjaja mListener.onMessageFromPrviFragment(message); } }); return v; } @Override public void onAttach(Context context) { super.onAttach(context); // Definisanje listener objekat koji osluškuje kao aktivnost if (context instanceof OnPrviFragmentListener) { mListener = (OnPrviFragmentListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnPrviFragmentListener"); } } @Override public void onDetach() { super.onDetach(); mListener = null; } } |
Aktivnost
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class MainActivity extends AppCompatActivity implements PrviFragment.OnPrviFragmentListener { private static final String PRVI_TAG = "prvi"; private static final String DRUGI_TAG = "drugi"; PrviFragment mPrviFragment; DrugiFragment mDrugiFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getSupportFragmentManager(); mPrviFragment = (PrviFragment) fragmentManager.findFragmentByTag(PRVI_TAG); if (mPrviFragment == null) { mPrviFragment = new PrviFragment(); fragmentManager.beginTransaction().add(R.id.prvi_fragment_container, mPrviFragment, PRVI_TAG).commit(); } mDrugiFragment = (DrugiFragment) fragmentManager.findFragmentByTag(DRUGI_TAG); if (mDrugiFragment == null) { mDrugiFragment = new DrugiFragment(); fragmentManager.beginTransaction().add(R.id.drugi_fragment_container, mDrugiFragment, DRUGI_TAG).commit(); } } // Definisanje callback metode tj. akcije nakon okidanja dogadjaja @Override public void onMessageFromPrviFragment(String message) { mDrugiFragment.youveGotMail(message); } } |
DrugiFragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class DrugiFragment extends Fragment { private TextView mTextView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_drugi, container, false); mTextView = v.findViewById(R.id.textview); return v; } // Public metoda koju Aktivnost koristi da prosledi drugom fragmentu poruku public void youveGotMail(String message) { mTextView.setText(message); } } |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class nekiAdapter extends RecyclerView.Adapter<nekiAdapter.ViewHolder> { //Interfejs public interface nekiListener { void onItemClick(ControllerMap controller); } nekiListener mListener; //Setter metoda public void setListener(nekiListener value){ mListener = value; } . . . public class ViewHolder extends RecyclerView.ViewHolder { ... public ViewHolder(final View itemView) { super(itemView); ButterKnife.bind(this, itemView); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(mListener != null){ //Okidanje dogadjaja mListener.onItemClick(mMap); } } }); } } |
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:
1 2 3 4 5 6 |
mAdapter.setListener(new nekiAdapter.nekiListener() { @Override public void onItemClick(ControllerMap map) { // Neka akcija koja se izvršava kada se okine dogadjaj } }); |
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:
1 |
new { } |
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:
1 |
new NazivInterfejsa () { }; |
Takodje moramo da implementiramo metode interfejsa:
1 2 3 4 5 6 |
new NazivInterfejsa () { @Override public void nekaCallBacKMetoda() { // kod koji izvšava akciju nakon okidanja dogadjaja } }; |
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:
1 |
new PrvaKlasa.NazivInterfejsa () { }; |
Pored ovoga je potrebno da implementiramo sve abstraktne metode ovog interfejsa:
1 2 3 4 5 6 |
new PrvaKlasa.NazivInterfejsa () { @Override public void interfejsMetoda() { // neki kod u callback metodi } }; |
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:
1 2 3 4 5 6 |
PrvaKlasa.NazivInterfejsa listenerObjekat = new PrvaKlasa.NazivInterfejsa() { @Override public void callBackMetoda() { // kod koji izvšava akciju nakon okidanja dogadjaja } }; |
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:
1 |
new { } |
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:
1 |
new NazivInterfejsa () { }; |
Takodje moramo da implementiramo metode interfejsa:
1 2 3 4 5 6 |
new NazivInterfejsa () { @Override public void nekaCallBacKMetoda() { // kod koji izvšava akciju nakon okidanja dogadjaja } }; |
1 2 3 |
public void setOnClickListener(View.OnClickListener onClickListener) { this.onClickListener = onClickListener; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public interface NekiInterfejs { void callbackMetoda(); } private NekiInterfejs mListener; public void setListener(NekiInterfejs value){ this.mListener = value; } . . . // negde u prvoj klasi se pozove callback metoda i tako definiše trenutak kada se "okida" dogadjaj if(mListener != null){ mListener.callbackMetoda(); } |
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():
1 2 3 4 5 6 7 |
PrvaKlasa nekiCustomView; nekiCustomView = findViewById(R.id.ubacenViewPrveKlase); . . . // Pozivanje setter metode koristeći ubačeni view nekiCustomView.setListener(anonimniObjekatKojiImplementiraInterfejs); |
2) Ubačen programirano
2.a) Kreiranjem objekta PrveKlase
1 2 |
PrvaKlasa nekiCustomView = new PrvaKlasa(); nekiCustomView.setListener(listenerObjekat); |
“listenerObjekat” je ustvari anonimni objekat kreiran “on the fly”:
1 2 3 4 5 6 |
nekiCustomView.setListener(new PrvaKlasa.nekiInterfejs() { @Override public void callbackMetoda() { // akcija po okidanju dogadjaja } }); |
2.b) DrugaKlasa implementira interfejs
U slučaju da cela klasa implementra interfejs onda se koristi:
1 |
PrvaKlasa nekiCustomView = new PrvaKlasa(LayoutInflater.from(this), null); |
Prva klasa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public interface NekiInterfejs { void callbackMetoda(); } private ArrayList<NekiInterfejs> mListeners = new ArrayList<>(); //setter metoda public void registerListener (NekiInterfejs value){ if(!mListeners.contains(value)) mListeners.add(value); } public void unregisterListener (NekiInterfejs value){ if(mListeners.contains(value)) mListeners.remove(value); } // pripremna metoda za okidanje dogadjaja public void pozivanjeCallbackMetode(){ for(NekiInterfejs listener : mListeners){ listener.callbackMetoda(); } } . . . // Negde u prvoj klasi posredno pozvati callbackMetoda() pozivanjem metode pozivanjeCallbackMetode() jer ćemo tako da okinemo dogadjaj pozivanjeCallbackMetode(); |
Druga klasa
1 2 3 4 5 6 7 |
// registerovanje objekta koji će da bude listener objekatKojiImplemetiraInterfejs.registerListener(objekatKojiOsluškuje); . . . // Sada umesto pozivanja callback metode za okidanje dogadjaja pozivamo metodu pozivanjeCallbackMetode() pozivanjeCallbackMetode(); |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class NekiFragment extends Fragment { public interface NekiInterfejs { void callbackMetoda(); } private NekiInterfejs mListener; . . . // @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof NekiInterfejs) { // Dodeljivanje aktivnosti "mListener" pri kačenju fragmenta mListener = (NekiInterfejs) context; } else { throw new ClassCastException(context.toString() + " must implement NekiFragment.NekiInterfejs"); } } } . . . public void nekaMetoda() { // Okidanje dogadjaja: mListener.callbackMetoda(); } } |
U aktivnosti koja implementira interfejs se samo override callbackMetoda da bi definisali akciju po okidanju dogadjaja:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static class MainActivity extends Activity implements NekiFragment.OnHeadlineSelectedListener{ NekiFragment fragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fragment = (NekiFragment) getSupportFragmentManager().findFragmentById(R.id.neki_fragment); } . . . // Definisanje akcije kada se okine dogadjaj @Override public void callbackMetoda() { if (fragment != null && fragment.isInLayout()) { // Neka akcija koja se izvršava po okidanju dogadjaja } } } |