[译]:Xamarin.Android用户界面——ViewPager与View组合使用

标签: Xamarin.Android, 官方教程, 中文翻译

博客分类: 官方教程

返回索引目录
原文链接:Part 1 - ViewPager with Views
译文链接:Xamarin.Android用户界面——ViewPager与View组合使用

Part 1 - ViewPager with Views

ViewPager是一个布局管理器,以此可以实现手势导航。手势导航可以允许用户左右滑动来逐步浏览数据页面。本指南将介绍如何使用ViewPager和PagerTabStrip来实现可滑动的UI —— 使用View作为数据页,后续 指南会介绍如何使用Fragment作为页面。

概览

本指南将一步步演示如何使用ViewPager实现(内容为落叶和常绿树)图库。在此应用中,用户可以通过左右滑动(树分类)来查看树图像。在分类的每个页面顶部,树的名称会列在PagerTabStrip中,同时,一张树的图片会显示在ImagerView中。适配器是用于将ViewPager连接到底层数据模型的。本应用中会实现一个从PagerAdapter派生出来的适配器。

尽管基于ViewPager的应用通常是利用Fragment实现,但是一些比较简单的案例是不需要使用较为复杂的Fragment。例如,本文示例-基本的图片库应用是不需要使用Fragment。因为其内容是静态的,并且用户只在不同图片间来回滑动,所以通过使用标准的Android视图和布局可以让实现比较简单。

创建应用项目

创建一个名为TreePager的Android项目。关于如何创建Android项目的更多信息,参阅:原文:Hello, Android译文:Xamarin.Android开发入门——Hello,Android快速上手。然后,启动NuGet包管理器。关于安装NuGet软件包的更多信息,参阅:原文:Walkthrough: Including a NuGet in your project。最后,查找并安装Android Support Library v4:

在此,Android Support Library v4相关的额外的(依赖)包也会同时安装。

添加示例数据源

在本示例中,树目录数据源(通过TreeCatalog类)向ViewPager提供项目内容。TreeCatalog类包含了一个现成的树图片和树标题的集合,以此适配器可以利用它们创建视图。TreeCatalog的构造函数不需要参数:

TreeCatalog treeCatalog = new TreeCatalog();

TreeCatalog中的图像集合是组织好的,以便于可以通过索引器访问每一个图像。例如,以下代码行通过图像资源ID来查找集合中的第三章图像:

int imageId = treeCatalog[2].imageId;

由于TreeCatalog的实现细节与理解ViewPager无关,故在此将不列出TreeCatalog代码。具体的TreeCatalog代码见:TreeCatalog.cs。下载此源文件,然后将其添加到你的项目中(或将代码复制并粘贴到一个新的TreeCatalog.cs文件中)。另外,下载并将图片文件解压到你的Resources/drawable文件夹,然后将它们包含进项目 中。图片文件下载地址:image files

创建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 Support Library 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为空,因为它缺少一个用于访问TreeCatalog内容的适配器。在下一节中,将创建一个PagerAdapter,以便于将ViewPager与TreeCatalog连接起来。

创建适配器

ViewPager使用位于ViewPager和数据源之间的适配器控制对象(参阅例图:原文:Adapter译文:适配器)。为了访问此数据,ViewPager需要你提供一个派生自PagerAdapter的自定义适配器。此适配器使用来自数据源的内容来填充每个ViewPager页面。由于数据源是属于特定应用的,故此自定义适配器是解析如何访问数据的代码。当用户滑动ViewPager中的页面时,适配器会从数据源中提取信息,并将信息加载到页面中,以供于ViewPager显示。

当你实现一个PageAdapter时,你必须覆盖以下内容:

  • InstantiateItem —— 在给定位置创建页面(视图),并将其添加到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类(TreePagerAdapter.cs文件)的顶部:

Context context;
TreeCatalog treeCatalog;

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

这个构造函数的目的是存储上下文和TreeCatalog实例(TreePagerAdapter将要使用的实例)。

实现Count属性

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

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

TreeCatalog的NumTrees属性可以返回数据集中的树的数量(页面的数量)。

实现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()才能正确 执行转换(这是必需的,以便于Android可以执行运行时类型转换检查)。
  4. 将实例化的ImageView添加到ViewPager,并将ImageView返回 给调用者。

当ViewPager在特定位置显示图像时,它将显示此ImageView。最初,InstantiateItem会被调用两次,并以此填充前两个页面的视图。当用户滚动时,它会再次被调用,以此维护紧邻当前视图项之前和之后的视图。

实现DestroyItem方法

DestroyItem方法会从给定位置删除页面。在应用中,任何给定位置的视图都可以更改,ViewPager必须给定位置替换新视图前,需要用某些方式来移除该位置的过期视图。在TreeCatalog示例中,每个位置上的视图都未改变,因此,由DestoryItem移除的视图都将在该位置调用InstantiateItem时,重新添加。(为了更高的效率,可以实现一个池来回收那些将被重新显示在相同位置的视图)。

用以下代码替换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对象(视图)转化为C#的View对象(view as View)。
  3. 从ViewPager中删除视图。

实现IsViewFromObject方法

当用户在内容页面上左右滑动时,ViewPager调用IsViewFromObject方法来验证给定位置的子视图是否与同一位置的适配器对象相关联(后面,我们将适配器对象称为 对象键 )。对于相对简单的应用,关联关系是其中一个身份标识 —— 适配器的对象键在该实例中是由之前的InstantiateItem方法返回给ViewPager的视图。然而对于其他应用,对象键可能是一些其他特定的适配器类的实例,它与ViewPager显示在该位置的子视图相关联(却不相同)。只有适配器知道传入的视图是否与对象键有关。

IsViewFromObject必须实现,以此保证PagerAdapter正常工作。如果IsViewFromObject为给定位置返回false,则ViewPager将不会在该位置显示视图。在TreePager应用中,由InstantiateItem返回的对象键是树的页面视图,因此代码只需检查标识(如,对象键与视图是否是同一个)。使用以下代码替换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中当前页面的指示器;它会随着用户在个页面间滑动时滚动并更新。

打开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/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会调用GetPagerTitleFormatted(如果实现了)来获取描述指定位置页面的标题字符串。将下面的方法添加到TreePagerAdapter.cs文件的TreePagerAdapter类中:

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

上述代码会从树目录中的指定页面(位置)获取树的标题字符串,然后将其转换为Java字符串,最后将它返回给ViewPager。当你使用此新方法运行应用时,每个页面都会在PagerTabStrip中显示树标题。你应该会在屏幕顶部看到带有下划线的树名称:

原文描述说没有下划线,PagerTabStrip是带有下划线的,此处原文有误。

你可以来回滑动以查看目录中每一个带有标题的树图像。

PagerTitleStrip变化

PagerTitleStrip与PagerTabStrip非常相似,除了PagerTabStrip为当前选项卡添加下划线。你可以在上面的布局文件中用PagerTitleStrip替换PagerTabStrip,然后重新运行应用,以此查看PagerTitleStrip的显示效果:

注意,当转为使用PagerTitleStrip时,下划线将会被删除。

总结

本文提供了一个逐步操作示例,说明了如何在不使用Fragment的情况下构建基础的ViewPager应用。它提供了一个包含图像和标题字符串的示例数据源,一个显示图像的ViewPager布局,以及一个连接ViewPager和数据源的PagerAdapter子类。为了帮助用户浏览数据集,本操作指南说明了怎样添加一个PagerTabStrip或PagerTitleStrip来在每一个页面顶部显示图像标题。


译:奇葩史

没有评论