Con el fin de organizar la información del detalle, especialmente cuando dichos datos son numerosos o de distintos tipos, puede ser interesante organizar la vista de detalle en varias pantallas, a las que se accederá a través de pestañas o como si se pasaran páginas arrastrando lateralmente la pantalla.

Cambios en el layout de la activity de detalle

En el archivo de layout activity_xxxxx_detail.xml se deben hacer algunos cambios para añadir los elementos que van a permitir almacenar varias páginas en dentro de la misma activity y para que se muestre la barra de pestañas.

En la parte izquierda del siguiente esquema puedes ver los elementos que componían este layout hasta ahora, tal como lo había creado el asistente del proyecto master-detail. La parte derecha es la estructura que hay que darle para que pueda funcionar de la manera que ahora deseamos.

Como puedes ver en el esquema, debes añadir un elemento TabLayout, como un nuevo elemento dentro del contenedor CoordinatorLayout. Este elemento va a permitir mostrar la barra de pestañas. Observa el id que le vas a asignar porque deberás utilizarlo posteriormente en el código Java. En el código que se propone, se le da el mismo color de fondo que el color principal de la aplicación, pero puedes asignar el que desees. Por último, ten en cuenta que se hace referencia al id del elemento AppBarLayout en el atributo layout_anchor, para que esta barra de pestañas quede anclada a la barra superior de la aplicación, por lo que comprueba que ese id se corresponde con el que tengas en el AppBarLayout que estará a continuación en el mismo archivo.

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="?attr/colorPrimary"
        app:layout_anchor="@+id/app_bar"
        app:layout_anchorGravity="bottom" />

El elemento NestedScrollView es el que ya realizaba el efecto de scroll desde la mitad inferior de la pantalla, de manera que la barra de título aparece inicialmente con bastante tamaño en altura y al hacer scroll hacia arriba en la pantalla se encoge hasta quedar sólo como una barra de título habitual en la parte superior. Este elemento NestedScrollView deberá colocarse dentro de los layout que formen la estructura del contenido de cada pestaña (fragment), así que de momento se debe eliminar del layout de la activity copiándolo como elemento raíz de los layout de los fragment. El id que tiene el NestedScrollView deberás usarlo para el elemento ViewPager que lo va a sustituir como se va a indicar a continuación, por que deberás asignarle un nuevo id o incluso dejarlo sin ningún id, eliminado todo el atributo android:id.

En lugar del NestedScrollView se va a colocar un elemento de tipo ViewPager que será el que permita alojar varias páginas de manera que se pueda pasar de una a otra haciendo scroll lateralmente. A este elemento debes asignarle el id que tenía el NestedScrollView anterior, ya que ese id es el que se utiliza en el código Java para alojar, en el elemento que lo tenga, la información que se mostrará. El código que debe tener es el siguiente:

        <android.support.v4.view.ViewPager
            android:id="@+id/person_detail_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="?attr/actionBarSize"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

El archivo completo en este ejemplo ha quedado como sigue:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".PersonDetailActivity"
    tools:ignore="MergeRootFrame">
 
    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:toolbarId="@+id/toolbar">
 
            <android.support.v7.widget.Toolbar
                android:id="@+id/detail_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
 
        </android.support.design.widget.CollapsingToolbarLayout>
 
    </android.support.design.widget.AppBarLayout>
 
    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="?attr/colorPrimary"
        app:layout_anchor="@+id/app_bar"
        app:layout_anchorGravity="bottom" />
 
    <!--    <android.support.v4.widget.NestedScrollView
            android:id="@+id/container_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
 
    <android.support.v4.view.ViewPager
        android:id="@+id/person_detail_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?attr/actionBarSize"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|start"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/stat_notify_chat"
        app:layout_anchor="@+id/person_detail_container"
        app:layout_anchorGravity="top|end" />
 
</android.support.design.widget.CoordinatorLayout>

Cambios en la clase Java de la activity de detalle

La clase Java xxxxxDetailActivity es la encargada de gestionar toda esta pantalla de detalle, por tanto hay que realizar en ella algunos cambios para hacer que funcionen las pestañas. Hasta ahora esta clase Activity se encargaba de mostrar toda la parte superior de la pantalla, con la barra de título, y en la parte inferior cargaba un Fragment con los datos de detalle. Ahora, no sólo debe cargar un Fragment, sino que deberá poder mostrar más de un Fragment con el contenido de cada pestaña.

