Se ti stai chiedendo come funziona BaseAdapter Android, quando ha senso usarlo rispetto ad ArrayAdapter e a RecyclerView.Adapter e come scrivere codice Java pulito e riutilizzabile, questa guida fa per te. Vedremo passo passo come collegare un elenco di oggetti Java a una ListView, come creare layout personalizzati con immagini e testo e come mantenere buone performance anche con molte righe a schermo.

Nel corso dell’articolo analizzeremo in dettaglio la classe BaseAdapter Android, capiremo perché è diversa da ArrayAdapter e quali sono i limiti rispetto a RecyclerView.Adapter. Inoltre, troverai esempi di codice Java completi e commentati per gestire più tipi di layout, aggiornamenti dinamici dei dati, checkbox con selezione multipla e il pattern ViewHolder per evitare lag nello scroll. Tutti gli snippet sono pensati per essere copiati e incollati direttamente in Android Studio.

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.

BaseAdapter Android con layout personalizzato (immagine + testo)

Uno degli scenari più comuni in cui ha senso usare BaseAdapter Android è quando devi mostrare, per ogni riga della ListView, sia un’immagine (ImageView) sia uno o più testi (TextView). In questi casi un semplice ArrayAdapter non basta più ed è molto più comodo definire un layout XML personalizzato e un adapter che sappia “gonfiarlo” (inflate) e popolarlo con i dati.

Esempio BaseAdapter Android con layout personalizzato composto da ImageView e TextView in una ListView

Di seguito un esempio minimale di adapter che estende BaseAdapter e utilizza un layout con ImageView + TextView (simile a una semplice lista di prodotti o di elementi di menu):

// Prodotto.java
public class Prodotto {
    private String nome;
    private int iconaResId;

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

    public String getNome() {
        return nome;
    }

    public int getIconaResId() {
        return iconaResId;
    }
}

// item_prodotto.xml (layout con ImageView + TextView)
// Vedi guida su <code>ListView Android</code> per dettagli sui layout

// ProdottoAdapter.java
// Adapter personalizzato basato su BaseAdapter Android

public class ProdottoAdapter extends BaseAdapter {

    private Context context;
    private List<Prodotto> prodotti;
    private LayoutInflater inflater;

    public ProdottoAdapter(Context context, List<Prodotto> prodotti) {
        this.context = context;
        this.prodotti = prodotti;
        this.inflater = LayoutInflater.from(context);
    }

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

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

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

    private static class ViewHolder {
        ImageView imageViewIcona;
        TextView textViewNome;
    }

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

        if (convertView == null) {
            // Inflate del layout personalizzato per la riga
            convertView = inflater.inflate(R.layout.item_prodotto, parent, false);

            holder = new ViewHolder();
            holder.imageViewIcona = convertView.findViewById(R.id.imageViewIcona);
            holder.textViewNome = convertView.findViewById(R.id.textViewNome);

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

        // Recupero del prodotto corrente
        Prodotto prodotto = prodotti.get(position);

        // Binding dei dati ai widget
        holder.textViewNome.setText(prodotto.getNome());
        holder.imageViewIcona.setImageResource(prodotto.getIconaResId());

        return convertView;
    }
}

Come vedi, con BaseAdapter Android hai il pieno controllo sul layout della riga e sul modo in cui colleghi i dati agli elementi grafici. Vediamo ora un esempio completo di app con più classi per consolidare i concetti.

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. Performance e pattern ViewHolder in BaseAdapter Android Java

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. BaseAdapter Android con più tipi di layout (getViewTypeCount e getItemViewType)

Un’altra esigenza frequente quando si lavora con BaseAdapter Android è gestire righe diverse nella stessa lista: ad esempio una riga di intestazione (header) e più righe di contenuto, oppure elementi normali e elementi in evidenza. Con ListView puoi farlo sovrascrivendo i metodi getViewTypeCount() e getItemViewType(int position).

// Esempio di BaseAdapter Android con due tipi di layout: HEADER e ITEM

public class SezioneAdapter extends BaseAdapter {

    private static final int TIPO_HEADER = 0;
    private static final int TIPO_ITEM = 1;

