ListView, dikey listeleri görüntülemek için temel bir Android bileşenidir ve API 1'den (Android 1.0) itibaren kullanılabilir. ListView, ArrayAdapter kullanarak öğelerin bir listesini oluşturur ve özel hücre düzenlerini destekler. ListView'in ne olduğunu, nasıl yapılandırılacağını ve bu eski ama hâlâ kullanılan bileşenin hangi tuzakları gizlediğini anlayalım. IT Sectr'de, eski projelerde ListView ile karşılaşıyoruz ve Android 4.4 ile geriye dönük uyumluluğu koruyoruz. Modern alternatifle karşılaştırma için RecyclerView hakkındaki makaleyi okuyun.
Önemli Noktalar
ListView, android.widget paketinden bir ViewGroup'tur ve kaydırılabilir öğe listesi görüntülemek için tasarlanmıştır. ListView, AdapterView modelini uygular: veriler, veri öğelerini ekranda görüntülenen View'lara dönüştüren bir adaptör (Adapter) aracılığıyla iletilir. ListView, ilk sürümden (API 1) beri Android SDK'da bulunur ve ek kütüphane gerektirmez.
Mimari olarak ListView, RecyclerView'dan daha basittir: LayoutManager yerleşiktir (yalnızca dikey kaydırma), değişiklik animasyonları yoktur, ayırıcılar XML niteliği android:divider ile ayarlanır. ListView, prototip oluşturmayı basitleştiren OverScrollView ve odak vurgulamayı otomatik olarak ekler. Statista'ya (2025) göre, Play Store'daki ilk 100 Android uygulamasının yaklaşık %15'i hâlâ ListView içeriyor — çoğunlukla minimum destekli eski projeler.
ListView, 2014'te Google I/O'da RecyclerView tanıtılana kadar 2008'den 2014'e kadar Android'deki birincil liste bileşeniydi. Kısmen kullanımdan kaldırılmış (soft-deprecated) olmasına rağmen, ListView tüm Android sürümlerinde kullanılabilir durumdadır ve SDK API 35'te @Deprecated olarak işaretlenmemiştir. Projeniz Android 4.4 (API 19) ve altını destekliyorsa, RecyclerView AndroidX ve Gradle bağımlılıkları gerektirdiğinden ListView tek yerleşik seçenektir.
ArrayAdapter, ListView için en popüler adaptördür. Her öğe için bir List<T> ve bir düzen alır. Varsayılan olarak ArrayAdapter, her nesnede toString() çağırır ve metni simple_list_item_1'de (tek bir TextView) görüntüler. Özel görüntüleme için getView() öğesini geçersiz kılmanız veya oluşturucuya özel bir düzen iletmeniz gerekir.
// ArrayAdapter ile ListView (basit metin)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Rusya", "ABD", "Almanya",
"Fransa", "Japonya", "Çin", "Brezilya"
)
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
// Tıklama işleme
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter, Map verilerinden listeler için tasarlanmıştır. Bir List<Map<String, Any>> ve Map anahtarlarını düzendeki widget kimliklerine bağlamak için from/to dizisi alır. SimpleAdapter, ayrı bir adaptör sınıfı gerekmediğinde hızlı prototipler için uygundur. Dezavantajı, tür güvenliği eksikliği ve özel mantıkla karmaşıklıktır.
<!-- ListView düzeni -->
<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>
Özel adaptör, ListView için getView() yöntemini geçersiz kılar; RecyclerView bunu onCreateViewHolder + onBindViewHolder olarak ayırır. ListView'de her iki adım birleştirilmiştir, bu nedenle geliştiricinin ViewHolder modelini convertView (yeniden kullanılabilir View) ve setTag() yöntemi aracılığıyla bağımsız olarak uygulaması gerekir.
// Veri modeli
data class Product(
name: String,
price: String,
imageResId: Int
)
// ViewHolder ile özel adaptör
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// Önbellekleme için ViewHolder
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
}
}
RecyclerView'den temel fark: ListView'de getView() yöntemi, bir öğe ekranda her göründüğünde çağrılır. convertView ve ViewHolder kullanılmazsa, her kaydırma yeni bir View nesnesi oluşturur ve 50'den fazla öğe içeren listelerde bellek sızıntısına ve gecikmeye neden olur. Özel düzenlere sahip ListView için view.tag ile ViewHolder zorunlu bir optimizasyondur.
ListView'in Google'ın RecyclerView'de düzelttiği dört mimari sınırlaması vardır: (1) yalnızca dikey yönlendirme — ızgaralar, yatay listeler veya basamaklı düzen yok; (2) ayrıntılı bildirim yok — herhangi bir veri değişikliği notifyDataSetChanged() ile tam yeniden çizim tetikler ve sarsıntıya neden olur; (3) yerleşik öğe ekleme/kaldırma animasyonu yok; (4) sorumluluk ayrımı yok — LayoutManager yerleşiktir, ItemAnimator yoktur.
<!-- item_product.xml — ListView öğe düzeni -->
<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>
Pratik sonuçlar: 20 öğeye kadar olan listelerde ListView, RecyclerView kadar hızlı çalışır. 100'den fazla öğede fark belirginleşir: fark hesaplamaları ve zorunlu ViewHolder eksikliği nedeniyle ListView'de FPS düşmeye başlar. Sık güncellenen listeler (sohbet, akış, günlükler) için ListView uygun değildir — Paging 3 ile RecyclerView kullanın. IT Sectr'de, 50'den fazla öğe içeren eski ekranlar üzerinde çalışırken ListView'i RecyclerView'e geçiriyoruz.
ListView, kod olmadan hızlı yapılandırma için bir dizi XML niteliği sağlar: android:divider (ayırıcı rengi/çizilebiliri), android:dividerHeight (kalınlık), android:scrollbars (kaydırma çubukları), android:fastScrollEnabled (tutamakla hızlı kaydırma), android:choiceMode (seçim modu: singleChoice, multipleChoice). android:entries niteliği, adaptör olmadan bir kaynak dizisinden (string-array) liste doldurmayı sağlar.
| Nitelik | Açıklama | Örnek değer |
|---|---|---|
| android:divider | Ayırıcı rengi veya çizilebiliri | @color/gray_light |
| android:dividerHeight | px/dp cinsinden ayırıcı kalınlığı | 1dp |
| android:entries | Kaynaklardan dizi dizisi | @array/countries |
| android:choiceMode | Öğe seçim modu | singleChoice |
| android:fastScrollEnabled | Hızlı kaydırma | true |
| android:overlapAnchor | Açılır listeler için çapa (API 21+) | true |
Seçim modu: choiceMode="singleChoice", seçili öğenin vurgulandığı RadioButton stilini etkinleştirir. choiceMode="multipleChoice" — birden çok öğe için onay kutuları. Seçim verilerine listView.checkedItemPosition veya listView.checkedItemIds aracılığıyla erişilebilir. Önemli: choiceMode ile ListView arka plan için StateListDrawable kullanır — standart mavi tasarıma uymuyorsa kendi seçicinizi ayarlayın.
Seçim, ListView ve RecyclerView arasında proje gereksinimlerine bağlıdır. RecyclerView modern standarttır, ancak ListView, eski kod için minimum bakım maliyeti olan basit ekranlar için haklı gösterilebilir. Tüm temel parametrelerde karşılaştırma:
| Parametre | ListView | RecyclerView |
|---|---|---|
| Kütüphane boyutu | SDK'ya yerleşik (≈60 KB) | AndroidX (bağımlılıklarla ≈300 KB) |
| Minimum sürüm | API 1 | API 14 (AndroidX ile) |
| Yönlendirme | Yalnızca dikey | Dikey, yatay, ızgara, kademeli |
| ViewHolder | Manuel uygulama | Mimari gereği zorunlu |
| Animasyon | Yalnızca soldurma geçişleri | DefaultItemAnimator (ekle, kaldır, taşı, değiştir) |
| Ayrıntılı güncellemeler | Hayır (yalnızca notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Tıklama dinleyicisi | onItemClickListener (yerleşik) | ViewHolder aracılığıyla (yerleşik değil) |
| Performans | 50 öğeye kadar listeler için yeterli | 1000+ öğe için sabit 60 FPS |
Sonuç: yeni projeler için RecyclerView kullanın. ListView'i yalnızca geçişin makul olmayan şekilde pahalı olduğu eski kodda bırakın. Material Design 3 yönergelerine (2026) göre, herhangi bir karmaşıklıktaki listeler, modern performans ve erişilebilirlik standartlarını karşılamak için RecyclerView kullanmalıdır.
Sıkça Sorulan Sorular
ListView, Android SDK API 35'te @Deprecated olarak işaretlenmemiştir, ancak Google yeni projelerde kullanılmasını önermez (soft-deprecated). Bileşen tüm Android sürümlerinde çalışmaya devam eder. RecyclerView, 2014'ten beri resmi değiştirme olarak kullanılmaktadır. Yeni bir projeye başlıyorsanız — RecyclerView'i seçin. Mevcut bir projeyi sürdürüyorsanız — ListView değişiklik yapılmadan çalışmaya devam edecektir.
ListView, addHeaderView(View, Object, boolean) ve addFooterView(View, Object, boolean) yöntemlerini destekler. Header ve footer, genel öğe listesine eklenir ancak adaptöre katılmaz — her zaman listenin başında ve sonunda görüntülenir. Önemli: adaptörü ayarlamadan (setAdapter()) önce header/footer ekleyin, aksi takdirde dahil edilmezler. Bir header ekledikten sonra, öğe konum kimlikleri header sayısı kadar kayar.
Hem ListView hem de ScrollView dikey hareketleri işler, bu da bir çakışma yaratır: dış ScrollView kaydırmayı durdurur ve ListView kaydırmaz. Çözümler: (1) ListView'i ScrollView içine yerleştirmeyin — NestedScrollingChild ile RecyclerView kullanın, (2) ScrollView'i NestedScrollView (API 21+) ile değiştirin, (3) ScrollView içinde sabit yükseklik (android:layout_height="...dp") ile ListView kullanın, ancak bu uyarlanabilirliği bozacaktır.
ListView'de veri temizleme, adaptör türüne bağlıdır. ArrayAdapter için: adapter.clear() ve adapter.addAll(newList) ardından adapter.notifyDataSetChanged(). Özel adaptör için: veri listesini güncelleyin ve notifyDataSetChanged() çağırın. Not: listView.invalidateViews() yalnızca View'ları yeniden çizer ancak verileri güncellemez. Verileri her zaman adaptör aracılığıyla değiştirin, doğrudan değil.
ListView öğesinin yüksekliği, öğe düzeninde ayarlanır. Düzen layout_height="wrap_content" kullanıyorsa, yükseklik içeriğe göre uyarlanır. Tüm öğeler için sabit bir yükseklik için, getView() içinde LayoutParams ayarlayan özel bir adaptör kullanın: view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Farklı yükseklikler için farklı görünüm türleriyle RecyclerView kullanın.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun