Compartir vía


ViewPager con fragmentos

ViewPager es un administrador de diseño que permite implementar la navegación por gestos. La navegación por gestos permite al usuario deslizar el dedo hacia la izquierda y hacia la derecha para recorrer páginas de datos. En esta guía se explica cómo implementar una interfaz de usuario deslizable con ViewPager usando fragmentos como páginas de datos.

Información general

ViewPager se usa a menudo junto con fragmentos para que sea más fácil administrar el ciclo de vida de cada página en ViewPager. En este tutorial, se usa ViewPager para crear una aplicación denominada FlashCardPager que presenta una serie de problemas matemáticos en tarjetas flash. Cada tarjeta flash se implementa como un fragmento. El usuario desliza el dedo hacia la izquierda y hacia la derecha en las tarjetas flash y pulsa en un problema matemático para mostrar su respuesta. Esta aplicación crea una instancia de Fragment para cada tarjeta flash e implementa un adaptador derivado de FragmentPagerAdapter. En Viewpager y Views, la mayor parte del trabajo se realizó en métodos de ciclo de vida MainActivity. En FlashCardPager, la mayor parte del trabajo se realizará mediante una instancia de Fragment en uno de sus métodos de ciclo de vida.

Esta guía no cubre los conceptos básicos de los fragmentos: si aún no está familiarizado con los fragmentos de Xamarin.Android, consulte Fragmentos para ayudarle a empezar a trabajar con fragmentos.

Iniciar un proyecto de aplicación

Cree un nuevo proyecto de Android denominado FlashCardPager. A continuación, inicie el Administrador de paquetes NuGet (para más información sobre cómo instalar paquetes NuGet, consulte Tutorial: Inclusión de paquetes NuGet en un proyecto). Busque e instale el paquete Xamarin.Android.Support.v4 como se explica en Viewpager y Views.

Agregar un origen de datos de ejemplo

En FlashCardPager, el origen de datos es una baraja de tarjetas flash representadas por la clase FlashCardDeck; este origen de datos proporciona a ViewPager el contenido del elemento. FlashCardDeck contiene una colección de problemas matemáticos y respuestas. El constructor FlashCardDeck no requiere argumentos:

FlashCardDeck flashCards = new FlashCardDeck();

La colección de tarjetas flash de FlashCardDeck está organizada de tal forma que un indexador pueda acceder a cada una de ellas. Por ejemplo, la siguiente línea de código recupera el cuarto problema de la tarjeta flash de la baraja:

string problem = flashCardDeck[3].Problem;

Esta línea de código recupera la respuesta correspondiente al problema anterior:

string answer = flashCardDeck[3].Answer;

Dado que los detalles de implementación de FlashCardDeck no son relevantes para comprender ViewPager, el código de FlashCardDeck no aparece aquí. El código fuente de FlashCardDeck está disponible en FlashCardDeck.cs. Descargue este archivo de código fuente (o copie y pegue el código en un nuevo archivo FlashCardDeck.cs) y agréguelo al proyecto.

Crear un diseño de ViewPager

Abra Resources/layout/Main.axml y reemplace su contenido por el siguiente XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    </android.support.v4.view.ViewPager>

Este XML define un ViewPager que ocupa toda la pantalla. Tenga en cuenta que debe usar el nombre completo android.support.v4.view.ViewPager porque ViewPager está empaquetado en una biblioteca de soporte técnico. ViewPager solo está disponible en la biblioteca de compatibilidad de Android v4; no está disponible en Android SDK.

Configurar ViewPager

Edite MainActivity.cs y agregue las siguientes using instrucciones:

using Android.Support.V4.View;
using Android.Support.V4.App;

Cambie la declaración de la clase MainActivity para que se derive de FragmentActivity:

public class MainActivity : FragmentActivity

MainActivity se deriva de FragmentActivity (en lugar de Activity) porque FragmentActivity sabe cómo administrar la compatibilidad de los fragmentos. Reemplace el método OnCreate con el código siguiente:

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);
    SetContentView(Resource.Layout.Main);
    ViewPager viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
    FlashCardDeck flashCards = new FlashCardDeck();
}

Este código hace lo siguiente:

  1. Establece la vista desde el recurso de diseño Main.axml.

  2. Recupera una referencia a ViewPager desde el diseño.

  3. Crea una instancia de un nuevo FlashCardDeck como origen de datos.

Al compilar y ejecutar este código, debería ver una pantalla similar a la siguiente captura de pantalla:

Captura de pantalla de la aplicación FlashCardPager con ViewPager vacío

En este momento, ViewPager está vacío porque carece de los fragmentos que se usan para rellenar ViewPager y carece de un adaptador para crear estos fragmentos a partir de los datos de FlashCardDeck.

