带视图的 ViewPager

ViewPager 是一个布局管理器,可用于实现手势导航。 Gestural 导航允许用户向左和向右轻扫以单步执行数据页。 本指南介绍如何使用 ViewPager 和 PagerTabStrip 实现可轻扫 UI,使用视图作为数据页 (后续指南介绍如何将片段用于页面) 。

概述

本指南是一个演练,分步演示了如何使用 ViewPager 实现落叶树和常绿树的图像库。 在此应用中,用户通过“树目录”向左和向右轻扫以查看树图像。 在目录的每页顶部,树的名称列在 中PagerTabStrip,树的图像显示在 中 ImageView。 适配器用于将 接口 ViewPager 连接到基础数据模型。 此应用实现派生自 PagerAdapter的适配器。

虽然 ViewPager基于 的应用通常使用 Fragment实现,但在某些相对简单的用例中,不需要 额外的复杂性 Fragment。 例如,本演练中演示的基本图像库应用不需要使用 Fragment。 由于内容是静态的,并且用户仅在不同图像之间来回轻扫,因此使用标准 Android 视图和布局可以简化实现。

启动应用项目

创建名为 TreePager 的新 Android 项目 (有关) 创建新 Android 项目的详细信息 ,请参阅 Hello, Android 。 接下来,启动 NuGet 包管理器。 (有关安装 NuGet 包的详细信息,请参阅 演练:在项目中包括 NuGet) 。 查找并安装 Android 支持库 v4

在 NuGet 包管理器中选择的支持 v4 NuGet 的屏幕截图

这还将安装 Android 支持库 v4 重新获取的任何其他包。

添加示例数据源

在此示例中,由 TreeCatalog 类表示的树目录数据源 () 提供 ViewPager 项内容。 TreeCatalog 包含适配器将用于创建 View的树图像和树标题的现成集合。 构造 TreeCatalog 函数不需要参数:

TreeCatalog treeCatalog = new TreeCatalog();

中的 TreeCatalog 图像集合经过组织,以便索引器可以访问每个图像。 例如,以下代码行检索集合中第三个图像的图像资源 ID:

int imageId = treeCatalog[2].imageId;

由于 的实现详细信息 TreeCatalog 与理解 ViewPager无关, TreeCatalog 因此此处未列出代码。 的源代码 TreeCatalogTreeCatalog.cs 中提供。 (下载此源文件,或将代码复制并粘贴到新的 TreeCatalog.cs 文件) 并将其添加到项目中。 此外,将 图像文件 下载并解压缩到 Resources/drawable 文件夹中,并将其包含在项目中。

创建 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;

OnCreate 方法替换为以下代码:

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

此代码将执行以下操作:

  1. 设置 Main.axml 布局资源的视图。

  2. 从布局中检索对 ViewPager 的引用。

  3. 将新的 TreeCatalog 实例化为数据源。

生成并运行此代码时,应会看到类似于以下屏幕截图的显示:

显示空 ViewPager 的应用屏幕截图

此时, 为空, ViewPager 因为它缺少用于访问 TreeCatalog 中内容的适配器。 在下一部分中,将创建 PagerAdapter 以将 连接到 ViewPagerTreeCatalog

创建适配器

ViewPager 使用位于 和 数据源之间的 ViewPager 适配器控制器对象 (请参阅 适配器) 中的插图。 为了访问此数据, ViewPager 需要提供派生自 PagerAdapter的自定义适配器。 此适配器使用数据源中的内容填充每个 ViewPager 页面。 由于此数据源特定于应用,因此自定义适配器是了解如何访问数据的代码。 当用户在 的页面 ViewPager中轻扫时,适配器会从数据源中提取信息,并将其加载到页面以供 ViewPager 显示。

实现 时, PagerAdapter必须重写以下内容:

  • InstantiateItem – 为给定位置创建页面 (View) ,并将其添加到 ViewPager视图集合。

  • DestroyItem – 从给定位置删除页面。

  • Count - 只读属性,返回可用页面 (视图数) 。

  • IsViewFromObject – 确定页面是否与特定键对象相关联。 (此对象由 InstantiateItem 方法创建。) 在此示例中,键对象是 TreeCatalog 数据对象。

添加名为 TreePagerAdapter.cs 的新文件,并将其内容替换为以下代码:

