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:
Establece la vista desde el recurso de diseño Main.axml.
Recupera una referencia a
ViewPager
desde el diseño.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:
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 Count
es 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:
Busca la cadena del problema matemático en la baraja
FlashCardDeck
correspondiente a la posición especificada.Busca la cadena de respuesta en la baraja
FlashCardDeck
correspondiente a la posición especificada.Llama al objeto
newInstance
de la instancia de Factory MethodFlashCardFragment
y pasa el problema de la tarjeta flash y las cadenas de respuesta.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:
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:
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
:
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:
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.