En las secciones siguientes, se crea un objeto FlashCardFragment para implementar la funcionalidad de cada tarjeta flash y se crea un objeto FragmentPagerAdapter para conectar ViewPager a los fragmentos creados a partir de los datos de FlashCardDeck.

Creación del fragmento

Cada tarjeta flash se administrará mediante un fragmento de interfaz de usuario denominado FlashCardFragment. La vista de FlashCardFragment mostrará la información contenida con una sola tarjeta flash. Cada instancia de FlashCardFragment se hospedará en ViewPager. La vista de FlashCardFragment constará de un objeto TextView que muestra el texto del problema de la tarjeta flash. Esta vista implementará un controlador de eventos que usa Toast para mostrar la respuesta cuando el usuario pulse sobre la pregunta de la tarjeta flash.

Creación del diseño de FlashCardFragment

Antes de poder implementar FlashCardFragment, se debe definir su diseño. Este diseño es un diseño de contenedor de fragmentos para un único fragmento. Agregue un nuevo diseño de Android a Resources/layout denominado flashcard_layout.axml. Abra Resources/layout/flashcard_layout.axml y reemplace su contenido por el código siguiente:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/flash_card_question"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textAppearance="@android:style/TextAppearance.Large"
            android:textSize="100sp"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:text="Question goes here" />
    </RelativeLayout>

Este diseño define el fragmento de una sola tarjeta flash; cada fragmento se compone de un objeto TextView que muestra un problema matemático con una fuente grande (100 píxeles escalados). Este texto está centrado vertical y horizontalmente en la tarjeta flash.

Creación de la clase FlashCardFragment inicial

Agregue un nuevo archivo denominado FlashCardFragment.cs y reemplace su contenido por el código siguiente:

using System;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android.Support.V4.App;

namespace FlashCardPager
{
    public class FlashCardFragment : Android.Support.V4.App.Fragment
    {
        public FlashCardFragment() { }

        public static FlashCardFragment newInstance(String question, String answer)
        {
            FlashCardFragment fragment = new FlashCardFragment();
            return fragment;
        }
        public override View OnCreateView (
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            View view = inflater.Inflate (Resource.Layout.flashcard_layout, container, false);
            TextView questionBox = (TextView)view.FindViewById (Resource.Id.flash_card_question);
            return view;
        }
    }
}

Este código crea el esquema básico de la definición de Fragment que se usará para mostrar una tarjeta flash. Tenga en cuenta que FlashCardFragment se deriva de la versión de la biblioteca de compatibilidad de Fragment definida en Android.Support.V4.App.Fragment. El constructor está vacío, así que se usa la instancia de Factory Method newInstance para crear un nuevo objeto FlashCardFragment en lugar de un constructor.

El método de ciclo de vida OnCreateView crea y configura el objeto TextView. Se infla el diseño del objeto TextView del fragmento y se devuelve el objeto TextView inflado al autor de la llamada. LayoutInflater y ViewGroup se pasan a OnCreateView para que se pueda inflar el diseño. La agrupación savedInstanceState contiene datos que OnCreateView usa para volver a crear el objeto TextView a partir de un estado guardado.

La vista del fragmento se infla explícitamente mediante la llamada a inflater.Inflate. El argumento container es el elemento principal de la vista y la marca false indica a la clase Inflater que se abstenga de agregar la vista inflada al elemento principal de la vista (se agregará cuando ViewPager llame al método GetItem del adaptador más adelante en este tutorial).

Adición de código de estado a FlashCardFragment

Al igual que una actividad, un fragmento tiene un objeto Bundle que se usa para guardar y recuperar su estado. En FlashCardPager, este objeto Bundle se usa para guardar el texto de preguntas y respuestas de la tarjeta flash asociada. En FlashCardFragment.cs, agregue las siguientes claves Bundle a la parte superior de la definición de clase FlashCardFragment:

private static string FLASH_CARD_QUESTION = "card_question";
private static string FLASH_CARD_ANSWER = "card_answer";

Modifique la instancia de Factory Method newInstance para que cree un objeto Bundle y use las claves anteriores para almacenar el texto de las preguntas y respuestas pasado en el fragmento después de que se crea una instancia de él:

public static FlashCardFragment newInstance(String question, String answer)
{
    FlashCardFragment fragment = new FlashCardFragment();

    Bundle args = new Bundle();
    args.PutString(FLASH_CARD_QUESTION, question);
    args.PutString(FLASH_CARD_ANSWER, answer);
    fragment.Arguments = args;

    return fragment;
}

Modifique el método de ciclo de vida OnCreateView del fragmento para recuperar esta información del paquete pasado y cargue el texto de la pregunta en TextBox:

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    string question = Arguments.GetString(FLASH_CARD_QUESTION, "");
    string answer = Arguments.GetString(FLASH_CARD_ANSWER, "");

    View view = inflater.Inflate(Resource.Layout.flashcard_layout, container, false);
    TextView questionBox = (TextView)view.FindViewById(Resource.Id.flash_card_question);
    questionBox.Text = question;

    return view;
}

