App Tutorial

BaseAdapter Android – Guida Completa con Esempi Java

Se ti stai chiedendo come funziona BaseAdapter Android Java e quando ha senso usarlo, questa guida pratica ti accompagna passo passo nella creazione di liste personalizzate e performanti. Inoltre, scoprirai in modo chiaro cos’è questa classe, perché è diversa da ArrayAdapter e RecyclerView e come integrarla correttamente nella tua app. In particolare, tutti gli esempi sono scritti in Java e pensati per essere usati subito in Android Studio, così puoi copiare, adattare e testare il codice nel tuo progetto in modo immediato. In conclusione, continua a leggere per capire davvero come sfruttare BaseAdapter al meglio nei tuoi layout con ListView.

BaseAdapter in Android: guida completa con esempi in Java

In questa guida vediamo in dettaglio come usare BaseAdapter in Android per creare liste personalizzate con ListView; in particolare, capiremo quando ha senso usarlo (rispetto ad ArrayAdapter e a RecyclerView) e come strutturare il codice in modo pulito e performante, così da ottenere liste più chiare e veloci.

Custom adapter Android con ListView: architettura di un adapter personalizzato per visualizzare dati dinamici in una lista

1. Introduzione a BaseAdapter

BaseAdapter Android Java è una classe astratta della libreria Android che funge da base per creare adapter personalizzati, usati tipicamente con viste come ListView e GridView. In altri termini, un adapter ha il compito di trasformare i dati (oggetti Java, array, liste) in viste da mostrare a schermo.

Invece, quando usi un BaseAdapter Android Java, puoi controllare completamente:

  • Come viene creato il layout di ogni elemento della lista.
  • Come vengono legati i dati ai widget (TextView, ImageView, ecc.).
  • Come ottimizzare le performance riutilizzando le viste con il pattern ViewHolder.

Quando usare BaseAdapter Android rispetto ad ArrayAdapter

ArrayAdapter è comodo per casi semplici, ad esempio una lista di stringhe o un layout molto basilare. Tuttavia, con il crescere della complessità dell’interfaccia, diventa limitante quando:

  • Ogni elemento deve mostrare più informazioni (es. testo + immagine + pulsanti).
  • Devi gestire logiche differenti per alcuni elementi.
  • Vuoi massima flessibilità sul layout.

In questi casi, BaseAdapter Android Java è preferibile perché, a differenza di un semplice ArrayAdapter, ti permette di gestire manualmente la creazione e il riuso delle viste tramite il metodo getView().

BaseAdapter Android Java vs RecyclerView

RecyclerView è il componente moderno e consigliato da Google per liste complesse e performanti; infatti offre molte funzionalità avanzate (layout manager, animazioni, decorazioni). Tuttavia, ListView con BaseAdapter è ancora utile quando:

  • Stai mantenendo o aggiornando un progetto legacy che usa già ListView.
  • Hai una lista relativamente semplice e non ti servono le funzionalità avanzate di RecyclerView.
  • Vuoi capire i concetti di base di come funziona un adapter in Android.

2. Struttura di BaseAdapter Android Java e metodi principali

In particolare, per creare un adapter personalizzato estendendo BaseAdapter, in primo luogo devi implementare obbligatoriamente quattro metodi:

getCount()

In particolare, restituisce il numero totale di elementi nella lista (la dimensione dei dati). Di conseguenza, ListView usa questo valore per sapere quanti elementi visualizzare.

getItem(int position)

Allo stesso modo, restituisce l’oggetto dati alla posizione specificata. Questo è particolarmente utile quando devi recuperare l’elemento associato a una certa riga, ad esempio nella gestione dei click.

getItemId(int position)

Restituisce un ID univoco per l’elemento alla posizione indicata. In molti casi, pertanto, puoi semplicemente restituire la posizione stessa (se non hai un ID univoco nel modello).

getView(int position, View convertView, ViewGroup parent)

Adapter personalizzato Android per lista contatti: implementazione Java con nome e numero di telefono in una ListView

È il metodo più importante: infatti crea (o riutilizza) la vista da mostrare nella ListView per la posizione indicata. Qui:

  • Inflate il layout XML dell’elemento se convertView è null.
  • Recuperi e/o crei il ViewHolder.
  • Colleghi i dati (testi, immagini, ecc.) ai widget.

