Como ya se ha visto en la explicación de los elementos ListView o Spinner, estos muestran un conjunto de datos que se asocian a un elemento gráfico a través de un adaptador. Dado a que este tipo de asociación independiza la parte gráfica de la parte de datos, hay que tener en cuenta que siempre que se quiera inferir sobre los datos hay que hacerlo dentro del adaptador.

Manejo de datos

Añadir, borrar, acceder a cualquier parte se hará dentro del propio adaptador siempre utilizando al final el método notifyDataSetChanged() para notificar al adaptador que los datos han cambiado. De esta forma si queremos añadir un dato desde la activity p.e. desde un formulario ejecutaríamos un método addData escrito en el adaptador

public class AdaptadorPersonalizadoSpinner extends BaseAdapter {

    ArrayList datos;
    //int layout;
    Context context;
    TextView nombre, apellido;
    ImageView genero;


    public AdaptadorPersonalizadoSpinner(ArrayList datos, Context context) {
        this.datos = datos;
        //this.layout = layout;
        this.context = context;
    }

    public void addPersona(Persona p){
        datos.add(p);
        notifyDataSetChanged();
    }

    public void remove(String nombre){
        for(int i = 0; i < datos.size(); i++){
            Persona temporal = (Persona) datos.get(i);
            if(temporal.getNombre().equals(nombre)){
                datos.remove(i);
            }
        }
        notifyDataSetChanged();
    }
}

De esta forma tan solo hay que llamar a estos métodos desde la actividad

AdaptadorPersonalizadoSpinner adapterPerso = new AdaptadorPersonalizadoSpinner(personas,MainActivity.this);
spinnerPerso.setAdapter(adapterPerso);
adapterPerso.addPersona(new Persona("NombreAñadido","apellido","dni",0,123));
adapterPerso.remove("NombreABorrar");

Optimización del adaptador

Dentro del adaptador, el método getView devolvía la vista rellena de cada una de las filas del elemento en cuestión. Para optimizar este método y que no ejecute todas las lineas de código las n veces (tantas como elementos existan en la lista de datos a representar) hay que indicar cuales si debe realizar y cuales no. Para empezar, hay una serie de filas que no necesita rellenar como por ejemplo el inflado de la vista que tan solo lo tendrá que realizar la primera vez

@Override
public View getView(int i, View view, ViewGroup viewGroup) {
    if (view == null) {
        view = LayoutInflater.from(context).inflate(R.layout.item_grid, viewGroup, false);
        }
    return view;
}

De esta forma tan solo realiza la primera vez la asociación de xml con vista a representar. Del mismo modo todos los elementos que se quieran instancias en la vista también podrían ir en esta evaluación ya que tan solo o¡lo debería de hacer la primera vez que ejecuta (ya que todos los elementos son los mismos)

public View getView(int i, View view, ViewGroup viewGroup) {
    if (view == null) {
        view = LayoutInflater.from(context).inflate(R.layout.item_grid, viewGroup, false);
        //Estos tres elementos estarán declarados como TextView - Imageview en la clase
        nombre = view.findViewById(R.id.textoGrid);
        imagen = view.findViewById(R.id.imagenGrid);
    }
}

Esta es la forma más sencilla de optimizar una lista, pero hay otra más estructurada que es crear una clase que abarque todos los elementos y se infancia la primera vez, utilizando el método setTag() para el resto de veces. Esto simularía el funcionamiento de la caché de un ordenador y en Android se conoce con el nombre de holder o contenedor

public class AdaptadorGridPersonalizado extends BaseAdapter {

    Context context;
    ArrayList listaDatos;

    public AdaptadorGridPersonalizado(Context context, ArrayList listaDatos) {
        this.context = context;
        this.listaDatos = listaDatos;
    }

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

    @Override
    public Object getItem(int i) {
        return listaDatos.get(i);
    }

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

        ViewHolder holder;
        if(view==null) {
            view = LayoutInflater.from(context).inflate(R.layout.item_grid, viewGroup, false);
            holder = new ViewHolder();
            TextView nombre = view.findViewById(R.id.textoGrid);
            ImageView imagen = view.findViewById(R.id.imagenGrid);
            holder.imagen = imagen;
            holder.nombre = nombre;
            view.setTag(holder);

        }

        else {
            holder = (ViewHolder) view.getTag();
        }

        Persona temporal = (Persona) listaDatos.get(i);
        holder.nombre.setText(temporal.getNombre());
        holder.imagen.setImageResource(temporal.getImagen());

        return view;
    }

    class ViewHolder{
        TextView nombre;
        ImageView imagen;

    }
}

Este holder actua como contenedor de todos los elementos de la vista de forma que la primera vez instancia todo y el resto de veces tan solo es necesario capturar el elemento holder y recuperar cada uno de los elementos que se han rellenado en la primera ejecución del método

Evaluar acciones dentro del adaptador

Dentro del propio adaptador en el método getView se instancian cada uno de los elementos que forman parte de la vista, por lo tanto es posible acceder a cualquier evento generado por los mismos.

Supongamos un GridView con un layout formado por un textview un imageview y un checkbox. Si se quiere inferir sobre el cambio de estado del checkbox, tan solo habría que indicárselo en el método getView, dentro de la primera vez que se ejecuta

public View getView(int i, View view, ViewGroup viewGroup) {

    ViewHolder holder;
    if(view==null) {
        view = LayoutInflater.from(context).inflate(R.layout.item_grid, viewGroup, false);
        holder = new ViewHolder();
        TextView nombre = view.findViewById(R.id.textoGrid);
        ImageView imagen = view.findViewById(R.id.imagenGrid);
        CheckBox checkBox = view.findViewById(R.id.checkboxGrid);
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                Toast.makeText(context,"Estado cambiado a "+String.valueOf(b),Toast.LENGTH_SHORT).show();
            }
        });
        holder.imagen = imagen;
        holder.nombre = nombre;
        holder.checkBox = checkBox;
        view.setTag(holder);
    }

    else {
        holder = (ViewHolder) view.getTag();
    }

    Persona temporal = (Persona) listaDatos.get(i);
    holder.nombre.setText(temporal.getNombre());
    holder.imagen.setImageResource(temporal.getImagen());
    holder.checkBox.setChecked(temporal.isFavorite());

    return view;
}