En el método onCreate de esta clase se encuentra el código que se encargaba de incrustar en esta Activity el Fragment xxxxxDetailFragment. Ese trozo de código debe ser eliminado y sustituido por el siguiente que se encarga de insertar en esta Actiivty la barra de pestañas (TabLayout) y la posibilidad de mostrar varias páginas (ViewPager). Ten en cuenta que en este código se hace referencia a los id que se ha asignado a esos elemento en el layout anterior. Además, como puedes observar, se hace referencia a la clase SectionsPagerAdapter que vas a crear a continuación, dentro de esta misma clase.

//        if (savedInstanceState == null) {
//            // Create the detail fragment and add it to the activity
//            // using a fragment transaction.
//            Bundle arguments = new Bundle();
//            arguments.putString(PersonDetailFragment.ARG_ITEM_ID,
//                    getIntent().getStringExtra(PersonDetailFragment.ARG_ITEM_ID));
//            PersonDetailFragment fragment = new PersonDetailFragment();
//            fragment.setArguments(arguments);
//            getSupportFragmentManager().beginTransaction()
//                    .add(R.id.person_detail_container, fragment)
//                    .commit();
//        }
 
        SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
 
        ViewPager mViewPager = (ViewPager) findViewById(R.id.person_detail_container);
        mViewPager.setAdapter(mSectionsPagerAdapter);
 
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager); 

La clase SectionsPagerAdapter, a la que se acaba de hacer referencia, debes crearla dentro de esta misma clase de la Activity. Como puedes ver en el siguiente código, esta clase va a permitir cargar dentro de la Activity un Fragment u otro en función de la pestaña que seleccione el usuario. El método getCount() debe retornar el número de pestañas que vaya a utilizar la aplicación, y el método getPageTitle() debe retornar los títulos de cada pestaña. En este ejemplo se retornan los títulos como cadenas de texto literales, pero sería conveniente utilizar recursos String, como siempre se recomienda.

Ten en cuenta que debes crear una nueva clase Java para cada Fragment asociado al contenido de cada pestaña y, a su vez, un layout asociado a cada Fragment con la estructura de pantalla. Lo más sencillo puede ser copiar la clase xxxxxDetailFragment y el layout fragment_xxxxx_detail tantas veces como pestañas se quiera tener, y luego personalizarlo como se desee. En este ejemplo se han creado las clases PersonDetailFragment2PersonDetailFragment3, de las cuales se crean objetos en el switch del método getItem() del siguiente código.

    public class SectionsPagerAdapter extends FragmentPagerAdapter {
 
        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }
 
        @Override
        public Fragment getItem(int position) {
            Bundle arguments = new Bundle();
            arguments.putString(PersonDetailFragment.ARG_ITEM_ID,
                    getIntent().getStringExtra(PersonDetailFragment.ARG_ITEM_ID));
            switch(position) {
                case 0:
                    PersonDetailFragment fragment1 = new PersonDetailFragment();
                    fragment1.setArguments(arguments);
                    return fragment1;
                case 1:
                    PersonDetailFragment2 fragment2 = new PersonDetailFragment2();
                    fragment2.setArguments(arguments);
                    return fragment2;
                case 2:
                    PersonDetailFragment3 fragment3 = new PersonDetailFragment3();
                    fragment3.setArguments(arguments);
                    return fragment3;
            }
            return null;
        }
 
        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }
 
        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "Main Details";
                case 1:
                    return "Address";
                case 2:
                    return "Email";
            }
            return null;
        }
    } 

Layout de los Fragment de detalle

Como se ha comentado, cada clase Java que se haya creado para los Fragment debe estar asociada a un Layout que defina la estructura en pantalla de su contenido. En la clase Java se hará referencia al Layout asociado en la línea que se encuentra dentro del método onCreateView. Por ejemplo, en una de las clases de este ejemplo aparece así:

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_person_detail, container, false);

El contenido de cada uno de esos layouts será el contenido que se mostrará al seleccionar cada pestaña, y para que siga funcionando el scroll de manera que la barra de título se encoja, el elemento raíz de estos layout debe ser el elemento NestedScrollView del que se había hablado al principio. Así por ejemplo, uno de esos layouts ha quedado algo así:

<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
 
        <TextView ...
 
        <TextView ...
 
        <TextView ...
 
    </LinearLayout>
 
</android.support.v4.widget.NestedScrollView>

Código fuente

Puedes ver el código fuente del proyecto hasta este punto en el siguiente commit del proyecto alojado en GitHub:

https://github.com/javiergarciaescobedo/AddressBookDroid/tree/f4602e6bae6d59e706c0a204f73100d7209b9c76

 

Ver todos los artículos de este tutorial