3. Esempio base completo di BaseAdapter Android Java con ListView

In questo esempio creiamo una semplice app che mostra una lista di frutti, ognuno con nome e un’icona. In particolare, useremo:

  • Classe modello: Frutto.java
  • Adapter personalizzato: FruttoAdapter.java (con pattern ViewHolder)
  • Activity principale: MainActivity.java
  • Layout XML dell’elemento: item_frutto.xml
  • Layout XML dell’activity: activity_main.xml

3.1 Modello Frutto.java

// Frutto.java
// Modello semplice con nome e id della risorsa immagine

public class Frutto {
    private String nome;
    private int iconaResId; // R.drawable.qualcosa

    public Frutto(String nome, int iconaResId) {
        this.nome = nome;
        this.iconaResId = iconaResId;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public int getIconaResId() {
        return iconaResId;
    }

    public void setIconaResId(int iconaResId) {
        this.iconaResId = iconaResId;
    }
}

3.2 Layout XML per ogni elemento: item_frutto.xml

Per esempio, si tratta di un layout composto da un’icona e un testo affiancati orizzontalmente.

<?xml version="1.0" encoding="utf-8"?>
<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/imageViewIcona"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginEnd="12dp"
        android:contentDescription="@string/icona_frutto"
        android:src="@drawable/ic_frutto_placeholder" />

    <TextView
        android:id="@+id/textViewNome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Mela"
        android:textSize="16sp" />

</LinearLayout>

3.3 Layout activity principale: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <ListView
        android:id="@+id/listViewFrutti"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

3.4 Adapter personalizzato BaseAdapter Android Java con ViewHolder

Pattern ViewHolder in un adapter ListView Android: ottimizzazione del riciclo delle view con custom adapter Java
// FruttoAdapter.java
// Adapter personalizzato che estende BaseAdapter e usa il pattern ViewHolder

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class FruttoAdapter extends BaseAdapter {

    private Context context;
    private List<Frutto> listaFrutti;
    private LayoutInflater inflater;

    // Costruttore
    public FruttoAdapter(Context context, List<Frutto> listaFrutti) {
        this.context = context;
        this.listaFrutti = listaFrutti;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        // Numero totale di elementi nella lista
        return listaFrutti.size();
    }

    @Override
    public Object getItem(int position) {
        // Restituisce l'oggetto Frutto alla posizione specificata
        return listaFrutti.get(position);
    }

    @Override
    public long getItemId(int position) {
        // In questo caso usiamo semplicemente la posizione come ID
        return position;
    }

    // Classe ViewHolder per memorizzare i riferimenti alle viste
    private static class ViewHolder {
        ImageView imageViewIcona;
        TextView textViewNome;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            // Non esiste una vista riutilizzabile, infliamo una nuova riga
            convertView = inflater.inflate(R.layout.item_frutto, parent, false);

            // Creiamo il ViewHolder e salviamo i riferimenti alle viste
            holder = new ViewHolder();
            holder.imageViewIcona = convertView.findViewById(R.id.imageViewIcona);
            holder.textViewNome = convertView.findViewById(R.id.textViewNome);

            // Associamo il ViewHolder alla vista
            convertView.setTag(holder);
        } else {
            // Riutilizziamo il ViewHolder esistente
            holder = (ViewHolder) convertView.getTag();
        }

        // Recuperiamo il frutto corrente
        Frutto frutto = listaFrutti.get(position);

        // Popoliamo le viste con i dati
        holder.textViewNome.setText(frutto.getNome());
        holder.imageViewIcona.setImageResource(frutto.getIconaResId());

        return convertView;
    }
}

3.5 Activity principale: MainActivity.java

// MainActivity.java

import android.os.Bundle;
import android.widget.ListView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView listViewFrutti;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listViewFrutti = findViewById(R.id.listViewFrutti);

        // Creiamo una lista di frutti di esempio
        List<Frutto> frutti = new ArrayList<>();
        frutti.add(new Frutto("Mela", R.drawable.ic_mela));
        frutti.add(new Frutto("Banana", R.drawable.ic_banana));
        frutti.add(new Frutto("Arancia", R.drawable.ic_arancia));
        frutti.add(new Frutto("Pera", R.drawable.ic_pera));

        // Creiamo l'adapter e lo associamo alla ListView
        FruttoAdapter adapter = new FruttoAdapter(this, frutti);
        listViewFrutti.setAdapter(adapter);
    }
}