    private LayoutInflater inflater;
    private List<Object> elementi; // Può contenere sia String (header) che Prodotto (item)

    public SezioneAdapter(Context context, List<Object> elementi) {
        this.inflater = LayoutInflater.from(context);
        this.elementi = elementi;
    }

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

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

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

    @Override
    public int getViewTypeCount() {
        // Abbiamo 2 tipi di vista: header e item normale
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        Object obj = elementi.get(position);
        if (obj instanceof String) {
            return TIPO_HEADER; // Le stringhe rappresentano le intestazioni
        } else {
            return TIPO_ITEM;   // Tutto il resto è un elemento normale
        }
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int tipo = getItemViewType(position);

        if (tipo == TIPO_HEADER) {
            // Layout per l'intestazione
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.item_header, parent, false);
            }

            TextView textViewHeader = convertView.findViewById(R.id.textViewHeader);
            String titoloSezione = (String) elementi.get(position);
            textViewHeader.setText(titoloSezione);

        } else {
            // Layout per l'elemento normale
            ViewHolder holder;

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

            Prodotto prodotto = (Prodotto) elementi.get(position);
            holder.textViewNome.setText(prodotto.getNome());
            holder.imageViewIcona.setImageResource(prodotto.getIconaResId());
        }

        return convertView;
    }

    // ViewHolder riutilizzato per il tipo ITEM
    private static class ViewHolder {
        ImageView imageViewIcona;
        TextView textViewNome;
    }
}

Grazie a questi due metodi, ListView sa quanti tipi diversi di riga esistono e può riciclare correttamente solo le viste compatibili. Se ti accorgi di avere molti tipi di layout diversi o logiche complesse, valuta comunque di migrare a RecyclerView, che gestisce meglio scenari avanzati.

7. Aggiornamento dinamico dei dati in BaseAdapter Android (notifyDataSetChanged)

Uno dei vantaggi di usare BaseAdapter Android è la possibilità di aggiornare dinamicamente il contenuto della lista quando cambiano i dati (ad esempio dopo una chiamata API o dopo un filtro di ricerca). Per fare questo, aggiorni la lista interna dell’adapter e poi chiami notifyDataSetChanged() per dire alla ListView di ridisegnarsi.

public class ProdottoAdapter extends BaseAdapter {

    private List<Prodotto> prodotti;
    private LayoutInflater inflater;

    public ProdottoAdapter(Context context, List<Prodotto> prodotti) {
        this.inflater = LayoutInflater.from(context);
        this.prodotti = new ArrayList<>(prodotti); // Copia mutabile
    }

    // ... implementazioni di getCount(), getItem(), getItemId(), getView() ...

    // Metodo per sostituire completamente la lista di dati
    public void aggiornaProdotti(List<Prodotto> nuoviProdotti) {
        this.prodotti.clear();
        this.prodotti.addAll(nuoviProdotti);
        // Notifica alla ListView che i dati sono cambiati
        notifyDataSetChanged();
    }

    // Metodo per aggiungere un singolo elemento
    public void aggiungiProdotto(Prodotto prodotto) {
        this.prodotti.add(prodotto);
        notifyDataSetChanged();
    }

    // Metodo per rimuovere un elemento
    public void rimuoviProdotto(int position) {
        if (position >= 0 && position < prodotti.size()) {
            this.prodotti.remove(position);
            notifyDataSetChanged();
        }
    }
}

In un’Activity o in un Fragment, ti basta conservare un riferimento all’adapter e chiamare questi metodi quando ricevi nuovi dati. Questo approccio è ideale per piccole liste; per dataset molto grandi o aggiornamenti frequenti, con RecyclerView è preferibile usare ListAdapter e DiffUtil.

8. BaseAdapter Android con checkbox e selezione multipla

Un caso pratico molto richiesto è la selezione multipla con checkbox, ad esempio per permettere all’utente di selezionare più elementi da una lista (file, contatti, prodotti). Con BaseAdapter Android puoi gestire facilmente questo scenario mantenendo una struttura dati che tiene traccia delle posizioni selezionate.

// Layout item_selezionabile.xml
// Contiene un TextView e un CheckBox affiancati

// Adapter con selezione multipla

