Custom ArrayAdapter u Androidu

Uvod

custom-adapter-row

U osnovnoj verziji adaptera se koristi samo jedan String podatak koji se smešta u neki od predefinisanih android layouta sa jednim TextView-om.
Custom adapter se razlikuje od “običnog” po tome što je kod njega jedan element liste predstavljen sa više podataka te je potrebno definisati “složeniji layout” koji bi prihvatio i prikazao te podatke. Takav customLayout row liste može da sadrži više subView-ova i widgeta: sliku, tekstualne podatke (rasporedjene na svojim specifičnim pozicijama)…

Postupak kreiranja Custom ArrayAdapter-a

a) Custom model (prihvata više podataka za jedan član liste)

U ovome primeru jedan član liste sadrži dva podatka, stoga je potrebno napraviti klasu koja će da bude “modla” za kreiranje objekata koji čuvaju podatke jednog člana nekog AdapterView-a:

User.java

Sada u okviru Aktivnosti (Fragmenta) možemo na sledeći način da definišemo inicijalni niz podataka kreirajući nove objekte:

Aktivnost

ili da na osnovu njega generišemo ArrayList User objekata:

User.java

Za generisanje podataka iz JSON-a koristimo sledeći kod koji konvertuje JSON u ArrayList User objekata:

Aktivnost

b) Kreiranje AdapterView-a

Potrebno je u sklopu layout-a Aktivnosti (fragmenta) definisati mesto gde će biti smeštena lista podataka. Ovaj deo je isti kao kod običnog adaptera, kreira se jedan ListView koji će da prihvati po jedan custom row svakog člana.

activity_main.xml

c) Kreiranje custom layout-a za jedan elementa liste

Sada je potrebno napraviti custom layout, koji će se koristiti da prikaže više podataka jednog elementa liste. Ovaj deo nije bio potreban kod “običnog” adaptera ali je sada potreban zbog viška podataka, jer ne možemo da koristimo već predefinisane androidove layout-e koji prihvataju samo jedan podatak.

item_user.xml

d) Kreiranje Custom Adapter klase

U nastavku aplikacije je potrebno da definišemo Adapter klasu na osnovu koje će biti kreirana nova instanca adaptera u sklopu Aktivnosti (Fragmenta). U ovoj klasi treba da bude smešten ceo proces konvertovanja Java objekta u View popunjen podacima.
Pravimo custom adapter tako što kreiramo novu klasu koja ekstenduje ArrayAdapter klasu. Ekstendovanjm klase dobijamo metodu getView() koju možemo da “pregazimo” (override) tako da za svaki element liste uzme podatke iz modela i da sa njima popuni prethodno definisani Custom layout elementa.

NEekonomični ArrayAdapter

Ovo je primer ArrayAdaptera koji troši veliku količinu resursa pri skrolovanju ekrana, što se naručito oseti kod velikih lista podataka.

Sve mane ovog postupka kao i sam poboljšani postupak koji rešava te manjkovasti je prikazan u narednom primeru.

Poboljšani ArrayAdapter (sa primenjenim ViewHolder pattern-om)

Prva stvar na koju trebamo da obratimo pažnju je ta da se u NEekonomičnom primeru pri svakom pozivanju metode getView() kreira novi “rowView”.

Ukoliko naša lista ima veći broj elemenata koji ne mogu da stanu na jedan ekran, pri svakom novom skrolovanju se generišu novi View-i (svaki View je 1-2kB), što stalno povećava opterećenje memorije.

convertView

Upravo zbog ovog problema Android prosledjuje metodi getVIew() parametar “convertView”. Ovaj parametar se koristi da u njega “skladištimo” View (npr. naš jedan rowView), koji ćemo kasnije koristiti iznova i iznova. Da bi smo sprečili “inflating uvek istog XML-a” (koja je skupa operacija) za svaki novi element liste, jednom napravljen View ćemo sačuvati u okviru convertView promenjive.

U prethodnom kodu proveravamo da li već postoji neki sačuvan View u promenjivoj, a ukoliko ga nema, mi ćemo ga kreirati (samo prvi put) kao naš rowView, jer će nam tako definisan convertView biti uvek dostupan kao parametar getView() metode.

ViewHolder pattern

Sledeći problem koji se javlja je učestalo pozivanje metode findViewById() za svaku podstavku (subView) convertView-a. Često pozivanje metoda findViewById() opterećuje sistem i smanjuje performance aplikacije (naručito ako ima mnogo podataka).

Za rešavanje ovoga problema nam u pomoć nam priskače tzv. “ViewHolder pattern” koji se oslanja na to da već koristimo convertView, i da možemo jednostavno da targetiramo sve njegove subView-ove (samo jednom) i tako izbegnemo stalno pozivanje metode findViewById(). Stoga kreiramo jednu unutrašnju statičnu klasu koja se često naziva ViewHolder.

Članove klase možemo da povežemo sa odgovrajućim View-om tako što ćemo pozivati findViewById(), ali uz napomenu da ćemo to uraditi samo jednom i to u trenutku kada prvi put definišemo convertView-a:

U prethodnom primeru smo koristili i metodu setTag() da bi smo sačuvali ViewHolder objekat ako smo ga već jednom napravili i onda smo ga kasnije pozvali metodu getTag() i tako dobili nazad sačuvani objekat.
Korišćenje ViewHolder pattern-a ubrzava populaciju ListView-a, te sa njim dobijamo glatko i brzo učitavanje stavki. Njegova implementacija omogućava da se izbegne korišćenje “skupog” metoda findViewById() u okviru adapter-a. Ceo primer custom adapter-a:

×

×

×

×

×

Method Description
notifyItemChanged(int pos) Notify that item at position has changed.
notifyItemInserted(int pos) Notify that item reflected at position has been newly inserted.
notifyItemRemoved(int pos) Notify that items previously located at position has been removed from the data set.
notifyDataSetChanged() Notify that the dataset has changed. Use only as last resort.