4. Pattern ViewHolder in BaseAdapter Android Java: perché è importante

Ad esempio, quando scorri una ListView, Android crea e distrugge molte viste. Se ad ogni passaggio chiami findViewById() per trovare i widget all’interno di ogni riga, il costo può diventare elevato e, di conseguenza, la lista può risultare scattosa.

Il pattern ViewHolder, invece, serve proprio a evitare chiamate ripetute a findViewById(). L’idea è:

  • Creare una classe che contiene i riferimenti ai widget di una riga.
  • Creare il ViewHolder solo quando convertView è null.
  • Salvare il ViewHolder nella vista tramite setTag() e recuperarlo con getTag() quando la vista viene riutilizzata.

In questo modo, si riduce drasticamente il lavoro fatto durante lo scrolling e si migliora sensibilmente la fluidità.

Esempio di ViewHolder con BaseAdapter Android Java

private static class ViewHolder {
    ImageView icona;
    TextView titolo;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.item_layout, parent, false);
        holder = new ViewHolder();
        holder.icona = convertView.findViewById(R.id.imageViewIcona);
        holder.titolo = convertView.findViewById(R.id.textViewTitolo);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    // Usa holder.icona e holder.titolo senza doverli cercare di nuovo
    // ...

    return convertView;
}

5. Mini App di esempio con BaseAdapter Android Java: Lista Contatti

Vediamo ora un esempio leggermente più completo: una lista di contatti con nome, numero di telefono e un’icona avatar. Inoltre, gestiremo anche il click sugli elementi mostrando un Toast con il nome del contatto selezionato.

5.1 Modello Contatto.java

// Contatto.java

public class Contatto {
    private String nome;
    private String numeroTelefono;
    private int avatarResId;

    public Contatto(String nome, String numeroTelefono, int avatarResId) {
        this.nome = nome;
        this.numeroTelefono = numeroTelefono;
        this.avatarResId = avatarResId;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getNumeroTelefono() {
        return numeroTelefono;
    }

    public void setNumeroTelefono(String numeroTelefono) {
        this.numeroTelefono = numeroTelefono;
    }

    public int getAvatarResId() {
        return avatarResId;
    }

    public void setAvatarResId(int avatarResId) {
        this.avatarResId = avatarResId;
    }
}

5.2 Layout XML dell’elemento: item_contatto.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/imageViewAvatar"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginEnd="12dp"
        android:src="@drawable/ic_avatar_placeholder"
        android:contentDescription="@string/avatar_contatto" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textViewNomeContatto"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Mario Rossi"
            android:textStyle="bold"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/textViewNumeroTelefono"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="+39 333 1234567"
            android:textSize="14sp"
            android:textColor="#666666" />

    </LinearLayout>

</LinearLayout>

5.3 Layout XML dell’activity: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Rubrica contatti"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_marginBottom="8dp" />

    <ListView
        android:id="@+id/listViewContatti"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="1dp" />

</LinearLayout>

5.4 ContattoAdapter.java: estendere BaseAdapter Android Java

// ContattoAdapter.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class ContattoAdapter extends BaseAdapter {

    private Context context;
    private List<Contatto> listaContatti;
    private LayoutInflater inflater;

    public ContattoAdapter(Context context, List<Contatto> listaContatti) {
        this.context = context;
        this.listaContatti = listaContatti;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return listaContatti.size();
    }

    @Override
    public Object getItem(int position) {
        return listaContatti.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    private static class ViewHolder {
        ImageView imageViewAvatar;
        TextView textViewNome;
        TextView textViewNumero;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_contatto, parent, false);

            holder = new ViewHolder();
            holder.imageViewAvatar = convertView.findViewById(R.id.imageViewAvatar);
            holder.textViewNome = convertView.findViewById(R.id.textViewNomeContatto);
            holder.textViewNumero = convertView.findViewById(R.id.textViewNumeroTelefono);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // Recupera il contatto corrente
        Contatto contatto = listaContatti.get(position);

        // Popola le viste
        holder.textViewNome.setText(contatto.getNome());
        holder.textViewNumero.setText(contatto.getNumeroTelefono());
        holder.imageViewAvatar.setImageResource(contatto.getAvatarResId());

        return convertView;
    }
}

