En el artículo Uso básico de Activity Master-Detail se había tratado el tema de crear una lista en Android usando el asistente de Activity Master-Detail. Ahí podíamos ver que se creaba de una manera bastante automatizada el código necesario para que una aplicación pudiera utilizar una lista de elementos, y en el momento en que el usuario seleccione un elemento, se mostraría la información detallada de ese elemento en otra Activity.

Parte de la información contenida en ese artículo será necesario para poder realizar las operaciones que se realicen ahora, por lo que se debería completar previamente los pasos que ahí se indican.

Ahora se pretende que la lista sea más personalizada. Por defecto, la lista se crea de manera que únicamente se muestra una línea de texto por cada entrada. Ahora vamos a intentar mostrar una imagen, un texto principal y otro secundario para cada elemento de la lista. El resultado será similar al que se muestra en la siguiente imagen:

Llamada al método setListAdapter

Para realizar esta personalización de los elementos de la lista hay que crear una clase Java heredada de la clase ArrayAdapter. Si recuerdas el artículo anterior, se utilizaba una llamada al método setListAdapter al que se le pasa por parámetro un objeto ArrayAdapter para convertir el contenido del ArrayList que contiene los datos a una lista que se pueda mostrar en pantalla. Se hizo algo como esto (en la clase PersonListFragment):

        setListAdapter(new ArrayAdapter<Person>(
                getActivity(),
                android.R.layout.simple_list_item_1,
                android.R.id.text1,
                PersonContent.getPersonList()));

En el caso que estamos tratando, hay que pasar al método setListAdapter un objeto de una clase hija de ArrayAdapter. Como veremos posteriormente, la nueva clase heredada de ArrayAdapter personalizada la vamos a llamar MyArrayAdapter, al que haremos un método constructor que tenga únicamente como parámetros el contexto (getActivity()) y la lista de datos. Por tanto, el código mostrado anteriormente hay que sustituirlo por este otro:

        setListAdapter(new MyArrayAdapter(getActivity(), PersonContent.getPersonList()));

Recuerda que previamente se había cargado la lista de datos con algunos casos de ejemplo usando PersonContent.loadPersonList(), y eso debe permanecer igual.

Clase Java MyArrayAdapter

Este es el código que se ha utilizado para crear la clase MyArrayAdapter

/* Se crea esta clase como una extensión (hija) de ArrayAdapter (extends ArrayAdapter),
 que es la clase que se utiliza normalmente para cargar valores en un ListView. De esta manera
 conseguiremos tener las mismas funcionalidades que la clase ArrayAdapater pero haciendo la
 personalización que deseemos */
public class MyArrayAdapter extends ArrayAdapter {
 
    private final Context context;
    //TODO: Cambiar el tipo de elementos contenidos en la lista
    private final ArrayList<Person> valuesList; //Lista de objetos que va a contener la lista
 
    /**
     * Constructor que permite cargar las propiedades de esta clase, llamando también al constructor
     * original de ArrayAdapter
     *
     * @param context    Contexto (p.e. getActivity())
     * @param valuesList Lista de objetos que va a contener la lista
     */
    public MyArrayAdapter(Context context, ArrayList valuesList) {
        //TODO: Comprobar el nombre del layout que contiene estructura de elemento de la lista
        super(context, R.layout.rowlayout, valuesList);
        this.context = context;
        this.valuesList = valuesList;
    }
 
    /* Sobrecargar el método getView para rellenar el elemento de la lista con los valores
    almacenados en la propiedad values de esta clase
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
        //TODO: Comprobar el nombre del layout que contiene estructura de elemento de la lista
        View rowView = inflater.inflate(R.layout.rowlayout, parent, false);
 
        //Obtener el objeto correspondiente a la posición de la lista
        //TODO: Cambiar el tipo de objeto
        Person person = valuesList.get(position);
 
        //TODO: Rellenar los componentes (Views) del layout con el valor correspondiente del array
        // values, obteniendo previamente la variable que haga referencia a cada componente
 
        //Cargar en el TextView de letra grande el alias del contacto
        TextView textViewAlias = (TextView) rowView.findViewById(R.id.textViewAlias);
        textViewAlias.setText(person.getAlias());
 
        //Cargar en el TextView de letra pequeña el nombre completo del contacto
        TextView textViewFullName = (TextView) rowView.findViewById(R.id.textViewFullName);
        textViewFullName.setText(person.getSurnames() + ", " + person.getName());
 
        //Cargar la imagen con la foto del contacto
        ImageView imageViewPhoto = (ImageView) rowView.findViewById(R.id.imageViewPhoto);
        int id = context.getResources().getIdentifier(
                "drawable/" + person.getPhotoFileName(),
                "drawable",
                context.getPackageName());
        imageViewPhoto.setImageDrawable(context.getResources().getDrawable(id));
 
        return rowView;
    }
}

Los comentarios indicados con el prefijo TODO (To_do: Por hacer) indican los cambios que deberías realizar para crear tu propia lista con otro tipo de objetos. 

Layout rowlayout.xml

Observa que se hace referencia al layout (rowlayout) que debe contener la estructura de cada elemento de la lista. En este ejemplo se ha querido hacer con una imagen junto a dos líneas de texto principal y secundario. Por tanto, el layout ha quedado como se muestra a continuación:

<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <!-- TODO: Fijar un ancho en la imagen para que quede todo alineado -->
    <ImageView
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewPhoto"
        android:src="@drawable/ic_launcher"
        android:layout_margin="5dp" />
 
    <!-- TODO: La altura debe ajustarse al contenido para que se muestre todo el contenido -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Large Text"
            android:id="@+id/textViewAlias"
            android:enabled="false" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text="Small Text"
            android:id="@+id/textViewFullName" />
    </LinearLayout>
</LinearLayout>

El diseño ha quedado visualmente así:

 

Observa que se ha utilizado el icono por defecto de la aplicación sólo como modelo de imagen para tener una idea de cómo quedarían las imágenes en la lista.

Proyecto completo en GitHub

https://github.com/clases-javiergarbedo/TutorialMasterDetail