次の方法で共有


ViewPager とビュー

ViewPager は、ジェスチャ ナビゲーションを実装できるレイアウト マネージャーです。 ジェスチャ ナビゲーションを使うと、ユーザーは左右にスワイプしてデータのページを順繰りに移動できます。 このガイドでは、データ ページとしてビューを使って、ViewPager と PagerTabStrip でスワイプ可能な UI を実装する方法について説明します (この後のガイドでは、ページにフラグメントを使う方法を説明します)。

概要

このガイドは、ViewPager を使って落葉樹と常緑樹の画像ギャラリーを実装する方法の手順を見ていくチュートリアルです。 このアプリでは、ユーザーは "樹木カタログ" を左右にスワイプして、樹木の画像を表示します。 カタログの各ページの先頭では、樹木の名前が PagerTabStrip に一覧表示され、樹木の画像が ImageView に表示されています。 ViewPager と基になるデータ モデルの間のインターフェイスには、アダプターが使われています。 このアプリでは、PagerAdapter から派生したアダプターを実装します。

ViewPager ベースのアプリは Fragment で実装されることがよくありますが、Fragment の余分な複雑さを不要としない、比較的シンプルなユース ケースがいくつかあります。 たとえば、このチュートリアルで示す基本的な画像ギャラリー アプリでは、Fragment を使う必要はありません。 コンテンツは静的であり、ユーザーは異なる画像間をスワイプして前後に移動するだけなので、Android の標準のビューとレイアウトを使うことで、実装をシンプルに保つことができます。

アプリ プロジェクトを開始する

TreePager という新しい Android プロジェクトを作成します (新しい Android プロジェクトの作成について詳しくは、Hello, Android に関する記事をご覧ください)。 次に、NuGet パッケージ マネージャーを起動します。 (NuGet パッケージのインストールについて詳しくは、プロジェクトに NuGet を含めるチュートリアルに関する記事をご覧ください)。 Android Support Library v4 を見つけてインストールします。

NuGet パッケージ マネージャーで選択されたサポート v4 NuGet のスクリーンショット

これにより、Android Support Library v4 で必要な追加パッケージもインストールされます。

データ ソースの例を追加する

この例では、樹木カタログ データ ソース (TreeCatalog クラスで表されます) によって ViewPager に項目の内容が提供されます。 TreeCatalog には、アダプターが View の作成に使用する樹木の画像と樹木のタイトルの既製のコレクションが含まれています。 TreeCatalog コンストラクターには引数は必要ありません。

TreeCatalog treeCatalog = new TreeCatalog();

TreeCatalog 内の画像のコレクションは、インデクサーで各画像にアクセスできるように編成されています。 たとえば、次のコード行は、コレクション内の 3 番目の画像の画像リソース 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 が定義されています。 ViewPager はサポート ライブラリにパッケージ化されているため、完全修飾名 android.support.v4.view.ViewPager を使う必要があることに注意してください。 ViewPagerAndroid 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 のコンテンツにアクセスするためのアダプターがないため、ViewPager は空です。 次のセクションでは、ViewPagerTreeCatalog に接続するための PagerAdapter を作成します。

アダプターを作成する

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.csTreePagerAdapter クラスの先頭に、次のメンバー変数とコンストラクターを追加します。

Context context;
TreeCatalog treeCatalog;

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

このコンストラクターの目的は、TreePagerAdapter が使うコンテキストと TreeCatalog インスタンスを格納することです。

Count を実装する

Count の実装は比較的簡単です。それは、樹木カタログ内の樹木の数を返します。 Count を次のコードに置き換えます。

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

TreeCatalogNumTrees プロパティは、データ セット内の樹木の数 (ページ数) を返します。

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. インスタンス化された ImageViewViewPager に追加し、呼び出し元に ImageView を返します。

ViewPager は、position に画像を表示するとき、この ImageView を表示します。 最初に、InstantiateItem が 2 回呼び出されて、最初の 2 ページにビューが設定されます。 ユーザーがスクロールすると、それが再び呼び出されて、現在表示されている項目のすぐ後ろと前のビューが維持されます。

DestroyItem を実装する

