En este artículo se van a desglosar los pasos necesarios para crear una aplicación Android que haga uso de la vista Maestro-Detalle de la manera más sencilla posible, mostrando una lista de personas, de manera que al seleccionar una de ellas, se muestre en la siguiente pantalla sus datos detallados.

Las siguientes capturas muestran el funcionamiento. Al hacer clic en un nombre de la lista, aparecerá el resto de sus datos en la siguiente pantalla:

 

Este tipo de Activity cambia de aspecto automáticamente cuando se muestra en una pantalla grande como en la de una tablet, juntado en la misma pantalla la lista y el detalle:

 

Al crear un nuevo proyecto desde Android Studio podemos seleccionar el tipo de la Activity principal que va a iniciar la aplicación. En el asistente se debe seleccionar el tipo Master-Detail.

También puedes crear este tipo de Activity en un proyecto preexistente, usando el menú contextual en la carpeta app, donde puede usar la opción New > Activity > Master/Detail Flow.

En uno de los pasos del asistente de ese tipo de Activity, nos preguntará sobre el nombre que deseamos asignar a los elementos que van a formar parte de la lista (en singular y en plural). Para este ejemplo se ha puesto Person y People: Esto simplemente afecta a los nombres que asignará a los archivos que creará automáticamente (Activities y Layouts).

En la siguiente imagen puedes ver la estructura de archivos que ha creado para las clases Java y Layouts:

También puedes observar que se crea una carpeta dummy, que contiene la clase DummyContent que puede eliminarse, y que sólo son un ejemplo de uso para este tipo de Activity. Esa clase se va a sustituir por otras dos que harán referencia al tipo de objeto Person y a una lista de personas PersonContent, que se van crear dentro de una carpeta person por tenerlo mejor organizado.

Clase Person

Es la que describe el tipo de objetos que se van a utilizar en la lista, con las propiedades que podrá tener. En este ejemplo, se ha creado esta clase con más propiedades de las que realmente se están utilizando.

Es importante observar el método toString, ya que será el que sea invocado para mostrar los elementos en la lista. En este ejemplo, ese método retornará el nombre de la persona seguido de sus apellidos, que como puedes ver en la lista de la pantalla, es como se mostrará cada persona.

package es.javiergarbedo.tutorialmasterdetail.person;
 
import java.util.Date;
 
public class Person {
 
    private int id;
    private String name = "";
    private String surnames = "";
    private String alias = "";
    private String email = "";
    private String phoneNumber = "";
    private String mobileNumber = "";
    private String address = "";
    private String postCode = "";
    private String city = "";
    private String province = "";
    private String country = "";
    private Date birthDate = null;
    private String comments = "";
    private String photoFileName = "";
 
    public Person() {
    }
 
    public Person(int id, String name, String surnames, String alias, String email, String phoneNumber, String mobileNumber, String address, String postCode, String city, String province, String country, Date birthDate, String comments, String photoFileName) {
        this.id = id;
        this.name = name;
        this.surnames = surnames;
        this.alias = alias;
        this.email = email;
        this.phoneNumber = phoneNumber;
        this.mobileNumber = mobileNumber;
        this.address = address;
        this.postCode = postCode;
        this.city = city;
        this.province = province;
        this.country = country;
        this.birthDate = birthDate;
        this.comments = comments;
        this.photoFileName = photoFileName;
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getSurnames() {
        return surnames;
    }
 
    public void setSurnames(String surnames) {
        this.surnames = surnames;
    }
 
    public String getAlias() {
        return alias;
    }
 
    public void setAlias(String alias) {
        this.alias = alias;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getPhoneNumber() {
        return phoneNumber;
    }
 
    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
 
    public String getMobileNumber() {
        return mobileNumber;
    }
 
    public void setMobileNumber(String mobileNumber) {
        this.mobileNumber = mobileNumber;
    }
 
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address = address;
    }
 
    public String getPostCode() {
        return postCode;
    }
 
    public void setPostCode(String postCode) {
        this.postCode = postCode;
    }
 
    public String getCity() {
        return city;
    }
 
    public void setCity(String city) {
        this.city = city;
    }
 
    public String getProvince() {
        return province;
    }
 
    public void setProvince(String province) {
        this.province = province;
    }
 
    public String getCountry() {
        return country;
    }
 
    public void setCountry(String country) {
        this.country = country;
    }
 
    public Date getBirthDate() {
        return birthDate;
    }
 
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
 
    public String getComments() {
        return comments;
    }
 
    public void setComments(String comments) {
        this.comments = comments;
    }
 
    public String getPhotoFileName() {
        return photoFileName;
    }
 
    public void setPhotoFileName(String photoFileName) {
        this.photoFileName = photoFileName;
    }
 
    @Override
    public String toString() {
        return name + " " + surnames;
    }
 
}
 

Clase PersonContent

Contiene una propiedad de tipo ArrayList que almacenará la lista de personas que se utilizarán en la aplicación. El método loadPersonList permite rellenar la lista con una serie de personas con algunos datos ficticios.

package es.javiergarbedo.tutorialmasterdetail.person;
 
import java.util.ArrayList;
import java.util.List;
 
public class PersonContent {
 
    //Lista que almacenará las personas
    private static ArrayList<Person> personList = new ArrayList();
 
    public static ArrayList getPersonList() {
        return personList;
    }
 