public class SelezioneAdapter extends BaseAdapter {

    private LayoutInflater inflater;
    private List<String> elementi;
    private SparseBooleanArray elementiSelezionati = new SparseBooleanArray();

    public SelezioneAdapter(Context context, List<String> elementi) {
        this.inflater = LayoutInflater.from(context);
        this.elementi = elementi;
    }

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

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

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

    private static class ViewHolder {
        TextView textViewTitolo;
        CheckBox checkBox;
    }

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

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

            holder = new ViewHolder();
            holder.textViewTitolo = convertView.findViewById(R.id.textViewTitolo);
            holder.checkBox = convertView.findViewById(R.id.checkBoxSeleziona);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // Imposta il testo
        String titolo = elementi.get(position);
        holder.textViewTitolo.setText(titolo);

        // Imposta lo stato della checkbox in base alla posizione
        boolean selezionato = elementiSelezionati.get(position, false);
        holder.checkBox.setOnCheckedChangeListener(null); // Evita trigger indesiderati
        holder.checkBox.setChecked(selezionato);

        // Gestione del click sulla checkbox
        holder.checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            elementiSelezionati.put(position, isChecked);
        });

        // Opzionale: gestire il click sull'intera riga
        convertView.setOnClickListener(v -> {
            boolean nuovoStato = !elementiSelezionati.get(position, false);
            elementiSelezionati.put(position, nuovoStato);
            notifyDataSetChanged();
        });

        return convertView;
    }

    // Restituisce la lista degli elementi selezionati
    public List<String> getElementiSelezionati() {
        List<String> selezionati = new ArrayList<>();
        for (int i = 0; i < elementi.size(); i++) {
            if (elementiSelezionati.get(i, false)) {
                selezionati.add(elementi.get(i));
            }
        }
        return selezionati;
    }
}

Nell’Activity puoi quindi chiamare getElementiSelezionati() per recuperare tutti gli elementi marcati dall’utente. Fai solo attenzione a inizializzare sempre correttamente la checkbox in getView(), perché le viste vengono riciclate e potresti altrimenti vedere selezioni errate durante lo scroll.

9. 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().

10. FAQ su BaseAdapter Android

Per chi sta iniziando a usare BaseAdapter Android, ecco alcune domande frequenti che aiutano a chiarire i dubbi più comuni.

  • 1. Quando dovrei usare BaseAdapter invece di ArrayAdapter?
    Usa ArrayAdapter quando devi mostrare dati semplici (ad esempio solo testo) e ti basta un layout standard. Scegli BaseAdapter Android quando hai bisogno di layout personalizzati con più viste (immagini, pulsanti, checkbox), più tipi di riga o logiche di visualizzazione particolari.
  • 2. BaseAdapter è deprecato o ancora valido nel 2025?
    BaseAdapter non è deprecato e continua a funzionare correttamente. Tuttavia, per i nuovi progetti Google consiglia di usare RecyclerView, che offre migliori performance, API più moderne e integrazione con librerie come ListAdapter e Paging.
  • 3. Devo sempre usare il pattern ViewHolder con BaseAdapter Android?
    Nella pratica, sì: il pattern ViewHolder è fondamentale per evitare chiamate ripetute a findViewById() dentro getView(). Senza ViewHolder, le liste diventano facilmente lente e scattose, soprattutto su dispositivi meno recenti o con molte righe.
  • 4. Posso usare BaseAdapter con GridView o altre viste oltre a ListView?
    Sì, BaseAdapter può essere usato anche con GridView e con altre viste che accettano un ListAdapter. Il principio è lo stesso: l’adapter trasforma i tuoi dati in viste da visualizzare nella griglia o nella lista.
  • 5. Come gestisco click e long-click sugli elementi con BaseAdapter Android?
    Per la maggior parte dei casi puoi impostare un OnItemClickListener sulla ListView (come mostrato nella mini app di esempio). Se invece ti servono interazioni su singoli widget dentro la riga (ad esempio un pulsante di cancellazione), puoi impostare i listener direttamente in getView(), ricordandoti di usare la posizione o l’oggetto associato per capire quale elemento è stato toccato.

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.


Guide correlate