La variable answer no se usa aquí, pero se usará más adelante cuando se agregue código del controlador de eventos a este archivo.

Crear el adaptador

ViewPager usa un objeto controlador de adaptador que se encuentra entre el objeto ViewPager y el origen de datos (consulte la ilustración del artículo Adaptador de ViewPager). Para acceder a estos datos, ViewPager requiere que proporcione un adaptador personalizado derivado de PagerAdapter. Dado que en este ejemplo se usan fragmentos, se utilizará FragmentPagerAdapter: FragmentPagerAdapter se deriva de PagerAdapter. FragmentPagerAdapter representa cada página como un objeto Fragment que se mantiene persistentemente en el administrador de fragmentos siempre y cuando el usuario pueda volver a la página. A medida que el usuario desliza el dedo por las páginas de ViewPager, FragmentPagerAdapter extrae información del origen de datos y la usa para crear objetos Fragment que se mostrarán en ViewPager.

Al implementar un FragmentPagerAdapter, debe invalidar lo siguiente:

  • Recuento : propiedad de solo lectura que devuelve el número de vistas (páginas) disponibles.

  • GetItem: devuelve el fragmento que se va a mostrar para la página especificada.

Agregue un nuevo archivo denominado FlashCardDeckAdapter.cs y reemplace su contenido por el código siguiente:

using System;
using Android.Views;
using Android.Widget;
using Android.Support.V4.App;

namespace FlashCardPager
{
    class FlashCardDeckAdapter : FragmentPagerAdapter
    {
        public FlashCardDeckAdapter (Android.Support.V4.App.FragmentManager fm, FlashCardDeck flashCards)
            : base(fm)
        {
        }

        public override int Count
        {
            get { throw new NotImplementedException(); }
        }

        public override Android.Support.V4.App.Fragment GetItem(int position)
        {
            throw new NotImplementedException();
        }
    }
}

Este código quita el código auxiliar de la implementación esencial de FragmentPagerAdapter. En las secciones siguientes, cada uno de estos métodos se reemplaza por código de trabajo. El propósito del constructor es pasar el administrador de fragmentos al constructor de la clase base de FlashCardDeckAdapter.

Implementación del constructor del adaptador

Cuando la aplicación crea una instancia de FlashCardDeckAdapter, proporciona una referencia al administrador de fragmentos y una instancia de FlashCardDeck. Agregue la siguiente variable miembro a la parte superior de la clase FlashCardDeckAdapter en FlashCardDeckAdapter.cs:

public FlashCardDeck flashCardDeck;

Agregue la siguiente línea de código al constructor FlashCardDeckAdapter:

this.flashCardDeck = flashCards;

Esta línea de código almacena la instancia de FlashCardDeck que FlashCardDeckAdapter usará.

Implementar recuento

La implementación de Countes relativamente sencilla: devuelve el número de tarjetas flash de la baraja. Reemplaza Count por el código siguiente:

public override int Count
{
    get { return flashCardDeck.NumCards; }
}

La propiedad NumCards de FlashCardDeck devuelve el número de tarjetas flash (número de fragmentos) del conjunto de datos.

Implementación de GetItem

El método GetItem devuelve el fragmento asociado a la posición especificada. Cuando se llama a GetItem en relación con una posición en la baraja de tarjetas flash, se devuelve un objeto FlashCardFragment configurado para mostrar el problema de la tarjeta flash correspondiente a esa posición. Reemplace el método GetItem con el código siguiente:

public override Android.Support.V4.App.Fragment GetItem(int position)
{
    return (Android.Support.V4.App.Fragment)
        FlashCardFragment.newInstance (
            flashCardDeck[position].Problem, flashCardDeck[position].Answer);
}

Este código hace lo siguiente:

  1. Busca la cadena del problema matemático en la baraja FlashCardDeck correspondiente a la posición especificada.

  2. Busca la cadena de respuesta en la baraja FlashCardDeck correspondiente a la posición especificada.

  3. Llama al objeto newInstance de la instancia de Factory Method FlashCardFragment y pasa el problema de la tarjeta flash y las cadenas de respuesta.

  4. Crea y devuelve una nueva tarjeta flash Fragment que contiene el texto de preguntas y respuestas correspondiente a esa posición.

Cuando ViewPager representa Fragment en position, muestra el objeto TextBox que contiene la cadena del problema matemático que reside en position en la baraja de tarjetas flash.

Agregar el adaptador a ViewPager

Ahora que se implementa FlashCardDeckAdapter, es el momento de agregarlo a ViewPager. En MainActivity.cs, agregue la siguiente línea de código al final del método OnCreate:

FlashCardDeckAdapter adapter =
    new FlashCardDeckAdapter(SupportFragmentManager, flashCards);