using System;
using Android.App;
using Android.Runtime;
using Android.Content;
using Android.Views;
using Android.Widget;
using Android.Support.V4.View;
using Java.Lang;

namespace TreePager
{
    class TreePagerAdapter : PagerAdapter
    {
        public override int Count
        {
            get { throw new NotImplementedException(); }
        }

        public override bool IsViewFromObject(View view, Java.Lang.Object obj)
        {
            throw new NotImplementedException();
        }

        public override Java.Lang.Object InstantiateItem (View container, int position)
        {
            throw new NotImplementedException();
        }

        public override void DestroyItem(View container, int position, Java.Lang.Object view)
        {
            throw new NotImplementedException();
        }
    }
}

此代码将基本实现存根 PagerAdapter 。 在以下部分中,这些方法中的每一个都替换为工作代码。

实现构造函数

当应用实例化 时, TreePagerAdapter它提供 (MainActivity) 的上下文和实例化的 TreeCatalog。 将以下成员变量和构造函数添加到 TreePagerAdapter.cs 中类的TreePagerAdapter顶部:

Context context;
TreeCatalog treeCatalog;

public TreePagerAdapter (Context context, TreeCatalog treeCatalog)
{
    this.context = context;
    this.treeCatalog = treeCatalog;
}

此构造函数的用途是存储 将要使用的上下文和 TreeCatalog 实例 TreePagerAdapter

实现计数

实现 Count 相对简单:它返回树目录中的树数。 将 Count 替换为以下代码:

public override int Count
{
    get { return treeCatalog.NumTrees; }
}

