RecyclerView — component ng Android Jetpack para sa mahusay na pagpapakita ng malalaking set ng data sa anyo ng mga listahan at grid. Hindi tulad ng ListView, ang RecyclerView ay muling gumagamit ng mga ViewHolder at pinamamahalaan ang layout sa pamamagitan ng LayoutManager. Ayon sa Google (Android Developers, 2026), pinoproseso ng RecyclerView ang hanggang 60 frames bawat segundo kapag nag-i-scroll ng listahan na may 1000+ elemento dahil sa ViewHolder pool at DiffUtil difference algorithm. Magbasa pa tungkol sa arkitektura ng Android apps sa aming artikulo tungkol sa ListView.
Mga Pangunahing
RecyclerView — component ng Android Jetpack library, ipinakilala sa Google I/O 2014 bilang bahagi ng Android Support Library v7. Ito ay dinisenyo para sa pagpapakita ng mga dynamic na listahan at grid na may mataas na pagganap. Ang RecyclerView ay lumitaw bilang kapalit ng ListView at nilulutas ang pangunahing problema ng hinalinhan nito — kakulangan ng sapilitang muling paggamit ng mga ViewHolder.
Ang arkitektura ng RecyclerView ay binuo sa pattern na Model-View-Adapter: ang data ay naka-imbak sa modelo (List, LiveData, Flow), ang pagpapakita ay pinamamahalaan ng Adapter, at ang layout ay kinokontrol ng LayoutManager. Ang bawat component ay responsable para sa sarili nitong lugar ng responsibilidad, na ginagawang flexible at napapalawak ang system.
Ayon sa survey ng Android Developers (2025), ang RecyclerView ay ginagamit sa 78% ng Android apps mula sa top-100 ng Google Play. Ang pagganap ng pag-scroll ng listahan na may 1000+ elemento ay nananatiling stable sa 60 FPS sa mga mid-range device dahil sa tatlong mekanismo: ViewHolder pool, differential na pagkalkula ng mga update, at asynchronous na pag-load ng data.
Ang RecyclerView ay optimal para sa: mga listahan na may hindi alam na bilang ng mga elemento (feeds, chat, logs), grid na may N column (galeri, katalogo), cascade layout (Pinterest board), mga listahan na may animation ng pagdagdag/pag-alis ng elemento. Para sa simpleng screen na may 2-5 elemento, ang LinearLayout o ScrollView ay mas simple at mas mahusay — nagdadagdag ng overhead ang RecyclerView.
Para gamitin ang RecyclerView, idagdag ang dependency sa build.gradle (app): implementation 'androidx.recyclerview:recyclerview:1.4.0'. Minimum API version — 14 (Android 4.0). Simula sa AndroidX RecyclerView 1.3.0, available ang mga module na recyclerview-selection para sa pagpili ng elemento at recyclerview-swipe para sa pag-swipe.
RecyclerView ay nahahati sa tatlong independiyenteng layer: Pinamamahalaan ng Adapter ang data at mga ViewHolder, ang LayoutManager ay responsable para sa pagpoposisyon ng mga elemento, kinokontrol ng ItemAnimator ang mga animation. Ang ganitong arkitektura ay nagpapahintulot na palitan ang anumang layer nang hindi binabago ang iba — halimbawa, lumipat mula sa LinearLayoutManager patungong GridLayoutManager nang hindi binabago ang code ng Adapter.
| Component | Responsibilidad | Pagpapatupad |
|---|---|---|
| Adapter | Paggawa ng mga ViewHolder, pagbind ng data sa View, pag-notify ng mga pagbabago | RecyclerView.Adapter<VH> |
| ViewHolder | Pag-cache ng mga reference sa View, pag-iwas sa findViewById() | RecyclerView.ViewHolder |
| LayoutManager | Pagpoposisyon ng mga elemento, pagtukoy ng direksyon ng scroll | LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager |
| ItemAnimator | Animation ng pagdagdag, pag-alis, paglipat ng mga elemento | DefaultItemAnimator |
| ItemDecoration | Pagguhit ng mga separator, espasyo, background sa pagitan ng mga elemento | RecyclerView.ItemDecoration |
Ikot ng trabaho: (1) Gumagawa ang Adapter ng ViewHolder sa pamamagitan ng onCreateViewHolder(), (2) Tinutukoy ng LayoutManager ang posisyon ng bagong View sa screen, (3) Binibind ng Adapter ang data sa pamamagitan ng onBindViewHolder(), (4) habang nag-i-scroll, ang mga ViewHolder na lumabas sa screen ay pumupunta sa pool (RecycledViewPool) para muling magamit. Sa halip na gumawa ng bagong View, muling ginagamit ang mga umiiral na — ito ang pangunahing mekanismo ng pagganap.
ViewHolder — isang klase na nag-iimbak ng mga reference sa View (TextView, ImageView, atbp.) sa loob ng elemento ng listahan. Kung walang ViewHolder, sa bawat scroll tatawag ang system ng findViewById() — isa sa pinakamahal na operasyon ng Android UI. Inaalis ng ViewHolder ang tawag na ito sa pamamagitan ng pag-alala ng mga reference pagkatapos ng unang pagbind.
// Modelo ng data
data class Article(
title: String,
summary: String,
author: String,
imageUrl: String?
)
// ViewHolder — nag-iimbak ng mga reference sa View
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)
fun bind(article: Article) {
titleText.text = article.title
summaryText.text = article.summary
authorText.text = article.author
}
}
Praktikal na patakaran: bawat View sa layout ng item ay dapat ideklara bilang property ng ViewHolder na may findViewById() nang eksaktong isang beses — sa constructor. Ang bind() method ay hindi tumatawag ng findViewById(), nagtatakda lamang ito ng text, kulay, at listener. Ang pamamaraang ito ay nagpapabilis ng pag-scroll ng listahan ng 30-50% ayon sa Google (Android Performance Patterns, 2025).
LayoutManager tumutukoy kung paano inaayos ang mga elemento sa loob ng RecyclerView. Bilang default, nagbibigay ang Android ng tatlong implementasyon: LinearLayoutManager, GridLayoutManager, at StaggeredGridLayoutManager. Ang pagpili ng LayoutManager ay nakakaapekto sa persepsyon ng data ng user at pagganap ng scroll.
| LayoutManager | Pagkakaayos | Kailan gagamitin | Paraan ng pag-set |
|---|---|---|---|
| LinearLayoutManager | Listahan (vertical o horizontal) | Feeds, chat, notipikasyon, listahan ng produkto | LinearLayoutManager(context) |
| GridLayoutManager | Grid na may N column | Galeri, katalogo, icon, grid ng larawan | GridLayoutManager(context, spanCount) |
| StaggeredGridLayoutManager | Cascade grid (iba't ibang taas ng elemento) | Pinterest board, tala, masonry gallery | StaggeredGridLayoutManager(spanCount, orientation) |
// Pag-set up ng LinearLayoutManager sa Fragment
class ArticleListFragment : Fragment() {
private lateinit var binding: FragmentArticleListBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = ArticleAdapter(articlesList)
// Optimisasyon: fixed size + pool
recyclerView.setHasFixedSize(true)
recyclerView.setItemViewCacheSize(20)
// Separator sa pagitan ng mga elemento
val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
recyclerView.addItemDecoration(divider)
}
}
Pagganap: Ang LinearLayoutManager na may vertical scroll ay ang pinakamabilis na opsyon, dahil ang mga elemento ay inaayos nang sunud-sunod at kinakalkula ng LayoutManager ang mga posisyon sa O(1). Ang GridLayoutManager ay nangangailangan ng karagdagang pagkalkula para sa paghahati sa mga column, ngunit nananatiling mahusay sa fixed spanCount. Ang StaggeredGridLayoutManager ay ang pinakamabagal sa tatlo, dahil kailangan nitong kalkulahin ang posisyon ng elemento sa cascade na isinasaalang-alang ang taas ng mga kalapit na elemento. Gamitin lamang ito kapag biswal na kinakailangan ang iba't ibang taas ng cell.
Adapter sa RecyclerView ay nagpapatupad ng Adapter pattern mula sa GoF: ginagawang ViewHolder na ipinapakita sa screen ang data (List<T>). Kapag nagbago ang data, maaaring i-notify ng Adapter ang RecyclerView sa pamamagitan ng mga method na notifyDataSetChanged(), notifyItemInserted(), at mga katulad. Ang problema ay ang notifyDataSetChanged() ay muling gumuhit ng lahat ng nakikitang elemento, na nagdudulot ng jank.
// Adapter na may DiffUtil para sa mahusay na pag-update
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {
private var articles: List<Article> = emptyList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_article, parent, false)
return ArticleViewHolder(view)
}
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
holder.bind(articles[position])
}
override fun getItemCount(): Int = articles.size
// Pag-update ng listahan sa pamamagitan ng DiffUtil
fun updateArticles(newArticles: List<Article>) {
val diffCallback = ArticleDiffCallback(articles, newArticles)
val diffResult = DiffUtil.calculateDiff(diffCallback)
articles = newArticles
diffResult.dispatchUpdatesTo(this)
}
}
// Callback para sa DiffUtil
class ArticleDiffCallback(
private val oldList: List<Article>,
private val newList: List<Article>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos].title == newList[newPos].title
}
override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
return oldList[oldPos] == newList[newPos]
}
}
DiffUtil ay gumagamit ng Eugene W. Myers' difference algorithm, na kinakalkula ang pinakamababang bilang ng mga operasyon (insert, remove, move, change) para gawing bago ang lumang listahan. Para sa listahan ng 100 elemento, gumagana ang DiffUtil sa 1-5 ms (Google, 2025). Para sa malalaking listahan (10000+), gamitin ang AsyncListDiffer o PagingDataAdapter mula sa Paging library — ginagawa nila ang mga kalkulasyon sa background.
ListView — hinalinhan ng RecyclerView, lumitaw sa Android 1.0 (API 1). Nagpapakita rin ito ng mga listahan, ngunit hindi nagpapatupad ng ViewHolder, hindi sumusuporta sa LayoutManager, at hindi maaaring mag-animate ng mga pagbabago. Ang RecyclerView ay dinisenyo bilang kapalit ng ListView na may mas mahigpit na arkitektura at mas mahusay na pagganap.
| Kriteria | RecyclerView | ListView |
|---|---|---|
| ViewHolder | Mandatory (arkitektura) | Inirerekomenda, ngunit hindi mandatory |
| LayoutManager | Pagkahati: listahan, grid, cascade | Vertical listahan lamang |
| Animation ng pagbabago | DefaultItemAnimator agad | Walang built-in na suporta |
| Pag-notify ng pagbabago | Pointwise: notifyItemInserted/Removed | notifyDataSetChanged() buong listahan lamang |
| DiffUtil | Suporta sa pamamagitan ng DiffUtil.Callback | Walang katumbas |
| Separator | ItemDecoration (custom) | android:divider (built-in) |
| Minimum na bersyon | API 14 (Android 4.0) sa pamamagitan ng AndroidX | API 1 (lahat ng bersyon) |
Rekomendasyon ng Google (Android Developers, 2026): gamitin ang RecyclerView para sa lahat ng bagong proyekto. Maaaring panatilihin ang ListView sa lumang codebase (legacy) kung saan hindi makatarungan ang gastos ng migration. Ang RecyclerView ay nagbibigay ng mas mahusay na pagganap sa 100+ elemento, sumusuporta sa horizontal scroll at grid nang walang karagdagang solusyon. Sa Android 15, ang ListView ay opisyal na minarkahan bilang bahagyang deprecated.
Mga Madalas Itanong
Suriin ang tatlong kondisyon: (1) Naka-set ang Adapter sa pamamagitan ng recyclerView.adapter = adapter, (2) Naka-set ang LayoutManager sa pamamagitan ng recyclerView.layoutManager = LinearLayoutManager(context), (3) hindi walang laman ang data sa listahan at na-notify ang adapter (notifyDataSetChanged()). Karaniwang pagkakamali — pag-set ng data pagkatapos tawagan ang notifyDataSetChanged(). Dapat italaga ang data bago ang notipikasyon. Suriin ang laki ng listahan: adapter.itemCount ay dapat mas malaki sa 0.
Pagbagal habang nag-i-scroll (jank) ay nangyayari kapag ang onBindViewHolder() ay nagsasagawa ng mabibigat na operasyon: pag-load ng mga imahe nang walang cache, findViewById() sa loob ng bind, kumplikadong kalkulasyon. Solusyon: (1) gamitin ang Glide o Coil library para sa asynchronous na pag-load ng imahe, (2) ilipat ang lahat ng findViewById() sa constructor ng ViewHolder, (3) para sa differential na kalkulasyon gamitin ang AsyncListDiffer. I-activate ang GPU profiling sa Android Studio (Profile GPU Rendering) para mahanap ang mabagal na frame.
Ibigay ang oryentasyon sa LayoutManager: LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false). Ang pangalawang parameter ay reverseLayout (false = kaliwa pakanan). Para sa horizontal grid, gamitin ang GridLayoutManager na may horizontal na oryentasyon o custom na LinearSnapHelper para sa page scroll. Ang mga horizontal na listahan ay angkop para sa mga carousel ng produkto, larawan, kategorya.
RecyclerView ay walang built-in na onClickListener (hindi tulad ng ListView na may onItemClickListener). Implement ito sa ViewHolder: magbigay ng lambda sa constructor at i-set ang itemView.setOnClickListener. Halimbawa: class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView). Tawagan ang onItemClick(article) sa loob ng bind(). Alternatibo — i-set ang RecyclerView.addOnItemTouchListener() para sa paghawak ng gesture.
RecycledViewPool — internal pool ng mga ViewHolder na naging invisible habang nag-i-scroll. Sa halip na sirain, sila ay iniimbak sa pool at muling ginagamit. Bilang default, ang pool ay nag-iimbak ng 5 ViewHolder ng bawat uri. Palakihin ang laki para sa kumplikadong layout: recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount). Kung ang RecyclerView ay may maraming uri ng View (viewType), ang pool ay nag-iimbak ng hiwalay na stack para sa bawat isa. Ang pagbabahagi ng RecycledViewPool sa pagitan ng iba't ibang RecyclerView ay nakakatipid ng memorya sa mga screen na may nested na listahan.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din