5.5 MainActivity.java con gestione dei click

// MainActivity.java

import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView listViewContatti;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listViewContatti = findViewById(R.id.listViewContatti);

        // Dati di esempio
        final List<Contatto> contatti = new ArrayList<>();
        contatti.add(new Contatto("Mario Rossi", "+39 333 1111111", R.drawable.ic_avatar_1));
        contatti.add(new Contatto("Luca Bianchi", "+39 333 2222222", R.drawable.ic_avatar_2));
        contatti.add(new Contatto("Anna Verdi", "+39 333 3333333", R.drawable.ic_avatar_3));
        contatti.add(new Contatto("Giulia Neri", "+39 333 4444444", R.drawable.ic_avatar_4));

        // Crea e assegna l'adapter
        ContattoAdapter adapter = new ContattoAdapter(this, contatti);
        listViewContatti.setAdapter(adapter);

        // Gestione del click sugli elementi
        listViewContatti.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Contatto contattoSelezionato = contatti.get(position);
                String messaggio = "Hai selezionato: " + contattoSelezionato.getNome();
                Toast.makeText(MainActivity.this, messaggio, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

6. Differenze tra BaseAdapter Android Java, ArrayAdapter e RecyclerView

La tabella seguente, infatti, riassume in modo sintetico le principali differenze tra queste tre soluzioni:

CaratteristicaBaseAdapter + ListViewArrayAdapter + ListViewRecyclerView
Flessibilità layoutMolto alta (layout completamente personalizzabile)Media (ottimizzato per casi semplici)Molto alta
Complessità di implementazioneMediaBassaAlta
Performance su liste molto lungheBuone con ViewHolderDiscreteEccellenti (riciclo avanzato)
Supporto funzioni avanzate (animazioni, grid, staggered)LimitatoLimitatoAvanzato (LayoutManager, ItemDecoration, ecc.)
Uso consigliato nel 2024Progetti legacy, liste semplici, formazioneListe molto semplici (stringhe, array)Nuovi progetti, interfacce moderne

7. Consigli pratici e best practice per BaseAdapter Android Java (2025)

  • Per nuovi progetti complessi, preferisci RecyclerView. Offre più controllo e integrazione con le librerie moderne (es. Paging, DiffUtil, ListAdapter).
  • Usa BaseAdapter quando devi mantenere codice esistente basato su ListView, oppure per esercitarti a capire come funziona il binding dei dati alle viste.
  • Implementa sempre il pattern ViewHolder per evitare lag durante lo scroll.
  • Evita logica pesante in getView(). Operazioni costose (IO, rete, parsing complesso) dovrebbero stare fuori dall’adapter.
  • Gestisci il riciclo con attenzione: ricorda che la stessa vista può mostrare dati diversi durante lo scroll, quindi inizializza sempre tutti i campi (non dare per scontato lo stato precedente).
  • Non fare assunzioni sulla posizione: i dati possono cambiare, quindi quando possibile lavora sugli ID o sugli oggetti, non su indici fissi.
  • Se hai più tipi di vista (righe diverse nella stessa lista), valuta se passare a RecyclerView o, se resti con ListView, sovrascrivi getItemViewType() e getViewTypeCount().

8. Conclusioni su BaseAdapter Android Java e risorse correlate

In conclusione, BaseAdapter Android Java rimane uno strumento utile per comprendere le basi del pattern adapter in Android e per gestire applicazioni legacy che usano ancora ListView. Sebbene oggi RecyclerView sia la scelta consigliata per nuove app e interfacce complesse, conoscere BaseAdapter Android Java ti aiuta comunque a capire meglio come funziona il riciclo delle viste e l’ottimizzazione delle performance.

A questo punto, ecco alcune risorse utili (in inglese):

Partendo dagli esempi di questa guida su BaseAdapter Android Java puoi adattare facilmente il codice ai tuoi modelli di dati e layout; inoltre, puoi sperimentare con diverse strutture e aggiungere funzionalità come menu contestuali, pulsanti di azione e caricamento asincrono delle immagini.