Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
ViewPager is a layout manager that lets you implement gestural navigation. Gestural navigation allows the user to swipe left and right to step through pages of data. This guide explains how to implement a swipeable UI with ViewPager, using Fragments as the data pages.
Overview
ViewPager is often used in conjunction with fragments so that it is
easier to manage the lifecycle of each page in the ViewPager. In this
walkthrough, ViewPager is used to create an app called
FlashCardPager that presents a series of math problems on flash
cards. Each flash card is implemented as a fragment. The user swipes
left and right through the flash cards and taps on a math problem to
reveal its answer. This app creates a Fragment instance for each
flash card and implements an adapter derived from
FragmentPagerAdapter. In
Viewpager and Views,
most of the work was done in MainActivity lifecycle methods. In
FlashCardPager, most of the work will be done by a Fragment in
one of its lifecycle methods.
This guide does not cover the basics of fragments – if you are not yet familiar with fragments in Xamarin.Android, see Fragments to help you get started with fragments.
Start an App Project
Create a new Android project called FlashCardPager. Next, launch the NuGet Package Manager (for more information about installing NuGet packages, see Walkthrough: Including a NuGet in your project). Find and install the Xamarin.Android.Support.v4 package as explained in Viewpager and Views.
Add an Example Data Source
In FlashCardPager, the data source is a deck of flash cards
represented by the FlashCardDeck class; this data source supplies the
ViewPager with item content. FlashCardDeck contains a ready-made
collection of math problems and answers. The FlashCardDeck
constructor requires no arguments:
FlashCardDeck flashCards = new FlashCardDeck();
The collection of flash cards in FlashCardDeck is organized such that
each flash card can be accessed by an indexer. For example, the
following line of code retrieves the fourth flash card problem in the
deck:
string problem = flashCardDeck[3].Problem;
This line of code retrieves the corresponding answer to the previous problem:
string answer = flashCardDeck[3].Answer;
Because the implementation details of FlashCardDeck are not relevant to
understanding ViewPager, the FlashCardDeck code is not listed here.
The source code to FlashCardDeck is available at
FlashCardDeck.cs.
Download this source file (or copy and paste the code into a new
FlashCardDeck.cs file) and add it to your project.
Create a ViewPager Layout
Open Resources/layout/Main.axml and replace its contents with the following 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>
This XML defines a ViewPager that occupies the entire screen. Note that
you must use the fully-qualified name android.support.v4.view.ViewPager
because ViewPager is packaged in a support library. ViewPager is
available only from the
Android Support Library v4;
it is not available in the Android SDK.
Set up ViewPager
Edit MainActivity.cs and add the following using statements:
using Android.Support.V4.View;
using Android.Support.V4.App;
Change the MainActivity class declaration so that it is derived
from FragmentActivity:
public class MainActivity : FragmentActivity
MainActivity is derived fromFragmentActivity (rather than
Activity) because FragmentActivity knows how to manage the support
of fragments. Replace the OnCreate method with the following code:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
ViewPager viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
FlashCardDeck flashCards = new FlashCardDeck();
}
This code does the following:
Sets the view from the Main.axml layout resource.
Retrieves a reference to the
ViewPagerfrom the layout.Instantiates a new
FlashCardDeckas the data source.
When you build and run this code, you should see a display that resembles the following screenshot:
At this point, the ViewPager is empty because it is lacking the
fragments that are used populate the ViewPager, and it is lacking an
adapter for creating these fragments from the data in
FlashCardDeck.
In the following sections, a FlashCardFragment is create to implement
the functionality of each flash card, and a FragmentPagerAdapter is
created to connect the ViewPager to the fragments created from data
in the FlashCardDeck.
Create the Fragment
Each flash card will be managed by a UI fragment called
FlashCardFragment. FlashCardFragment's view will display the
information contained with a single flash card. Each instance of
FlashCardFragment will be hosted by the ViewPager.
FlashCardFragment's view will consist of a TextView that displays
the flash card problem text. This view will implement an event handler
that uses a Toast to display the answer when the user taps the flash
card question.
Create the FlashCardFragment Layout
Before FlashCardFragment can be implemented, its layout must be
defined. This layout is a fragment container layout for a single
fragment. Add a new Android layout to Resources/layout called
flashcard_layout.axml. Open
Resources/layout/flashcard_layout.axml and replace its contents
with the following code:
<?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>
This layout defines a single flash card fragment; each fragment is
comprised of a TextView that displays a math problem using a large
(100sp) font. This text is centered vertically and horizontally on the
flash card.
Create the Initial FlashCardFragment Class
Add a new file called FlashCardFragment.cs and replace its contents with the following code:
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;
}
}
}
This code stubs out the essential Fragment definition that will be
used to display a flash card. Note that FlashCardFragment is derived
from the support library version of Fragment defined in
Android.Support.V4.App.Fragment. The constructor is empty so that the
newInstance factory method is used to create a new
FlashCardFragment instead of a constructor.
The OnCreateView lifecycle method creates and configures the
TextView. It inflates the layout for the fragment's TextView and
returns the inflated TextView to the caller. LayoutInflater and
ViewGroup are passed to OnCreateView so that it can inflate the
layout. The savedInstanceState bundle contains data that
OnCreateView uses to recreate the TextView from a saved state.
The fragment's view is explicitly inflated by the call to
inflater.Inflate. The container argument is the view's parent, and
the false flag instructs the inflater to refrain from adding the
inflated view to the view's parent (it will be added when ViewPager
call's the adapter's GetItem method later in this walkthrough).
Add State Code to FlashCardFragment
Like an Activity, a fragment has a Bundle that it uses to save and
retrieve its state. In FlashCardPager, this Bundle is used to save
the question and answer text for the associated flash card. In
FlashCardFragment.cs, add the following Bundle keys to the top of
the FlashCardFragment class definition:
private static string FLASH_CARD_QUESTION = "card_question";
private static string FLASH_CARD_ANSWER = "card_answer";
Modify the newInstance factory method so that it creates a Bundle
object and uses the above keys to store the passed question and answer text
in the fragment after it is instantiated:
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;
}
Modify the fragment lifecycle method OnCreateView to retrieve this
information from the passed-in Bundle and load the question text into
the 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;
}
The answer variable is not used here, but it will be used later when
event handler code is added to this file.
Create the Adapter
ViewPager uses an adapter controller object that sits between the
ViewPager and the data source (see the illustration in the ViewPager
Adapter article).
To access this data, ViewPager requires that you provide a custom
adapter derived from PagerAdapter. Because this example uses
fragments, it uses a FragmentPagerAdapter –
FragmentPagerAdapter is derived from PagerAdapter.
FragmentPagerAdapter represents each page as a Fragment that is
persistently kept in the fragment manager for as long as the user can
return to the page. As the user swipes through pages of the
ViewPager, the FragmentPagerAdapter extracts information from the
data source and uses it to create Fragments for the ViewPager to
display.
When you implement a FragmentPagerAdapter, you must override the following:
Count – Read-only property that returns the number of views (pages) available.
GetItem – Returns the fragment to display for the specified page.
Add a new file called FlashCardDeckAdapter.cs and replace its contents with the following code:
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();
}
}
}
This code stubs out the essential FragmentPagerAdapter
implementation. In the following sections, each of these methods is
replaced with working code. The purpose of the constructor is to pass
the fragment manager to the FlashCardDeckAdapter's base class
constructor.
Implement the Adapter Constructor
When the app instantiates the FlashCardDeckAdapter, it supplies a
reference to the fragment manager and an instantiated FlashCardDeck.
Add the following member variable to the top of the
FlashCardDeckAdapter class in FlashCardDeckAdapter.cs:
public FlashCardDeck flashCardDeck;
Add the following line of code to the FlashCardDeckAdapter
constructor:
this.flashCardDeck = flashCards;
This line of code stores the FlashCardDeck instance that the
FlashCardDeckAdapter will use.
Implement Count
The Count implementation is relatively simple: it returns the number
of flash cards in the flash card deck. Replace Count with the
following code:
public override int Count
{
get { return flashCardDeck.NumCards; }
}
The NumCards property of FlashCardDeck returns the number of flash
cards (number of fragments) in the data set.
Implement GetItem
The GetItem method returns the fragment associated with the given
position. When GetItem is called for a position in the flash card
deck, it returns a FlashCardFragment configured to display the flash
card problem at that position. Replace the GetItem method with the
following code:
public override Android.Support.V4.App.Fragment GetItem(int position)
{
return (Android.Support.V4.App.Fragment)
FlashCardFragment.newInstance (
flashCardDeck[position].Problem, flashCardDeck[position].Answer);
}
This code does the following:
Looks up the math problem string in the
FlashCardDeckdeck for the specified position.Looks up the answer string in the
FlashCardDeckdeck for the specified position.Calls the
FlashCardFragmentfactory methodnewInstance, passing in the flash card problem and answer strings.Creates and returns a new flash card
Fragmentthat contains the question and answer text for that position.
When the ViewPager renders the Fragment at position, it displays
the TextBox containing the math problem string residing at position
in the flash card deck.
Add the Adapter to the ViewPager
Now that the FlashCardDeckAdapter is implemented, it's time to add it to the
ViewPager. In MainActivity.cs, add the following line of code to the
end of the OnCreate method:
FlashCardDeckAdapter adapter =
new FlashCardDeckAdapter(SupportFragmentManager, flashCards);
viewPager.Adapter = adapter;
This code instantiates the FlashCardDeckAdapter, passing in the
SupportFragmentManager in the first argument. (The
SupportFragmentManager property of FragmentActivity is used to get a
reference to the FragmentManager – for more information about
the FragmentManager, see
Managing Fragments.)
The core implementation is now complete – build and run the app. You should see the first image of the flash card deck appear on the screen as shown on the left in the next screenshot. Swipe left to see more flash cards, then swipe right to move back through the flash card deck:
Add a Pager Indicator
This minimal ViewPager implementation displays each flash card in the
deck, but it provides no indication as to where the user is within the
deck. The next step is to add a PagerTabStrip. The PagerTabStrip
informs the user as to which problem number is displayed and provides
navigation context by displaying a hint of the previous and next flash
cards.
Open Resources/layout/Main.axml and add a PagerTabStrip to the layout:
<?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>
When you build and run the app, you should see the empty
PagerTabStrip displayed at the top of each flash card:
Display a Title
To add a title to each page tab, implement the GetPageTitleFormatted
method in the adapter. ViewPager calls GetPageTitleFormatted (if
implemented) to obtain the title string that describes the page at the
specified position. Add the following method to the
FlashCardDeckAdapter class in FlashCardDeckAdapter.cs:
public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String("Problem " + (position + 1));
}
This code converts the position in the flash card deck to a problem
number. The resulting string is converted into a Java String that is
returned to the ViewPager. When you run the app with this new method,
each page displays the problem number in the PagerTabStrip:
You can swipe back and forth to see the problem number in the flash card deck that is displayed at the top of each flash card.
Handle User Input
FlashCardPager presents a series of fragment-based flash cards in a
ViewPager, but it does not yet have a way to reveal the answer for
each problem. In this section, an event handler is added to the
FlashCardFragment to display the answer when the user taps on the
flash card problem text.
Open FlashCardFragment.cs and add the following code to the end of
the OnCreateView method just before the view is returned to the caller:
questionBox.Click += delegate
{
Toast.MakeText(Activity.ApplicationContext,
"Answer: " + answer, ToastLength.Short).Show();
};
This Click event handler displays the answer in a Toast that appears
when the user taps the TextBox. The answer variable was initialized
earlier when state information was read from the Bundle that was passed
to OnCreateView. Build and run the app, then tap the problem text on
each flash card to see the answer:
The FlashCardPager presented in this walkthrough uses a
MainActivity derived from FragmentActivity, but you can also derive
MainActivity from AppCompatActivity (which also provides support
for managing fragments).
Summary
This walkthrough provided a step-by-step example of how to build a basic
ViewPager-based app using Fragments. It presented an example data
source containing flash card questions and answers, a ViewPager
layout to display the flash cards, and a FragmentPagerAdapter
subclass that connects the ViewPager to the data source. To help the
user navigate through the flash cards, instructions were included that
explain how to add a PagerTabStrip to display the problem number at
the top of each page. Finally, event handling code was added to display
the answer when the user taps on a flash card problem.




