C#Xamarin.Formsでスマホ開発#14 ListViewの作り方

当サイトではアフィリエイト広告を利用しています。

Xamarin.Formsでスマホアプリ開発

ListViewとは?

ListViewとは,繰り返し項目をリストにして表示してくれるコントロールで,顧客リストデータ等を画面に表示するときに使うことができます。自由にレイアウトをすることができるので,イメージをおいて,その横にラベルを2つ並べたり,文字の色を変更したりすることができます。データの一覧の中から一つ選択させて,別のページに移動させたり,単純にデータを閲覧したりするときなどに使用できます。今回はMenuPageをListViewにして,そこにイメージとタイトル,サブタイトルを表示するListViewを作成していきます。

ListViewの書き方

前回にMenuPageに書いたLabelなどは不要なのでStackLayoutごと削除し,次のようにListViewを記述します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.MenuPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Image Source="{Binding ImageName}"/>
                            <StackLayout>
                                <Label Text="{Binding Title}" TextColor="Blue"/>
                                <Label Text="{Binding SubTitle}"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>
  • ListViewを定義する。名前はMyListViewとしている。
  • 次にItemTemplateを定義する。
  • DataTemplateを定義する。
  • ViewCellを定義し,その中に任意のレイアウトを定義する
  • 任意のレイアウトはまずStackLayoutを横向きで指定し,Imageを置く。
  • ImageのSourceには画像ファイルを指定する必要がありますが,一覧表で表示するため,ここではデータバインドする変数名を記述する必要があります。ListViewにはまだ何もバインドしていませんが,このあとMenuDtoというクラスを作り,その中のImageNameという変数名をバインドする予定なので,ImageのSourceにはImageNameをバインドします。
  • イメージの横には縦向きにラベルを2つ置いて,タイトルとサブタイトルにしたいので,さらにStackLayoutを置いて,そこにLabelを2つ置きます。
  • 1つ目のLabelにはTitleをバインドし,文字の色を青にしています。
  • 2つ目のLabelには,SubTitleをバインドしています。

以上でMenuPage.xaml側の記述となります。

バインドするクラスの作成

続いて,データバインドするクラスを作成します。画像のファイルパスと,タイトル,サブタイトルを表現する必要があるので,その3つを保持するためのクラスを作成します。新規でクラスを作成し,名前はMenuPageにバインドするクラスということで,MenuDtoとしておきます。

namespace XSample.Objects
{
    public sealed class MenuDto
    {
        public MenuDto(string title, string subTitle, string imageName)
        {
            Title = title;
            SubTitle = subTitle;
            ImageName = imageName;
        }

        public string Title { get; set; }
        public string SubTitle { get; set; }
        public string ImageName { get; set; }
    }
}

こんな感じにクラスを作成します。コンストラクタでは必ず3つの値が指定されるようにしています。

データの作成

それではMenuPage.xamlのコードビハインド側に行き,MenuDtoのリストを作成して,ListViewにバインドしましょう。書き方は次のような感じになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XSample.Objects;

namespace XSample.Pages
{
	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class MenuPage : ContentPage
	{
		public MenuPage ()
		{
			InitializeComponent ();

            var items = new List();
            items.Add(new MenuDto(
                "AAA", 
                "aaaaaaaaaaaa",
                "mail.png"));
            items.Add(new MenuDto(
                "BBB", 
                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccce!", 
                "mail.png"));
            MyListView.ItemsSource = items; 
        }
    }
}
  • まず,MenuDtoのListを生成します。
  • その中に任意のタイトル,サブタイトル,ファイルパスを設定したMenuDtoを追加します。
  • 1つめはAAAというタイトルのMenuDtoです。
  • 2つめはBBBというタイトルのMenuDtoです。
  • pngというファイルを指定していますが,まだファイルが存在しないため設置する必要があります。

Androidのファイル置き場

Androidの為の画像はAndroidプロジェクトの「Resources」フォルダーの中の,「drawable」フォルダーの中に入れます。

UWPのファイル置き場

UWPのファイルはそのままUWPのプロジェクトの直下に置きます。これで細かいパスを指定しなくても画像が表示されるようになります。

実行

それでは実行してみましょう。

いい感じでListViewが表示されます。ただ,AndroidのBBBのサブタイトルがすべて表示されていませんね。これをちゃんと表示されるように調整します。

行の幅を自動で調整する

ListViewの設定にHasUnevenRows = “True”を追加します。これで必要なだけ行の幅が調整されます。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.MenuPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView"
                  HasUnevenRows = "True">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Image Source="{Binding ImageName}"/>
                            <StackLayout>
                                <Label Text="{Binding Title}" TextColor="Blue"/>
                                <Label Text="{Binding SubTitle}"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

AndroidのBBBのサブタイトルが,すべて表示されているのが確認できます。

行のタップイベントを追加

次に行をタップしたときのイベントを入れましょう。実際の業務では,行をタップしたら,その行にあったページに遷移したり,メッセージを表示したりするはずです。ここではタップされた行に対するメッセージを表示してみます。タップのイベントはItemTappedなので次のようにListViewに定義を追加します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.MenuPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView"
                  HasUnevenRows = "True"
                  ItemTapped="MyListView_ItemTapped">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Image Source="{Binding ImageName}"/>
                            <StackLayout>
                                <Label Text="{Binding Title}" TextColor="Blue"/>
                                <Label Text="{Binding SubTitle}"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

コードビハインド側のItemTappedイベントに次のように記述します。

private void MyListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
    var item = e.Item as MenuDto;
    DisplayAlert(item.Title, item.SubTitle, "OK");
}
  • 第2引数「e」のItemにタップされた行の情報が通知されますが,型がobjectになっているため,バインドしているMenuDto型に変換します。
  • 変換後のMenuDtoのタイトルとサブタイトルをメッセージとして表示します。

実行

実行して,行をタップすると,それに従ったメッセージが表示されることが確認できます。

サンプルコード全体

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XSample.Pages.MenuPage">
    <ContentPage.Content>

        <ListView x:Name="MyListView"
                  HasUnevenRows = "True"
                  ItemTapped="MyListView_ItemTapped">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Image Source="{Binding ImageName}"/>
                            <StackLayout>
                                <Label Text="{Binding Title}" TextColor="Blue"/>
                                <Label Text="{Binding SubTitle}"/>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XSample.Objects;

namespace XSample.Pages
{
	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class MenuPage : ContentPage
	{
		public MenuPage ()
		{
			InitializeComponent ();

            var items = new List();
            items.Add(new MenuDto(
                "AAA", 
                "aaaaaaaaaaaa",
                "mail.png"));
            items.Add(new MenuDto(
                "BBB", 
                "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccce!", 
                "mail.png"));
            MyListView.ItemsSource = items; 
        }

        private void Button_Clicked(object sender, EventArgs e)
        {
            Navigation.PushAsync(new NextPage());
        }

        private void MyListView_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            var item = e.Item as MenuDto;
            DisplayAlert(item.Title, item.SubTitle, "OK");
        }
    }
}
namespace XSample.Objects
{
    public sealed class MenuDto
    {
        public MenuDto(string title, string subTitle, string imageName)
        {
            Title = title;
            SubTitle = subTitle;
            ImageName = imageName;
        }

        public string Title { get; set; }
        public string SubTitle { get; set; }
        public string ImageName { get; set; }
    }
}