ViewPager とフラグメント
ViewPager は、ジェスチャ ナビゲーションを実装できるレイアウト マネージャーです。 ジェスチャ ナビゲーションを使うと、ユーザーは左右にスワイプしてデータのページを順繰りに移動できます。 このガイドでは、フラグメントをデータ ページとして使用し、ViewPager でスワイプ可能な UI を実装する方法について説明します。
概要
ViewPager
は、多くの場合、フラグメントと組み合わせて使用されるため、ViewPager
内の各ページのライフサイクルを管理しやすくなります。 このチュートリアルでは、ViewPager
を使用して、フラッシュ カードに一連の数学の問題を表示する FlashCardPager というアプリを作成します。 各フラッシュ カードはフラグメントとして実装されます。 ユーザーはフラッシュカードを左右にスワイプし、数学の問題をタップして解答を表示します。 このアプリは、フラッシュ カードごとに Fragment
インスタンスを作成し、FragmentPagerAdapter
から派生したアダプターを実装します。 Viewpager と Views では、ほとんどの作業は MainActivity
ライフサイクル メソッドで実行されました。 FlashCardPager では、作業のほとんどは、Fragment
によってライフサイクル メソッドの 1 つで実行されます。
このガイドではフラグメントの基本については説明しません。Xamarin.Android のフラグメントにまだ慣れていない場合は、フラグメントの使用を開始するために「フラグメント」を参照してください。
アプリ プロジェクトを開始する
FlashCardPager という新しい Android プロジェクトを作成します。 次に、NuGet パッケージ マネージャーを起動します (NuGet パッケージのインストールについて詳しくは、「チュートリアル: NuGet をプロジェクトに含める」を参照してください)。 Viewpager と Views の説明に従って、Xamarin.Android.Support.v4 パッケージを見つけてインストールします。
データ ソースの例を追加する
FlashCardPager において、データ ソースは FlashCardDeck
クラスで表されるフラッシュ カードのデッキです。このデータ ソースは ViewPager
に項目の内容を提供します。 FlashCardDeck
には、数学の問題と解答の既製のコレクションが含まれています。 FlashCardDeck
コンストラクターには引数は必要ありません。
FlashCardDeck flashCards = new FlashCardDeck();
FlashCardDeck
内のフラッシュ カードのコレクションは、インデクサーで各フラッシュ カードにアクセスできるように編成されています。 たとえば、次のコード行は、デッキ内の 4 番目のフラッシュ カード問題を取得します。
string problem = flashCardDeck[3].Problem;
このコード行は、前の問題に対する対応する解答を取得します。
string answer = flashCardDeck[3].Answer;
FlashCardDeck
の実装の詳細は ViewPager
の理解には関係ないため、FlashCardDeck
のコードはここでは示しません。
FlashCardDeck
のソース コードは FlashCardDeck.cs にあります。
このソース ファイルをダウンロードして (または、コードをコピーして新しい 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
が定義されています。 ViewPager
はサポート ライブラリにパッケージ化されているため、完全修飾名 android.support.v4.view.ViewPager を使う必要があることに注意してください。 ViewPager
は Android Support Library 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
のビューには、1 つのフラッシュ カードに含まれる情報が表示されます。 FlashCardFragment
の各インスタンスは ViewPager
によってホストされます。
FlashCardFragment
のビューは、フラッシュ カードの問題テキストを表示する TextView
で構成されます。 このビューは、ユーザーがフラッシュ カードの問題をタップしたときに Toast
を使用して解答を表示するイベント ハンドラーを実装します。
FlashCardFragment レイアウトを作成する
FlashCardFragment
を実装する前に、そのレイアウトを定義する必要があります。 このレイアウトは、単一のフラグメント用のフラグメント コンテナー レイアウトです。 Resources/layout に flashcard_layout.axml という新しい Android レイアウトを追加します。 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>
このレイアウトでは、単一のフラッシュ カード フラグメントを定義します。各フラグメントは、大きな (100sp) フォントを使用して数学の問題を表示する TextView
で構成されます。 このテキストはフラッシュ カード上で垂直方向と水平方向に中央揃えされます。
初期 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
バンドルには、保存された状態から TextView
を再作成するために OnCreateView
が使用するデータが含まれています。
フラグメントのビューは、inflater.Inflate
の呼び出しによって明示的にインフレートされます。 container
引数はビューの親であり、false
フラグはインフレーターに、インフレートしたビューをビューの親に追加しないように指示します (このチュートリアルの後半で ViewPager
がアダプターの GetItem
メソッドを呼び出すときに追加されます)。
FlashCardFragment に状態コードを追加する
アクティビティと同様に、フラグメントには状態を保存および取得するために使用する Bundle
があります。 FlashCardPager では、この Bundle
は、関連付けられたフラッシュ カードの問題と解答のテキストを保存するために使用されます。 FlashCardFragment.cs で、FlashCardFragment
クラス定義の先頭に次の Bundle
キーを追加します。
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
を変更して、渡された Bundle からこの情報を取得し、問題テキストを 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 の「アダプター」の記事の図を参照)。
ViewPager
でこのデータにアクセスするには、PagerAdapter
から派生したカスタム アダプターを指定する必要があります。 この例ではフラグメントを使用しているため、FragmentPagerAdapter
を使用します。FragmentPagerAdapter
は PagerAdapter
から派生しています。
FragmentPagerAdapter
は各ページを Fragment
として表し、ユーザーがそのページに戻れる限り、フラグメント マネージャーに永続的に保持されます。 ユーザーが ViewPager
のページ間をスワイプして移動すると、FragmentPagerAdapter
はデータ ソースから情報を抽出し、それを使用して ViewPager
が表示する Fragment
を作成します。
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
の実装は比較的単純です。フラッシュ カード デッキ内のフラッシュ カードの数を返します。 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;
このコードは、最初の引数に SupportFragmentManager
を渡して、FlashCardDeckAdapter
のインスタンスを作成します。 (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));
}
このコードは、フラッシュ カード デッキ内の位置を問題番号に変換します。 結果の文字列は Java String
に変換され、ViewPager
に返されます。 この新しいメソッドを含むアプリを実行すると、各ページの PagerTabStrip
に問題番号が表示されます。
前後にスワイプすると、各フラッシュ カードの上部に表示されているフラッシュ カード デッキの問題番号を確認できます。
ユーザーによる入力を処理する
FlashCardPager は ViewPager
でフラグメントベースの一連のフラッシュ カードを表示しますが、まだ各問題の解答を表示する方法がありません。 このセクションでは、ユーザーがフラッシュ カードの問題テキストをタップしたときに解答を表示するためのイベント ハンドラーを FlashCardFragment
に追加します。
FlashCardFragment.cs を開き、ビューが呼び出し元に返される直前の OnCreateView
メソッドの末尾に次のコードを追加します。
questionBox.Click += delegate
{
Toast.MakeText(Activity.ApplicationContext,
"Answer: " + answer, ToastLength.Short).Show();
};
この Click
イベント ハンドラーは、ユーザーが TextBox
をタップしたときに表示されるトーストに解答を表示します。 answer
変数は、OnCreateView
に渡された Bundle から状態情報が読み取られたときに初期化されています。 アプリをビルドして実行し、各フラッシュ カードの問題テキストをタップして解答を確認します。
このチュートリアルで紹介する FlashCardPager は、FragmentActivity
から派生した MainActivity
を使用しますが、AppCompatActivity
(これもフラグメントの管理をサポートしています) から MainActivity
を派生させることもできます。
まとめ
このチュートリアルでは、Fragment
を使用した、基本的な ViewPager
ベースのアプリを構築する方法の手順の例を示しました。 フラッシュ カードの問題と解答を含むサンプル データ ソース、フラッシュ カードを表示するための ViewPager
レイアウト、および ViewPager
をデータ ソースに接続する FragmentPagerAdapter
サブクラスを紹介しました。 ユーザーがフラッシュ カード内を移動できるように、各ページの上部に問題番号を表示する PagerTabStrip
を追加する方法を説明する手順を含めました。 最後に、ユーザーがフラッシュ カードの問題をタップしたときに解答を表示するイベント処理コードを追加しました。