U prethodnom odeljku smo govorili o bazama podataka. Sada ćemo prikazati kako se mogu rezultati baze podataka prikazati preko dinamične liste. RecyclerView omogućava odličan način za prikazivanje elemenata neke kolekcije na ekranu na unapred određen način. Prednost korišćenja RecyclerView je značajna ušteda performansi, jer se prilikom renderovanja elemenata kolekcije, renderuju samo oni koji će moći da se vide na ekranu. Na ostale elemente se ne troše resursi dok ne budu mogli da se vide. Načini prikazivanje elemenata su razni i korisnik može sam da definiše kako će elementi da mu se prikažu, što je takođe prednost korišćenja RecyclerView. Neki od njih su prikazani na slici ispod.
Da bismo ispravno radili sa RecyclerView, potrebni su nam sledeći elementi:
Sada se može RecyclerView urediti kao i svi ostali elementi. Ono što je dalje potrebno uraditi je definisati layout menadžera. Layout menadžer će nam objasniti kako se elementi raspoređuju u prozoru. Recimo, moguće je koristiti LinearLayoutManager koji će elemente rasporediti jedne ispod drugih. Drugi način je koristiti GridLayoutManager koji će elemente prikazati kao na trećem telefonu na gornjoj slici. Primera radi, prikazaćemo kako funkcioniše LinearLayoutManager. Za detalje o GridLayoutManager, savetujemo da se pogleda tutorijal ovde. Dakle, da bi se dodao LinearLayoutManager, potrebno je definisati parametar app:layoutManager u XML kodu, kod RecyclerView, kao u kodu dole.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
Dalje je potrebno definisati izgled jedne kartice koja će se prikazati. Za te potrebe, pravi se jedan XML fajl i dizajnira se na isti način kao što je to rađeno za fragmente i prozore.
Sledeća stavka je implementacija adaptera, nakon čega ćemo napraviti klasu ViewHolder unutar našeg adaptera. Adapter je potklasa od RecyclerView.Adapter koja je generička, što znači da moramo da prosledimo i tip koji ćemo koristiti. Tip koji treba proslediti je klasa ViewHolder koju ćemo kasnije napraviti. Da nam se ne bi prijavljivale greške, dobro je nekada postaviti bilo koju klasu privremeno, a potom je zameniti pravom. Dakle, klasa bi trebalo da izgleda ovako:
class AAdapter: RecyclerView.Adapter<AViewHolder>() {}
Nakon deklarisanja klase, prijaviće se greška da nisu potrebni metodi implementirani. Potrebno je svaki od njih implementirati kako bi naš adapter mogao da radi. Najpre, definišemo listu podataka koji će biti prikazani sa:
var data = listOf<A>()
U slučaju da se podaci u nekom trenutku promene, moramo obavestiti RecyclerView da je došlo do promene, kako bi se izgled ažurirao. Sledeći kod se često ponavlja. On ažurira listu, pa onda obaveštava o promeni.
var data = listOf<A>() set(value) { field = value notifyDataSetChanged() }
Metod getItemCount treba da vrati broj elemenata koje treba prikazati. Njega ćemo predefinisati tako da vrati broj elemenata u listi:
override fun getItemCount() : Int { return data.size }
Drugi metod koji mora da se implementira je onBindViewHolder koji će primiti ViewHolder objekat i poziciju elementa koji treba predstaviti, a metod treba da poveže odgovarajući element ViewHolder objektom. Ovde se može koristiti bind metod klase ViewHolder koji ćemo kasnije da implementiramo. Metod onBindViewHolder bi trebalo da izgleda ovako:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = data[position] holder.bind(item) }
Treći metod koji je potrebno implementirati je onCreateViewHolder koji će da prosledi RecyclerView način na koji treba da se prikažu informacije. Metod prima dva argumenta: parent i viewType. Prvi argument je ViewGroup objekaT koji drži ViewHolder, a drugi argument se koristi kada ima više elemenata u RecyclerView, jer je tada potrebno znati na koji se element odnosi. Za sada ćemo delegirati ovaj posao metodu koji ćemo ubrzo definisati.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder.from(parent) }
Klasu ViewHolder ćemo definisati unutar adaptera. Klasa ViewHolder je potklasa klase RecyclerView.ViewHolder. Konstruktoru ćemo proslediti View objekat vezan za izgled elementa koji treba da se prikaže. Recimo, ako imamo dva tekstualna polja za ime i prezime osobe koju izvlačimo iz baze podataka, to će izgledati ovako:
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){ val first: TextView = itemView.findViewById(R.id.first) val second: TextView = itemView.findViewById(R.id.second)
Dalje, moramo definisati metod bind koji će samo povezati podatke za polja. Recimo, na ovom primeru se samo u atribut text stavlja tekstualna reprezentacija elemenata.
fun bind(item: A) { first.text = item.first.toString() second.text = item.second.toString() }
Naravno, izgled metoda će zavisiti od toga šta želimo da prikažemo i koje elemente imamo u klasi.
Sledeće što nam treba je metod from. Njega definišemo kao companion object. Metod će samo napraviti View objekat na osnovu XML fajla koji opisuje jedan element (ovde prikaz_elementa.xml), pa potom napraviti ViewHolder od tog objekta koji ćemo samo proslediti kao povratnu vrednost.
companion object { fun from(parent: ViewGroup): ViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val view = layoutInflater.inflate(R.layout.prikaz_elementa, parent, false) return ViewHolder(view) }
Sada moramo povezati adapter sa RecyclerView. U fragmentu u kojem se nalazi RecyclerView ćemo u metodu onCreateView (u slučaju aktivnosti, u onCreate) kreirati adapter, vezati ga za RecyclerView i onda ga napuniti podacima. Kada to uradimo, dodajemo i posmatrača koji će pratiti promene podataka. Takođe, da bi ovo radilo, moramo promeniti povratnu vrednost metoda getAllElements (ili bilo kog drugog koji vraća neku listu koju želimo prikazati) u LiveData<List<A>> To bi izgledalo ovako:
val adapter = AAdapter() binding.id_recyclerview.adapter // izdvajamo sve elemente baze val podaci = database.getAllElements() AViewModel.podaci.observe(viewLifecycleOwner, Observer { it?.let { adapter.data = it } })