带视图的 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:
这还将安装 Android 支持库 v4 重新获取的任何其他包。
添加示例数据源
在此示例中,由 TreeCatalog
类表示的树目录数据源 () 提供 ViewPager
项内容。
TreeCatalog
包含适配器将用于创建 View
的树图像和树标题的现成集合。 构造 TreeCatalog
函数不需要参数:
TreeCatalog treeCatalog = new TreeCatalog();
中的 TreeCatalog
图像集合经过组织,以便索引器可以访问每个图像。 例如,以下代码行检索集合中第三个图像的图像资源 ID:
int imageId = treeCatalog[2].imageId;
由于 的实现详细信息 TreeCatalog
与理解 ViewPager
无关, TreeCatalog
因此此处未列出代码。
的源代码 TreeCatalog
在 TreeCatalog.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();
}
此代码将执行以下操作:
设置 Main.axml 布局资源的视图。
从布局中检索对
ViewPager
的引用。将新的
TreeCatalog
实例化为数据源。
生成并运行此代码时,应会看到类似于以下屏幕截图的显示:
此时, 为空, ViewPager
因为它缺少用于访问 TreeCatalog 中内容的适配器。 在下一部分中,将创建 PagerAdapter 以将 连接到 ViewPager
TreeCatalog。
创建适配器
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; }
}
NumTrees
的 TreeCatalog
属性返回数据集中) 的树数 (页数。
实现 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;
}
此代码将执行以下操作:
实例化新的
ImageView
以在指定位置显示树图像。 应用的MainActivity
是将传递给构造函数的ImageView
上下文。将
ImageView
资源设置为TreeCatalog
位于指定位置的图像资源 ID。将传递的容器
View
强制转换为ViewPager
引用。 请注意,必须使用JavaCast<ViewPager>()
正确执行此强制转换, (需要此转换,以便 Android) 执行运行时检查的类型转换。将实例化的
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);
}
此代码将执行以下操作:
将传递的容器
View
强制转换为ViewPager
引用。将传递的 Java 对象 (
view
) 强制转换为 C#View
(view as View
) ;从
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
实现 后,可以将其添加到 。ViewPager
在 MainActivity.cs 中,将以下代码行添加到 方法的 OnCreate
末尾:
viewPager.Adapter = new TreePagerAdapter(this, treeCatalog);
此代码实例化 , TreePagerAdapter
传入 MainActivity
作为上下文 (this
) 。 实例化 TreeCatalog
的 传递到构造函数的第二个参数中。 ViewPager
的 Adapter
属性设置为实例化TreePagerAdapter
对象;这会将 插入TreePagerAdapter
到 中ViewPager
。
核心实现现已完成 - 生成并运行应用。 应会看到树目录的第一个图像显示在屏幕上,如下一个屏幕截图中的左侧所示。 向左轻扫可查看更多树视图,然后向右轻扫以在树目录上移回:
添加寻呼指示器
此最小 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>
ViewPager
和 PagerTabStrip
旨在协同工作。 在布局内ViewPager
声明 PagerTabStrip
时,ViewPager
将自动找到 PagerTabStrip
并将其连接到适配器。 生成并运行应用时,应会看到每个屏幕顶部显示的空 PagerTabStrip
:
显示标题
若要将标题添加到每个页面选项卡,请在 派生类中PagerAdapter
实现 GetPageTitleFormatted
方法。 ViewPager
如果) 实现,则调用 GetPageTitleFormatted
(以获取描述指定位置的页面的标题字符串。 将以下方法添加到 TreePagerAdapter
TreePagerAdapter.cs 中的 类:
public override Java.Lang.ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String(treeCatalog[position].caption);
}
此代码从指定页面检索树描述文字字符串, (树目录中的位置) ,将其转换为 JavaString
,并将其返回到 ViewPager
。 使用此新方法运行应用时,每个页面都会在 中PagerTabStrip
显示树描述文字。 你应该会在屏幕顶部看到树名称,没有下划线:
可以来回轻扫以查看目录中每个标题树图像。
PagerTitleStrip 变体
PagerTitleStrip
与 非常相似 PagerTabStrip
,只不过为 PagerTabStrip
当前所选选项卡添加下划线。可以在上述布局中将 替换为 PagerTabStrip
PagerTitleStrip
,然后再次运行应用以查看其外观 PagerTitleStrip
:
请注意,转换为 PagerTitleStrip
时会删除下划线。
总结
本演练提供了一个分步示例,说明如何在不使用 的情况下Fragment
生成基于基本ViewPager
的应用。 它提供了一个示例数据源,其中包含图像和描述文字字符串、ViewPager
用于显示图像的布局,以及一个PagerAdapter
将 连接到ViewPager
数据源的子类。 为了帮助用户浏览数据集,我们提供了说明如何添加 PagerTabStrip
或 PagerTitleStrip
以在每个页面顶部显示图像描述文字。