带片段的 ViewPager
ViewPager 是一个布局管理器,可用于实现手势导航。 通过 Gestural 导航,用户可以向左和向右轻扫浏览数据页。 本指南介绍如何使用 Fragments 作为数据页,通过 ViewPager 实现可轻扫 UI。
概述
ViewPager
通常与片段结合使用,以便更轻松地管理 ViewPager
中每个页面的生命周期。 在本演练中,ViewPager
用于创建名为 FlashCardPager 的应用,该应用在抽认卡上呈现一系列数学问题。 每个抽认卡都作为片段实现。 用户通过抽认卡向左和向右轻扫,然后点击数学问题以显示答案。 此应用为每个抽认卡创建一个 Fragment
实例,并实现派生自 FragmentPagerAdapter
的适配器。 在 Viewpager 和 Views中,大部分工作是在 MainActivity
生命周期方法中完成的。 在 FlashCardPager中,大部分工作将由 Fragment
在其生命周期方法之一完成。
如果尚不熟悉 Xamarin.Android 中的片段,本指南不介绍片段的基础知识,请参阅片段帮助你开始使用片段。
启动应用项目
创建名为 FlashCardPager 的新 Android 项目。 接下来,启动 NuGet 包管理器(有关安装 NuGet 包的详细信息,请参阅演练:在项目中包括 NuGet)。 查找并安装 Xamarin.Android.Support.v4 包,如 Viewpager 和 Views 中所述。
添加示例数据源
在 FlashCardPager中,数据源是一系列由 FlashCardDeck
类表示的抽认卡;此数据源提供包含项内容的 ViewPager
。 FlashCardDeck
包含一组现成的数学问题和答案。 FlashCardDeck
构造函数不需要任何参数:
FlashCardDeck flashCards = new FlashCardDeck();
FlashCardDeck
中的抽认卡集合进行组织,以便索引器可以访问每个抽认卡。 例如,以下代码行检索排列中的第四个抽认卡问题:
string problem = flashCardDeck[3].Problem;
此代码行检索对上一问题的相应答案:
string answer = flashCardDeck[3].Answer;
由于 FlashCardDeck
的实现详细信息与理解 ViewPager
无关,因此此处未列出 FlashCardDeck
代码。
FlashCardDeck.cs 提供了要 FlashCardDeck
的源代码。
下载此源文件(或将代码复制并粘贴到新的 FlashCardDeck.cs 文件中),并将其添加到项目中。
创建 ViewPager 布局
打开 Resources/layout/Main.axml,并将其内容替换为以下 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>
此 XML 定义占用整个屏幕的 ViewPager
。 请注意,必须使用完全限定名称 android.support.v4.view.ViewPager,因为 ViewPager
打包在支持库中。 ViewPager
仅适用于 Android 支持库 v4;它在 Android SDK 中不可用。
设置 ViewPager
编辑 MainActivity.cs 并添加以下 using
语句:
using Android.Support.V4.View;
using Android.Support.V4.App;
更改 MainActivity
类声明,使其派生自 FragmentActivity
:
public class MainActivity : FragmentActivity
MainActivity
派生自FragmentActivity
(而不是 Activity
),因为 FragmentActivity
知道如何管理片段的支持。 将 OnCreate
方法替换为以下代码:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
ViewPager viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
FlashCardDeck flashCards = new FlashCardDeck();
}
此代码执行以下操作:
设置 Main.axml 布局资源的视图。
从布局中检索对
ViewPager
的引用。将新的
FlashCardDeck
实例化为数据源。
生成并运行此代码时,应会看到类似于以下屏幕截图的显示:
此时,ViewPager
为空,因为它缺少用于填充 ViewPager
的片段,并且缺少适配器用于从 FlashCardDeck 中的数据创建这些片段。
在以下部分中,将创建一个 FlashCardFragment
来实现每个抽认卡的功能,并创建一个 FragmentPagerAdapter
,用于将 ViewPager
连接到从 FlashCardDeck
中的数据创建的片段。
创建片段
每个抽认卡将由名为 FlashCardFragment
的 UI 片段管理。 FlashCardFragment
的视图将显示包含单个抽认卡的信息。 FlashCardFragment
的每个实例将由 ViewPager
托管。
FlashCardFragment
的视图将包含显示抽认卡问题文本的 TextView
。 此视图将实现事件处理程序,该事件处理程序使用 Toast
在用户点击抽认卡问题时显示答案。
创建 FlashCardFragment 布局
在实现 FlashCardFragment
之前,必须定义其布局。 此布局是单个片段的片段容器布局。 将新的 Android 布局添加到称为 flashcard_layout.axml 的资源/布局。 打开 Resources/layout/flashcard_layout.axml,并将其内容替换为以下代码:
<?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>
此布局定义单个抽认卡片段;每个片段由一个 TextView
组成,该使用大字体 (100sp) 字体显示数学问题。 此文本在抽认卡上垂直和水平居中。
创建初始 FlashCardFragment 类
添加名为 FlashCardFragment.cs 的新文件,并将其内容替换为以下代码:
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;
}
}
}
此代码会存根用于显示抽认卡的基本 Fragment
定义。 请注意,FlashCardFragment
派生自 Android.Support.V4.App.Fragment
中定义的 Fragment
的支持库版本。 构造函数为空,以便 newInstance
工厂方法用于创建新的 FlashCardFragment
而不是构造函数。
OnCreateView
生命周期方法创建和配置 TextView
。 它夸大片段的 TextView
布局,并将膨胀的 TextView
返回到调用方。 LayoutInflater
和 ViewGroup
传递给 OnCreateView
,以便它可以膨胀布局。 savedInstanceState
捆绑包包含 OnCreateView
用于从保存状态重新创建 TextView
的数据。
片段的视图由调用 inflater.Inflate
显式膨胀。 container
参数是视图的父级,false
标志指示膨胀器不要将膨胀视图添加到视图的父级(ViewPager
调用适配器的 GetItem
方法稍后在本演练中添加)。
将状态代码添加到 FlashCardFragment
与活动一样,片段具有用于保存和检索其状态的 Bundle
。 在 FlashCardPager 中,此 Bundle
用于保存关联抽认卡的问题和答案文本。 在 FlashCardFragment.cs 中,将以下 Bundle
键添加到 FlashCardFragment
类定义顶部:
private static string FLASH_CARD_QUESTION = "card_question";
private static string FLASH_CARD_ANSWER = "card_answer";
修改 newInstance
工厂方法,使其创建 Bundle
对象,并使用上述键在实例化后将传递的问题和答案文本存储在片段中:
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;
}
修改片段生命周期方法 OnCreateView
,以从传入捆绑包中检索此信息,并将问题文本加载到 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;
}
此处未使用 answer
变量,但在将事件处理程序代码添加到此文件时,稍后将使用该变量。
创建适配器
ViewPager
使用位于 ViewPager
和数据源之间的适配器控制器对象(请参阅 ViewPager Adapter 文章中的插图)。
若要访问此数据,ViewPager
需要提供派生自 PagerAdapter
的自定义适配器。 由于此示例使用片段,因此它使用 FragmentPagerAdapter
– FragmentPagerAdapter
派生自 PagerAdapter
。
FragmentPagerAdapter
将每个页面表示为一个永久保留在片段管理器中的 Fragment
,只要用户可以返回到页面。 当用户轻扫 ViewPager
的页面时,FragmentPagerAdapter
从数据源中提取信息,并使用它创建 Fragment
,以便 ViewPager
显示。
实现 FragmentPagerAdapter
时,必须替换以下内容:
Count – 只读属性,该属性返回可用的视图数(页面)。
GetItem – 返回要为指定页面显示的片段。
添加名为 FlashCardDeckAdapter.cs 的新文件,并将其内容替换为以下代码:
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();
}
}
}
此代码会存根基本 FragmentPagerAdapter
实现。 在以下部分中,每个方法都替换为工作代码。 构造函数的目的是将片段管理器传递给 FlashCardDeckAdapter
的基类构造函数。
实现适配器构造函数
当应用实例化 FlashCardDeckAdapter
时,它提供对片段管理器的引用和实例化的 FlashCardDeck
。
将以下成员变量添加到 FlashCardDeckAdapter.cs 中 FlashCardDeckAdapter
类的顶部:
public FlashCardDeck flashCardDeck;
将以下代码行添加到 FlashCardDeckAdapter
构造函数:
this.flashCardDeck = flashCards;
此代码行存储 FlashCardDeckAdapter
将使用的 FlashCardDeck
实例。
实现计数
Count
实现相对简单:它返回抽认卡排列中的抽认卡数。 将 Count
替换为以下代码:
public override int Count
{
get { return flashCardDeck.NumCards; }
}
FlashCardDeck
的 NumCards
属性返回数据集中的抽认卡数(片段数)。
实现 GetItem
GetItem
方法返回与给定位置关联的片段。 当 GetItem
在抽认卡排列中调用某个位置时,它将返回一个配置为在该位置显示抽认卡问题的 FlashCardFragment
。 将 GetItem
方法替换为以下代码:
public override Android.Support.V4.App.Fragment GetItem(int position)
{
return (Android.Support.V4.App.Fragment)
FlashCardFragment.newInstance (
flashCardDeck[position].Problem, flashCardDeck[position].Answer);
}
此代码执行以下操作:
查找指定位置的
FlashCardDeck
排列中的数学问题字符串。在
FlashCardDeck
排列中查找指定位置的答案字符串。调用
FlashCardFragment
工厂方法newInstance
,传入抽认卡问题和应答字符串。创建并返回一个新的抽认卡
Fragment
,其中包含该位置的问题和答案文本。
当 ViewPager
在 position
呈现 Fragment
时,它将显示包含抽认卡排列中 position
的数学问题字符串的 TextBox
。
将适配器添加到 ViewPager
实现 FlashCardDeckAdapter
后,即可将其添加到 ViewPager
。 在 MainActivity.cs 中,将以下代码行添加到 OnCreate
方法的末尾:
FlashCardDeckAdapter adapter =
new FlashCardDeckAdapter(SupportFragmentManager, flashCards);
viewPager.Adapter = adapter;
此代码实例化 FlashCardDeckAdapter
,传入第一个参数中的 SupportFragmentManager
。 (FragmentActivity 的 SupportFragmentManager
属性用于获取对 FragmentManager
的引用 – 如需了解有关 FragmentManager
的详细信息,请参阅管理片段。)
核心实现现已完成 – 生成并运行应用。 应会看到抽人卡排列的第一张图像显示在屏幕上,如下一屏幕截图左侧所示。 向左轻扫以查看更多抽认卡,然后向右轻扫以在抽认卡排列上移动:
添加寻呼指示器
这种最小的 ViewPager
实现显示排列中的每个抽认卡,但它没有说明用户位于排列内的位置。 下一步是添加 PagerTabStrip
。 PagerTabStrip
通过显示上一个和下一个抽认卡的提示,通知用户显示问题编号,并提供导航上下文。
打开 Resources/layout/Main.axml,并向布局添加 PagerTabStrip
:
<?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>
生成并运行应用时,应会看到每个抽认卡顶部显示的空 PagerTabStrip
:
显示标题
若要向每个页面选项卡添加标题,请在适配器中实现 GetPageTitleFormatted
方法。 ViewPager
调用 GetPageTitleFormatted
(如果已实现),以获取描述位于指定位置的页面的标题字符串。 将以下方法添加到 FlashCardDeckAdapter.cs 中的 FlashCardDeckAdapter
类:
public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String("Problem " + (position + 1));
}
此代码将抽认卡排列中的位置转换为问题编号。 生成的字符串将转换为返回到 ViewPager
的 Java String
。 使用此新方法运行应用时,每个页面都会在 PagerTabStrip
中显示问题编号:
可以来回轻扫,以查看每个抽认卡顶部显示的抽认卡排列片中的问题编号。
处理用户输入
FlashCardPager 在 ViewPager
中提供了一系列基于片段的抽认卡,但它还没有办法揭示每个问题的答案。 在本部分中,在用户点击抽认卡问题文本时,会将事件处理程序添加到 FlashCardFragment
以显示答案。
打开 FlashCardFragment.cs,并将以下代码添加到 OnCreateView
方法的末尾,然后再将视图返回到调用方:
questionBox.Click += delegate
{
Toast.MakeText(Activity.ApplicationContext,
"Answer: " + answer, ToastLength.Short).Show();
};
此 Click
事件处理程序在用户点击 TextBox
时显示的 Toast 中显示答案。 从传递给 OnCreateView
的捆绑包读取状态信息时,answer
变量已初始化。 生成并运行应用,然后点击每个抽认卡上的问题文本以查看答案:
本演练中显示的 FlashCardPager 使用派生自 FragmentActivity
的 MainActivity
,但也可以从 AppCompatActivity
派生 MainActivity
(这也为管理片段提供支持)。
总结
本演练提供了有关如何使用 Fragment
构建基于基本 ViewPager
的应用的分步示例。 它演示了一个示例数据源,其中包含抽认卡问题和答案、用于显示抽认卡的 ViewPager
布局,以及将 ViewPager
连接到数据源的 FragmentPagerAdapter
子类。 为了帮助用户浏览抽认卡,包含说明如何添加 PagerTabStrip
以在每个页面顶部显示问题编号。 最后,添加了事件处理代码,当用户点击抽认卡问题时显示答案。