ListView — βασικό στοιχείο Android για εμφάνιση κάθετων λιστών, διαθέσιμο από API 1 (Android 1.0). Το ListView δημιουργεί λίστα από στοιχεία με τη βοήθεια του ArrayAdapter και υποστηρίζει προσαρμοσμένες διατάξεις κελιών. Κατανοούμε τι είναι το ListView, πώς να το ρυθμίσετε και ποιες παγίδες κρύβει αυτό το παλιό αλλά ακόμα χρησιμοποιούμενο στοιχείο. Στην IT Sectr συναντάμε το ListView σε κληρονομικά έργα και υποστηρίζουμε αντίστροφη συμβατότητα με Android 4.4. Για σύγκριση με τη σύγχρονη εναλλακτική διαβάστε το άρθρο για το RecyclerView.
Κύρια σημεία
ListView — είναι μια ομάδα View από το πακέτο android.widget, σχεδιασμένη για εμφάνιση κυλιόμενης λίστας στοιχείων. Το ListView υλοποιεί το μοτίβο AdapterView: τα δεδομένα μεταδίδονται μέσω προσαρμογέα (Adapter), ο οποίος μετατρέπει τα στοιχεία δεδομένων σε View που εμφανίζονται στην οθόνη. Το ListView υπάρχει στο Android SDK από την πρώτη έκδοση (API 1) και δεν απαιτεί πρόσθετες βιβλιοθήκες.
Αρχιτεκτονική του ListView είναι απλούστερη από το RecyclerView: το LayoutManager είναι ενσωματωμένο (μόνο κάθετη κύλιση), η κίνηση αλλαγών απουσιάζει, οι διαχωριστές ορίζονται μέσω του XML χαρακτηριστικού android:divider. Το ListView προσθέτει αυτόματα OverScrollView και επισήμανση κατά την εστίαση, που απλοποιεί τη δημιουργία πρωτοτύπων. Σύμφωνα με δεδομένα της Statista (2025), περίπου 15% των εφαρμογών Android στο top-100 του Play Store εξακολουθούν να περιέχουν ListView — κυρίως κληρονομικά έργα με ελάχιστη υποστήριξη.
Το ListView ήταν το κύριο στοιχείο λιστών στο Android από το 2008 έως το 2014, όταν στο Google I/O 2014 παρουσιάστηκε το RecyclerView. Παρά την κατάσταση μερικώς απαρχαιωμένου (soft-deprecated), το ListView παραμένει διαθέσιμο σε όλες τις εκδόσεις Android και δεν επισημαίνεται ως @Deprecated στο SDK API 35. Εάν το έργο σας υποστηρίζει Android 4.4 (API 19) και χαμηλότερα, το ListView είναι η μόνη ενσωματωμένη επιλογή, καθώς το RecyclerView απαιτεί AndroidX και εξαρτήσεις gradle.
ArrayAdapter — ο πιο δημοφιλής προσαρμογέας για ListView. Δέχεται List<T> και διάταξη για κάθε στοιχείο. Από προεπιλογή ο ArrayAdapter καλεί toString() για κάθε αντικείμενο και εμφανίζει το κείμενο στο simple_list_item_1 (ένα TextView). Για προσαρμοσμένη εμφάνιση πρέπει να παρακαμφθεί η getView() ή να μεταδοθεί προσαρμοσμένη διάταξη στον κατασκευαστή.
// ListView με ArrayAdapter (απλό κείμενο)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"Ρωσία", "ΗΠΑ", "Γερμανία",
"Γαλλία", "Ιαπωνία", "Κίνα", "Βραζιλία"
)
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
// Χειρισμός κλικ
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapter προορίζεται για λίστες από δεδομένα Map. Δέχεται List<Map<String, Any>> και πίνακα from/to για σύνδεση κλειδιών Map με IDs widget στη διάταξη. Το SimpleAdapter είναι βολικό για γρήγορα πρωτότυπα όταν δεν χρειάζεται ξεχωριστή κλάση προσαρμογέα. Μειονέκτημα — έλλειψη type-safe τυποποίησης και πολυπλοκότητα με προσαρμοσμένη λογική.
<!-- Διάταξη με 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>
Προσαρμοσμένος προσαρμογέας για ListView παρακάμπτει τη μέθοδο getView(), την οποία το RecyclerView χωρίζει σε onCreateViewHolder + onBindViewHolder. Στο ListView και τα δύο βήματα είναι συνδυασμένα, επομένως ο προγραμματιστής πρέπει να υλοποιήσει ανεξάρτητα το μοτίβο ViewHolder μέσω του convertView (επαναχρησιμοποιούμενο View) και της ετικέτας setTag().
// Μοντέλο δεδομένων
data class Product(
name: String,
price: String,
imageResId: Int
)
// Προσαρμοσμένος προσαρμογέας με ViewHolder
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// 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: στο ListView η μέθοδος getView() καλείται για κάθε εμφάνιση στοιχείου στην οθόνη. Εάν δεν χρησιμοποιούνται convertView και ViewHolder, κάθε κύλιση δημιουργεί νέο αντικείμενο View, που οδηγεί σε διαρροή μνήμης και καθυστερήσεις σε λίστες με 50+ στοιχεία. Το ViewHolder με view.tag — υποχρεωτική βελτιστοποίηση για ListView με προσαρμοσμένες διατάξεις.
ListView έχει τέσσερις αρχιτεκτονικούς περιορισμούς που η Google διόρθωσε στο RecyclerView: (1) μόνο κάθετος προσανατολισμός — χωρίς πλέγματα, οριζόντιες λίστες ή καταρράκτες; (2) απουσία σημειακών ειδοποιήσεων — οποιαδήποτε αλλαγή δεδομένων προκαλεί πλήρη επανασχεδίαση μέσω notifyDataSetChanged(), που δημιουργεί τραντάγματα; (3) χωρίς ενσωματωμένη κίνηση προσθήκης/αφαίρεσης στοιχείων; (4) χωρίς διαχωρισμό αρμοδιοτήτων — το LayoutManager είναι ενσωματωμένο, το ItemAnimator απουσιάζει.
<!-- item_product.xml — διάταξη στοιχείου 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>
Πρακτικές συνέπειες: σε λίστες έως 20 στοιχεία το ListView λειτουργεί εξίσου γρήγορα με το RecyclerView. Σε 100+ στοιχεία η διαφορά γίνεται αισθητή: το ListView αρχίζει να χάνει FPS λόγω έλλειψης διαφορικών υπολογισμών και υποχρεωτικού ViewHolder. Για λίστες με συχνές ενημερώσεις (chat, feed, αρχεία καταγραφής) το ListView δεν είναι κατάλληλο — χρησιμοποιήστε RecyclerView με Paging 3. Στην IT Sectr μεταφέρουμε ListView σε RecyclerView σε κάθε εργασία σε κληρονομικές οθόνες με περισσότερα από 50 στοιχεία.
ListView παρέχει ένα σύνολο XML χαρακτηριστικών για γρήγορη ρύθμιση χωρίς κώδικα: android:divider (χρώμα/σχέδιο διαχωριστή), android:dividerHeight (πάχος), android:scrollbars (μπάρες κύλισης), android:fastScrollEnabled (γρήγορη κύλιση μέσω λαβής), android:choiceMode (λειτουργία επιλογής: singleChoice, multipleChoice). Το χαρακτηριστικό android:entries επιτρέπει τη συμπλήρωση της λίστας από πίνακα πόρων (string-array) χωρίς προσαρμογέα.
| Χαρακτηριστικό | Περιγραφή | Παράδειγμα τιμής |
|---|---|---|
| android:divider | Χρώμα ή drawable διαχωριστή | @color/gray_light |
| android:dividerHeight | Πάχος διαχωριστή σε px/dp | 1dp |
| android:entries | Πίνακας συμβολοσειρών από πόρους | @array/countries |
| android:choiceMode | Λειτουργία επιλογής στοιχείου | singleChoice |
| android:fastScrollEnabled | Γρήγορη κύλιση | true |
| android:overlapAnchor | Άγκυρα για αναδυόμενες λίστες (API 21+) | true |
Λειτουργία επιλογής: choiceMode="singleChoice" ενεργοποιεί στυλ RadioButton, όπου το επιλεγμένο στοιχείο επισημαίνεται. choiceMode="multipleChoice" — πλαίσια ελέγχου για πολλαπλά στοιχεία. Τα δεδομένα επιλογής είναι διαθέσιμα μέσω listView.checkedItemPosition ή listView.checkedItemIds. Σημαντικό: σε choiceMode το ListView χρησιμοποιεί StateListDrawable για φόντο — ορίστε δικό σας επιλογέα αν το προεπιλεγμένο μπλε δεν ταιριάζει με το σχέδιο.
Επιλογή μεταξύ ListView και RecyclerView εξαρτάται από τις απαιτήσεις του έργου. Το RecyclerView είναι το σύγχρονο πρότυπο, αλλά το ListView μπορεί να δικαιολογηθεί για απλές οθόνες με ελάχιστο κόστος συντήρησης παλαιού κώδικα. Σύγκριση βάσει όλων των βασικών παραμέτρων:
| Παράμετρος | ListView | RecyclerView |
|---|---|---|
| Μέγεθος βιβλιοθήκης | Ενσωματωμένο στο SDK (≈60 KB) | AndroidX (≈300 KB με εξαρτήσεις) |
| Ελάχιστη έκδοση | API 1 | API 14 (μέσω AndroidX) |
| Προσανατολισμός | Μόνο κάθετος | Κάθετος, οριζόντιος, πλέγμα, καταρράκτης |
| ViewHolder | Χειροκίνητη υλοποίηση | Υποχρεωτικό από αρχιτεκτονική |
| Κίνηση | Μόνο fade μεταβάσεις | DefaultItemAnimator (add, remove, move, change) |
| Σημειακές ενημερώσεις | Όχι (μόνο notifyDataSetChanged) | notifyItemInserted/Removed/Changed |
| Click listener | onItemClickListener (ενσωματωμένο) | Μέσω ViewHolder (δεν υπάρχει ενσωματωμένο) |
| Απόδοση | Επαρκής για λίστες έως 50 στοιχεία | Σταθερό 60 FPS για 1000+ στοιχεία |
Συμπέρασμα: για νέα έργα χρησιμοποιήστε RecyclerView. Αφήστε το ListView μόνο σε κληρονομικό κώδικα όπου η μετάβαση είναι αδικαιολόγητα δαπανηρή. Σύμφωνα με τις οδηγίες Material Design 3 (2026), λίστες οποιασδήποτε πολυπλοκότητας πρέπει να χρησιμοποιούν RecyclerView για συμμόρφωση με σύγχρονα πρότυπα απόδοσης και προσβασιμότητας.
Συχνές Ερωτήσεις
ListView δεν επισημαίνεται ως @Deprecated στο Android SDK API 35, αλλά η Google δεν συνιστά τη χρήση του σε νέα έργα (soft-deprecated). Το στοιχείο παραμένει λειτουργικό σε όλες τις εκδόσεις Android. Το RecyclerView είναι η επίσημη αντικατάσταση από το 2014. Αν ξεκινάτε νέο έργο — επιλέξτε RecyclerView. Αν συντηρείτε υπάρχον — το ListView θα συνεχίσει να λειτουργεί χωρίς αλλαγές.
Το ListView υποστηρίζει τις μεθόδους addHeaderView(View, Object, boolean) και addFooterView(View, Object, boolean). Το header και footer προστίθενται στη γενική λίστα στοιχείων, αλλά δεν συμμετέχουν στον προσαρμογέα — εμφανίζονται πάντα στην αρχή και στο τέλος της λίστας. Σημαντικό: προσθέστε header/footer πριν από τη ρύθμιση του προσαρμογέα (setAdapter()), διαφορετικά δεν θα ληφθούν υπόψη. Μετά την προσθήκη header, τα ID θέσεων στοιχείων μετατοπίζονται κατά τον αριθμό των header.
ListView και ScrollView και τα δύο επεξεργάζονται κάθετες χειρονομίες, που δημιουργεί σύγκρουση: το εξωτερικό ScrollView αναχαιτίζει την κύλιση και το ListView δεν κυλάει. Λύση: (1) μην τοποθετείτε ListView μέσα σε ScrollView — χρησιμοποιήστε RecyclerView με NestedScrollingChild, (2) αντικαταστήστε το ScrollView με NestedScrollView (API 21+), (3) χρησιμοποιήστε ListView με σταθερό ύψος (android:layout_height="...dp") μέσα στο ScrollView, αλλά αυτό θα σπάσει την προσαρμοστικότητα.
Ο καθαρισμός δεδομένων στο ListView εξαρτάται από τον τύπο προσαρμογέα. Για ArrayAdapter: adapter.clear() και adapter.addAll(newList) ακολουθούμενο από adapter.notifyDataSetChanged(). Για προσαρμοσμένο προσαρμογέα: ενημερώστε τη λίστα δεδομένων και καλέστε notifyDataSetChanged(). Προσοχή: το listView.invalidateViews() μόνο επανασχεδιάζει το View, αλλά δεν ενημερώνει τα δεδομένα. Πάντα αλλάζετε δεδομένα μέσω του προσαρμογέα, όχι απευθείας.
Το ύψος στοιχείου ListView ορίζεται στη διάταξη item. Εάν η διάταξη χρησιμοποιεί layout_height="wrap_content", το ύψος προσαρμόζεται στο περιεχόμενο. Για σταθερό ύψος όλων των στοιχείων χρησιμοποιήστε προσαρμοσμένο προσαρμογέα που ορίζει LayoutParams στο getView(): view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). Για διαφορετικά ύψη χρησιμοποιήστε RecyclerView με διαφορετικούς τύπους προβολής.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης