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 以在每个页面顶部显示问题编号。 最后,添加了事件处理代码,当用户点击抽认卡问题时显示答案。