NumTreesTreeCatalog 属性返回数据集中) 的树数 (页数。

实现 InstantiateItem

方法 InstantiateItem 为给定位置创建页面。 它还必须将新创建的视图添加到 ViewPager的视图集合。 为此,将 ViewPager 自身作为容器参数传递。

InstantiateItem 方法替换为以下代码:

public override Java.Lang.Object InstantiateItem (View container, int position)
{
    var imageView = new ImageView (context);
    imageView.SetImageResource (treeCatalog[position].imageId);
    var viewPager = container.JavaCast<ViewPager>();
    viewPager.AddView (imageView);
    return imageView;
}

此代码将执行以下操作:

  1. 实例化新的 ImageView 以在指定位置显示树图像。 应用的 MainActivity 是将传递给构造函数的 ImageView 上下文。

  2. ImageView 资源设置为 TreeCatalog 位于指定位置的图像资源 ID。

  3. 将传递的容器 View 强制转换为 ViewPager 引用。 请注意,必须使用 JavaCast<ViewPager>() 正确执行此强制转换, (需要此转换,以便 Android) 执行运行时检查的类型转换。

  4. 将实例化的 ImageView 添加到 , ViewPager 并将 返回到 ImageView 调用方。

ViewPager当 在 position处显示图像时,它会显示此 ImageView。 最初, InstantiateItem 调用 两次,以使用视图填充前两个页面。 当用户滚动时,会再次调用它,以在当前显示项的后面和前面维护视图。

实现 DestroyItem

方法 DestroyItem 从给定位置删除页面。 在任何给定位置的视图可能会更改的应用中, ViewPager 必须先以某种方式删除该位置的过时视图,然后再将其替换为新视图。 在此TreeCatalog示例中,每个位置的视图不会更改,因此在为该位置调用 时InstantiateItem,只需重新添加由 DestroyItem 删除的视图。 (为了提高效率,可以实现池来回收 View将重新显示在同一位置的 。)

DestroyItem 方法替换为以下代码:

public override void DestroyItem(View container, int position, Java.Lang.Object view)
{
    var viewPager = container.JavaCast<ViewPager>();
    viewPager.RemoveView(view as View);
}

此代码将执行以下操作:

  1. 将传递的容器 View 强制转换为 ViewPager 引用。

  2. 将传递的 Java 对象 (view) 强制转换为 C# View (view as View) ;

  3. ViewPager中删除视图。

实现 IsViewFromObject

当用户在内容页中向左和向右滑动时, ViewPager 调用 IsViewFromObject 以验证位于给定位置的子 View 对象是否与适配器的对象关联 (因此,适配器的对象称为对象 ) 。 对于相对简单的应用,关联是标识之一 - 该实例上的适配器对象键是以前通过 InstantiateItem返回到 ViewPager 的视图。 但是,对于其他应用,对象键可能是与 (关联的其他适配器特定的类实例,但与在该位置显示的子视图 ViewPager) 不同。 只有适配器知道传递的视图和对象键是否关联。

IsViewFromObject 必须实现 才能 PagerAdapter 正常运行。 如果 IsViewFromObject 返回 false 给定位置, ViewPager 则不会在该位置显示视图。 在TreePager应用中,返回InstantiateItem的对象键树的页面View,因此代码只需检查标识 (即,对象键和视图是一个相同的) 。 将 IsViewFromObject 替换为以下代码:

public override bool IsViewFromObject(View view, Java.Lang.Object obj)
{
    return view == obj;
}

将适配器添加到 ViewPager

TreePagerAdapter实现 后,可以将其添加到 。ViewPagerMainActivity.cs 中,将以下代码行添加到 方法的 OnCreate 末尾:

viewPager.Adapter = new TreePagerAdapter(this, treeCatalog);

此代码实例化 , TreePagerAdapter传入 MainActivity 作为上下文 (this) 。 实例化 TreeCatalog 的 传递到构造函数的第二个参数中。 ViewPagerAdapter 属性设置为实例化TreePagerAdapter对象;这会将 插入TreePagerAdapter到 中ViewPager

核心实现现已完成 - 生成并运行应用。 应会看到树目录的第一个图像显示在屏幕上,如下一个屏幕截图中的左侧所示。 向左轻扫可查看更多树视图,然后向右轻扫以在树目录上移回:

TreePager 应用在树图像中轻扫的屏幕截图

添加寻呼指示器

此最小 ViewPager 实现显示树目录的图像,但它不提供用户位于目录中的位置的指示。 下一步是添加 PagerTabStrip。 通知 PagerTabStrip 用户显示哪个页面,并通过显示上一页和下一页的提示来提供导航上下文。 PagerTabStrip 旨在用作当前页的 ViewPager指示器;当用户轻扫每一页时,它会滚动和更新。

打开 “资源/布局/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/viewpager"
    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>

ViewPagerPagerTabStrip 旨在协同工作。 在布局内ViewPager声明 PagerTabStrip 时,ViewPager将自动找到 PagerTabStrip 并将其连接到适配器。 生成并运行应用时,应会看到每个屏幕顶部显示的空 PagerTabStrip

空 PagerTabStrip 的特写屏幕截图

显示标题

若要将标题添加到每个页面选项卡,请在 派生类中PagerAdapter实现 GetPageTitleFormatted 方法。 ViewPager 如果) 实现,则调用 GetPageTitleFormatted (以获取描述指定位置的页面的标题字符串。 将以下方法添加到 TreePagerAdapterTreePagerAdapter.cs 中的 类:

public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
{
    return new Java.Lang.String(treeCatalog[position].caption);
}

此代码从指定页面检索树描述文字字符串, (树目录中的位置) ,将其转换为 JavaString,并将其返回到 ViewPager。 使用此新方法运行应用时,每个页面都会在 中PagerTabStrip显示树描述文字。 你应该会在屏幕顶部看到树名称,没有下划线:

包含文本填充 PagerTabStrip 选项卡的页面的屏幕截图

可以来回轻扫以查看目录中每个标题树图像。

PagerTitleStrip 变体

PagerTitleStrip 与 非常相似 PagerTabStrip ,只不过为 PagerTabStrip 当前所选选项卡添加下划线。可以在上述布局中将 替换为 PagerTabStripPagerTitleStrip ,然后再次运行应用以查看其外观 PagerTitleStrip

从文本中删除下划线的 PagerTitleStrip

请注意,转换为 PagerTitleStrip时会删除下划线。

总结

本演练提供了一个分步示例,说明如何在不使用 的情况下Fragment生成基于基本ViewPager的应用。 它提供了一个示例数据源,其中包含图像和描述文字字符串、ViewPager用于显示图像的布局,以及一个PagerAdapter将 连接到ViewPager数据源的子类。 为了帮助用户浏览数据集,我们提供了说明如何添加 PagerTabStripPagerTitleStrip 以在每个页面顶部显示图像描述文字。