DestroyItem メソッドは、指定された位置からページを削除します。 任意の特定の位置にあるビューが変更される可能性があるアプリでは、ViewPager に、新しいビューに置き換える前に、その位置にある古いビューを削除する何らかの方法が必要です。 TreeCatalog の例では、各位置のビューは変化しないため、DestroyItem によって削除されたビューは、その位置に対して InstantiateItem が呼び出されると、単純にもう一度追加されます。 (効率を高めるため、プールを実装して、同じ位置に再表示される 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 を実装する

ユーザーがコンテンツのページを左右にスライドすると、ViewPagerIsViewFromObject を呼び出して、指定された位置にある子 View が、その同じ位置に対するアダプターのオブジェクトと関連付けられていることを確認します (そのため、アダプターのオブジェクトは "オブジェクト キー" と呼ばれます)。 比較的単純なアプリの場合、関連付けは ID の 1 つです。そのインスタンスでのアダプターのオブジェクト キーは、以前に InstantiateItem から ViewPager に返されたビューです。 一方、他のアプリの場合は、オブジェクト キーは、ViewPager がその位置に表示する子ビューに関連付けられている (ただし、子ビューと同じではない) 他のアダプター固有のクラス インスタンスである場合があります。 渡されたビューとオブジェクト キーが関連付けられているかどうかは、アダプターだけが知っています。

PagerAdapter が正常に機能するためには、IsViewFromObject を実装する必要があります。 指定された位置に対して IsViewFromObjectfalse を返す場合、ViewPager はその位置にビューを表示しません。 TreePager アプリでは、InstantiateItem によって返されるオブジェクト キーツリーのページ Viewであるため、コードで ID を確認するだけで済みます (つまり、オブジェクト キーとビューは 1 つであり、同じです)。 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);

このコードは、コンテキストとして MainActivity を渡して (this)、TreePagerAdapter をインスタンス化します。 インスタンス化された TreeCatalog は、コンストラクターの 2 番目の引数に渡されます。 ViewPagerAdapter プロパティには、インスタンス化された TreePagerAdapter オブジェクトが設定されます。これにより、TreePagerAdapterViewPager に接続されます。

これでコア実装は完了しました。アプリをビルドして実行します。 次のスクリーンショットの左側で示されているように、樹木カタログの最初の画像が画面に表示されるはずです。 左にスワイプして他の樹木のビューを表示してから、右にスワイプして樹木カタログ内を戻ります。

ツリー画像をスワイプする TreePager アプリのスクリーンショット

ページャー インジケーターを追加する

この最小限の 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>

ViewPagerPagerTabStrip は、連携して動作するように設計されています。 ViewPager レイアウト内で PagerTabStrip を宣言すると、ViewPagerPagerTabStrip を自動的に見つけてアダプターに接続します。 アプリをビルドして実行すると、各画面の上部に空の PagerTabStrip が表示されます。

空の PagerTabStrip のスクリーンショットのクローズアップ

タイトルを表示する

各ページ タブにタイトルを追加するには、PagerAdapter 派生クラスに GetPageTitleFormatted メソッドを実装します。 ViewPagerGetPageTitleFormatted (実装されている場合) を呼び出して、指定した位置にあるページを説明するタイトル文字列を取得します。 次のメソッドを、TreePagerAdapter.csTreePagerAdapter クラスに追加します。

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

このコードは、樹木カタログ内で指定されたページ (位置) から樹木のキャプション文字列を取得し、それを Java の String に変換して、ViewPager に返します。 この新しいメソッドを含むアプリを実行すると、各ページの PagerTabStrip に樹木のキャプションが表示されます。 画面の上部に、下線なしで樹木名が表示されるはずです。

テキストで塗りつぶされた PagerTabStrip タブを含むページのスクリーンショット

前後にスワイプして、カタログ内のキャプションが付いた各樹木の画像を表示できます。

PagerTitleStrip のバリエーション

PagerTitleStripPagerTabStrip とよく似ていますが、PagerTabStrip では現在選ばれているタブに下線が追加される点が異なります。上記のレイアウトで PagerTabStripPagerTitleStrip に置き換えて、アプリをもう一度実行し、PagerTitleStrip がどのように表示されるかを確認できます。

テキストから下線が削除された PagerTitleStrip

PagerTitleStrip に変えると、下線が表示されなくなることに注意してください。

まとめ

このチュートリアルでは、Fragment を使わない、基本的な ViewPager ベースのアプリを構築する方法の手順の例を示しました。 画像とキャプション文字列、画像を表示するための ViewPager レイアウト、ViewPager をデータ ソースに接続する PagerAdapter サブクラスを含む、データ ソースの例を示しました。 ユーザーがデータ セット内を移動しやすいよう、各ページの上部に画像のキャプションを表示する PagerTabStrip または PagerTitleStrip を追加する方法を説明する手順を含めました。