viewPager.Adapter = adapter;

Este código crea una instancia de FlashCardDeckAdapter y pasa SupportFragmentManager en el primer argumento. (La propiedad SupportFragmentManager de FragmentActivity se usa para obtener una referencia a FragmentManager; para más información sobre FragmentManager, consulte Administración de fragmentos).

La implementación principal ya está completa: compile y ejecute la aplicación. Debería ver que la primera imagen de la baraja de tarjetas flash aparece en la pantalla, como se muestra a la izquierda en la captura de pantalla siguiente. Deslice el dedo hacia la izquierda para ver más tarjetas flash y, a continuación, deslice el dedo hacia la derecha para volver atrás en la baraja de tarjetas flash:

Capturas de pantalla de ejemplo de la aplicación FlashCardPager sin indicadores de buscapersonas

Agregar un indicador de buscapersonas

Esta implementación mínima de ViewPager muestra cada tarjeta flash de la baraja, pero no proporciona ninguna indicación sobre dónde está el usuario dentro de la baraja. El siguiente paso consiste en agregar un PagerTabStrip. PagerTabStrip informa al usuario sobre el número del problema que se muestra y proporciona contexto de navegación al mostrar una pista de las tarjetas flash anterior y siguiente.

Abra Resources/layout/Main.axml y agregue un PagerTabStrip al diseño:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

  <android.support.v4.view.PagerTabStrip
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="top"
      android:paddingBottom="10dp"
      android:paddingTop="10dp"
      android:textColor="#fff" />

</android.support.v4.view.ViewPager>

Al compilar y ejecutar la aplicación, debería ver el objeto PagerTabStrip vacío en la parte superior de cada tarjeta flash:

Primer plano de PagerTabStrip sin texto

Mostrar un título

Para agregar un título a cada pestaña de la página, implemente el método GetPageTitleFormatted en el adaptador. ViewPager llama a GetPageTitleFormatted (si se implementa) para obtener la cadena de título que describe la página en la posición especificada. Agregue el método siguiente a la clase FlashCardDeckAdapter en FlashCardDeckAdapter.cs:

public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
{
    return new Java.Lang.String("Problem " + (position + 1));
}

Este código convierte la posición en la baraja de tarjetas flash en un número de problema. La cadena resultante se convierte en un objeto String de Java que se devuelve a ViewPager. Al ejecutar la aplicación con este nuevo método, cada página muestra el número de problema en PagerTabStrip:

Capturas de pantalla de FlashCardPager con el número de problema mostrado encima de cada página

Puede deslizar hacia atrás y hacia adelante para ver el número de problema que se muestra en la parte superior de cada tarjeta flash de la baraja.

Control de la entrada de usuario

FlashCardPager presenta una serie de tarjetas flash basadas en fragmentos en ViewPager, pero aún no hay una manera de revelar la respuesta para cada problema. En esta sección, se agrega un controlador de eventos a FlashCardFragment para mostrar la respuesta cuando el usuario pulsa sobre el texto del problema de la tarjeta flash.

Abra FlashCardFragment.cs y agregue el código siguiente al final del método OnCreateView justo antes de que se devuelva la vista al autor de la llamada:

questionBox.Click += delegate
{
    Toast.MakeText(Activity.ApplicationContext,
            "Answer: " + answer, ToastLength.Short).Show();
};

Este controlador de eventos Click muestra la respuesta en una notificación del sistema que aparece cuando el usuario pulsa sobre TextBox. La variable answer se inicializó anteriormente cuando se leyó la información de estado de la agrupación que se pasó a OnCreateView. Compile y ejecute la aplicación y, a continuación, pulse sobre el texto del problema de cada tarjeta flash para ver la respuesta:

Capturas de pantalla de las notificaciones del sistema de la aplicación FlashCardPager cuando se pulsa el problema matemático

El proyecto FlashCardPager presentado en este tutorial usa un objeto MainActivity derivado de FragmentActivity, pero también puede derivarlo MainActivity de AppCompatActivity (que también proporciona compatibilidad con la administración de fragmentos).

Resumen

En este tutorial se ha proporcionado un ejemplo paso a paso de cómo compilar una aplicación básica basada en ViewPager mediante objetos Fragment. Se ha presentado un origen de datos de ejemplo que contiene preguntas y respuestas de tarjetas flash, un diseño ViewPager para mostrar las tarjetas flash y una subclase FragmentPagerAdapter que conecta ViewPager al origen de datos. Para ayudar al usuario a navegar por las tarjetas flash, se incluyeron instrucciones que explican cómo agregar un objeto PagerTabStrip para mostrar el número de problema en la parte superior de cada página. Por último, se ha agregado código de control de eventos para mostrar la respuesta cuando el usuario pulsa sobre un problema de una tarjeta flash.