    /**
     * Carga en la lista personList una serie de personas con datos ficticios
     */
    public static void loadPersonList() {
        Person person;
 
        person = new Person();
        person.setId(1);
        person.setName("AMADOR");
        person.setSurnames("OSSORIO MUÑOZ");
        person.setMobileNumber("684402588");
        person.setEmail("Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.");
        personList.add(person);
 
        person = new Person();
        person.setId(2);
        person.setName("BALTASAR");
        person.setSurnames("LORENTE FERNANDEZ");
        person.setMobileNumber("675435350");
        person.setEmail("Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.");
        personList.add(person);
 
        person = new Person();
        person.setId(3);
        person.setName("JESUS");
        person.setSurnames("PEDRO IGLESIAS");
        person.setMobileNumber("611932600");
        person.setEmail("Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.");
        personList.add(person);
    }
 
}
 

Cambios en la clase PersonListFragment

Esta clase describe el funcionamiento del Fragment que se incluirá dentro de la Activity de la lista. En el código que se ha modificado, se ejecuta el método loadPersonList para cargar los datos de las personas en la lista, sólo en caso de que la lista estuviera vacía. Posteriormente, la llamada a setListAdapter permite convertir el ArrayList de los datos para que pueda ser utilizada en la lista de la pantalla, mostrando el nombre de cada persona en el elemento android.R.id.text1.

El método onListItemClick se ha modificado también, para que pase a mCallbacks.onItemSelected la posición del elemento de la lista que haya seleccionado el usuario. Será esa posición, convertida a String, la que usemos para identificar qué elemento de la lista se ha seleccionado. Esa información se pasa (en otra parte del código que no hay que tocar) al Fragment del detalle, y así se podrán obtener los datos de esa persona en concreto para mostrarlas en pantalla.

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // TODO: replace with a real list adapter.
//        setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(
//                getActivity(),
//                android.R.layout.simple_list_item_activated_1,
//                android.R.id.text1,
//                DummyContent.ITEMS));
        if(PersonContent.getPersonList().isEmpty()) {
            PersonContent.loadPersonList();
        }
        // simple_list_item_activated_1 sólo es soportado a partir de API 11
        // lo cambio por simple_list_item_1
        setListAdapter(new ArrayAdapter<Person>(
                getActivity(),
                android.R.layout.simple_list_item_1,
                android.R.id.text1,
                PersonContent.getPersonList()));
    }

 

    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);
 
        // Notify the active callbacks interface (the activity, if the
        // fragment is attached to one) that an item has been selected.
//        mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
        mCallbacks.onItemSelected(String.valueOf(position));
    }

 

Cambios en la clase PersonDetailFragment

Se encarga de mostrar en pantalla los datos detallados de la persona que previamente haya seleccionado el usuario en la lista. Declara una propiedad person donde se almacenará el objeto relacionado con la posición seleccionada en la lista.

Durante el proceso de creación de este Fragment (onCreate), se obtendrá la posición seleccionada (index) de la lista, que viene como String desde la clase PersonListFragment, convirtiéndla a tipo int para utilizarla posteriormente.

Sabiendo la posición seleccionada, se obtiene la persona almacenada en esa misma posición dentro del ArrayList.

Por otro lado, en el método onCreateView se asignan los datos de la persona a los elementos del layout del detalle, que serán los que se muestren en pantalla.

public class PersonDetailFragment extends Fragment {
 
    public static final String ARG_ITEM_ID = "item_id";
 
//    private DummyContent.DummyItem mItem;
    private Person person;
 
    public PersonDetailFragment() {
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        if (getArguments().containsKey(ARG_ITEM_ID)) {
//            mItem = DummyContent.ITEM_MAP.get(getArguments().getString(ARG_ITEM_ID));
            //Obtener el número de orden en la lista del elemento seleccionado por el usuario
            int index = Integer.valueOf(getArguments().getString(ARG_ITEM_ID));
            //Obtener la persona que se se encuentra en esa posición de la lista
            person = (Person)PersonContent.getPersonList().get(index);
        }
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_person_detail, container, false);
 
//        if (mItem != null) {
//            ((TextView) rootView.findViewById(R.id.person_detail)).setText(mItem.content);
//        }
        if(person != null) {
            //Rellenar los elementos de la pantalla de detalle
            ((TextView) rootView.findViewById(R.id.textViewName)).setText(person.getName());
            ((TextView) rootView.findViewById(R.id.textViewSurnames)).setText(person.getSurnames());
            ((TextView) rootView.findViewById(R.id.textViewMobileNumber)).setText(person.getMobileNumber());
            ((TextView) rootView.findViewById(R.id.textViewEmail)).setText(person.getEmail());
        }
 
        return rootView;
    }
}

 

Estructura del layout de detalle fragment_person_detail.xml 

<ScrollView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView">
 
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Nombre:"
            android:id="@+id/textView" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/textViewName"
            android:text="textViewName" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Apellidos:"
            android:id="@+id/textView3" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="textViewSurnames"
            android:id="@+id/textViewSurnames" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Teléfono:"
            android:id="@+id/textView5" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="textViewMobileNumber"
            android:id="@+id/textViewMobileNumber" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Email:"
            android:id="@+id/textView7" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="textViewEmail"
            android:id="@+id/textViewEmail" />
    </LinearLayout>
</ScrollView>