ListView — komponen dasar Android untuk menampilkan daftar vertikal, tersedia sejak API 1 (Android 1.0). ListView membuat daftar dari elemen menggunakan ArrayAdapter dan mendukung tata letak sel kustom. Memahami apa itu ListView, cara mengaturnya, dan jebakan apa yang disembunyikan komponen lama namun masih digunakan ini. Di IT Sectr kami menemukan ListView di proyek legacy dan mendukung kompatibilitas mundur dengan Android 4.4. Untuk perbandingan dengan alternatif modern baca artikel tentang RecyclerView.
Poin Utama
ListView — adalah grup View dari paket android.widget, yang dirancang untuk menampilkan daftar elemen yang dapat digulir. ListView mengimplementasikan pola AdapterView: data dikirimkan melalui adaptor (Adapter) yang mengonversi elemen data menjadi View yang ditampilkan di layar. ListView ada di Android SDK sejak versi pertama (API 1) dan tidak memerlukan pustaka tambahan.
Arsitektur ListView lebih sederhana dari RecyclerView: LayoutManager sudah terintegrasi (hanya gulir vertikal), animasi perubahan tidak ada, pemisah diatur melalui atribut XML android:divider. ListView secara otomatis menambahkan OverScrollView dan penyorotan saat fokus, yang menyederhanakan pembuatan prototipe. Menurut data Statista (2025), sekitar 15% aplikasi Android di top-100 Play Store masih mengandung ListView — terutama proyek legacy dengan dukungan minimal.
ListView adalah komponen daftar utama di Android dari 2008 hingga 2014, ketika RecyclerView diperkenalkan di Google I/O 2014. Meskipun berstatus sebagian usang (soft-deprecated), ListView tetap tersedia di semua versi Android dan tidak ditandai sebagai @Deprecated di SDK API 35. Jika proyek Anda mendukung Android 4.4 (API 19) dan lebih rendah, ListView adalah satu-satunya pilihan bawaan, karena RecyclerView memerlukan AndroidX dan dependensi gradle.
ArrayAdapter — adaptor paling populer untuk ListView. Ia menerima List<T> dan tata letak untuk setiap elemen. Secara default ArrayAdapter memanggil toString() untuk setiap objek dan menampilkan teks di simple_list_item_1 (satu TextView). Untuk tampilan kustom perlu menimpa getView() atau mengirimkan tata letak kustom di konstruktor.
// ListView dengan ArrayAdapter (teks sederhana)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Rusia", "AS", "Jerman",
"Prancis", "Jepang", "Cina", "Brasil"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
countries
)
binding.listView.adapter = adapter
// Penanganan klik
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter ditujukan untuk daftar dari data Map. Ia menerima List<Map<String, Any>> dan array from/to untuk mengikat kunci Map dengan ID widget di tata letak. SimpleAdapter nyaman untuk prototipe cepat ketika kelas adaptor terpisah tidak diperlukan. Kekurangan — tidak adanya type-safe dan kompleksitas dengan logika kustom.
<!-- Tata letak dengan ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/divider"
android:dividerHeight="1px"
android:scrollbars="vertical" />
</LinearLayout>
Adaptor kustom untuk ListView menimpa metode getView(), yang RecyclView bagi menjadi onCreateViewHolder + onBindViewHolder. Di ListView kedua langkah digabungkan, sehingga pengembang harus mengimplementasikan pola ViewHolder secara mandiri melalui convertView (View yang digunakan ulang) dan tag setTag().
// Model data
data class Product(
name: String,
price: String,
imageResId: Int
)
// Adaptor kustom dengan ViewHolder
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// ViewHolder untuk caching
private class ViewHolder(view: View) {
val nameText: TextView = view.findViewById(R.id.tvName)
val priceText: TextView = view.findViewById(R.id.tvPrice)
val imageView: ImageView = view.findViewById(R.id.ivProduct)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(R.layout.item_product, parent, false)
.also { it.tag = ViewHolder(it) }
val holder = view.tag as ViewHolder
val product = getItem(position)
holder.nameText.text = product.name
holder.priceText.text = product.price
holder.imageView.setImageResource(product.imageResId)
return view
}
}
Perbedaan utama dari RecyclerView: di ListView metode getView() dipanggil setiap kali elemen muncul di layar. Jika tidak menggunakan convertView dan ViewHolder, setiap gulir membuat objek View baru, yang menyebabkan kebocoran memori dan lag pada daftar dengan 50+ elemen. ViewHolder dengan view.tag — optimasi wajib untuk ListView dengan tata letak kustom.
ListView memiliki empat keterbatasan arsitektur yang diperbaiki Google di RecyclerView: (1) hanya orientasi vertikal — tidak ada grid, daftar horizontal, atau kaskade; (2) tidak adanya notifikasi titik — setiap perubahan data menyebabkan penggambaran ulang penuh melalui notifyDataSetChanged(), yang menciptakan sentakan; (3) tidak ada animasi bawaan untuk penambahan/penghapusan elemen; (4) tidak ada pemisahan tanggung jawab — LayoutManager sudah terintegrasi, ItemAnimator tidak ada.
<!-- item_product.xml — tata letak elemen ListView -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:id="@+id/ivProduct"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="12dp">
<TextView
android:id="@+id/tvName"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPrice"
android:textSize="14sp"
android:textColor="@color/price" />
</LinearLayout>
</LinearLayout>
Konsekuensi praktis: pada daftar hingga 20 elemen ListView bekerja secepat RecyclerView. Pada 100+ elemen perbedaan menjadi terlihat: ListView mulai kehilangan FPS karena tidak adanya perhitungan diferensial dan ViewHolder yang dipaksakan. Untuk daftar dengan pembaruan sering (chat, feed, log) ListView tidak cocok — gunakan RecyclerView dengan Paging 3. Di IT Sectr kami memigrasi ListView ke RecyclerView pada setiap pekerjaan di layar legacy dengan lebih dari 50 elemen.
ListView menyediakan serangkaian atribut XML untuk konfigurasi cepat tanpa kode: android:divider (warna/gambar pemisah), android:dividerHeight (ketebalan), android:scrollbars (bilah gulir), android:fastScrollEnabled (gulir cepat melalui pegangan), android:choiceMode (mode pemilihan: singleChoice, multipleChoice). Atribut android:entries memungkinkan mengisi daftar dari array sumber daya (string-array) tanpa adaptor.
| Atribut | Deskripsi | Contoh Nilai |
|---|---|---|
| android:divider | Warna atau drawable pemisah | @color/gray_light |
| android:dividerHeight | Ketebalan pemisah dalam px/dp | 1dp |
| android:entries | Array string dari sumber daya | @array/countries |
| android:choiceMode | Mode pemilihan elemen | singleChoice |
| android:fastScrollEnabled | Gulir cepat | true |
| android:overlapAnchor | Jangkar untuk daftar popup (API 21+) | true |
Mode pemilihan: choiceMode="singleChoice" mengaktifkan gaya RadioButton, di mana elemen yang dipilih disorot. choiceMode="multipleChoice" — kotak centang untuk beberapa elemen. Data pemilihan tersedia melalui listView.checkedItemPosition atau listView.checkedItemIds. Penting: saat choiceMode ListView menggunakan StateListDrawable untuk latar belakang — atur pemilih sendiri jika biru standar tidak cocok dengan desain.
Pilihan antara ListView dan RecyclerView tergantung pada kebutuhan proyek. RecyclerView adalah standar modern, tetapi ListView dapat dibenarkan untuk layar sederhana dengan biaya perawatan kode lama yang minimal. Perbandingan berdasarkan semua parameter kunci:
| Parameter | ListView | RecyclerView |
|---|---|---|
| Ukuran Pustaka | Bawaan di SDK (≈60 KB) | AndroidX (≈300 KB dengan dependensi) |
| Versi Minimal | API 1 | API 14 (melalui AndroidX) |
| Orientasi | Hanya vertikal | Vertikal, horizontal, grid, kaskade |
| ViewHolder | Implementasi manual | Wajib berdasarkan arsitektur |
| Animasi | Hanya transisi fade | DefaultItemAnimator (add, remove, move, change) |
| Pembaruan Titik | Tidak (hanya notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Click listener | onItemClickListener (bawaan) | Melalui ViewHolder (tidak ada bawaan) |
| Performa | Cukup untuk daftar hingga 50 elemen | 60 FPS stabil untuk 1000+ elemen |
Kesimpulan: untuk proyek baru gunakan RecyclerView. Biarkan ListView hanya di kode legacy di mana migrasi tidak sebanding dengan biayanya. Menurut panduan Material Design 3 (2026), daftar dengan kompleksitas apa pun harus menggunakan RecyclerView untuk memenuhi standar performa dan aksesibilitas modern.
Pertanyaan yang Sering Diajukan
ListView tidak ditandai sebagai @Deprecated di Android SDK API 35, tetapi Google tidak merekomendasikan penggunaannya di proyek baru (soft-deprecated). Komponen tetap berfungsi di semua versi Android. RecyclerView adalah pengganti resmi sejak 2014. Jika Anda memulai proyek baru — pilih RecyclerView. Jika Anda memelihara proyek yang ada — ListView akan terus berfungsi tanpa perubahan.
ListView mendukung metode addHeaderView(View, Object, boolean) dan addFooterView(View, Object, boolean). Header dan footer ditambahkan ke daftar umum elemen, tetapi tidak berpartisipasi dalam adaptor — mereka selalu ditampilkan di awal dan akhir daftar. Penting: tambahkan header/footer sebelum mengatur adaptor (setAdapter()), jika tidak mereka tidak akan diperhitungkan. Setelah menambahkan header, ID posisi elemen bergeser sebanyak jumlah header.
ListView dan ScrollView keduanya memproses gerakan vertikal, yang menyebabkan konflik: ScrollView eksternal mencegat guliran, dan ListView tidak bisa scroll. Solusi: (1) jangan letakkan ListView di dalam ScrollView — gunakan RecyclerView dengan NestedScrollingChild, (2) ganti ScrollView dengan NestedScrollView (API 21+), (3) gunakan ListView dengan tinggi tetap (android:layout_height="...dp") di dalam ScrollView, tetapi ini akan merusak responsivitas.
Pembersihan data di ListView tergantung pada tipe adaptor. Untuk ArrayAdapter: adapter.clear() dan adapter.addAll(newList) diikuti dengan adapter.notifyDataSetChanged(). Untuk adaptor kustom: perbarui daftar data dan panggil notifyDataSetChanged(). Perhatian: listView.invalidateViews() hanya menggambar ulang View, tetapi tidak memperbarui data. Selalu ubah data melalui adaptor, bukan secara langsung.
Tinggi elemen ListView diatur dalam tata letak item. Jika tata letak menggunakan layout_height="wrap_content", tinggi menyesuaikan dengan konten. Untuk tinggi tetap semua elemen gunakan adaptor kustom yang mengatur LayoutParams di getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Untuk tinggi yang bervariasi gunakan RecyclerView dengan tipe tampilan yang berbeda.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga