読者です 読者をやめる 読者になる 読者になる

Xamarin 日本語情報

Xamarin(ザマリン) の代理店だったエクセルソフト田淵のブログです。主に Xamarin に関するエントリーをアップしていきます。(なるべく正しい有益な情報を掲載していきたいと考えていますが、このブログのエントリーは所属組織の公式見解ではありませんのでご注意ください)

Xamarin.Forms で ListView を最後まで表示するとクルクルを表示してその間に処理をするには~その2~

こんにちは。エクセルソフトの田淵です。

Xamarin.Forms で ListView を最後まで表示するとクルクルを表示してその間に処理をするには~その1~ - Xamarin 日本語情報 の続きのエントリーです。

上記では ListView の下にクルクルを表示しましたが、今度は良く見るポップアップでのクルクルにチャレンジします。

2015/8/27 時点での最新バージョン Xamarin.Forms 1.4.4.6392 を使用しています。

動作イメージ

単純なポップアップ的なやつ

f:id:ytabuchi:20150827100923g:plain:w300 f:id:ytabuchi:20150827100858g:plain:w300 f:id:ytabuchi:20150827100945g:plain:w300

黒透過背景に白ポップアップなやつ

f:id:ytabuchi:20150827143907p:plain:w300 f:id:ytabuchi:20150827143841p:plain:w300 f:id:ytabuchi:20150827133220p:plain:w300

サンプル

GitHub

AbsoluteLayout で手前に表示する Frame を用意します

本エントリーでは表示部分のみを記載しています。ListView のデータソースなどは Xamarin.Forms で最後まで表示すると動的にデータが増える ListView を作るには - Xamarin 日本語情報 をご参照ください。

Frame クラス は Children を一個だけ持ち、Padding が標準で 20 に設定されていて、角丸の枠がある、まさにポップアップを出すための View です。

C#

Frame でクルクルを定義します。

Color.Black.MultiplyAlpha(0.7d) と指定すると色自体の透過率を指定できるみたい!便利!XAML での書き方が分からなかったので誰か教えてくださいw)

frameLayer = new Frame
{
    BackgroundColor = Color.Black.MultiplyAlpha(0.7d), // 便利!
    IsVisible = false,
    Content = new StackLayout
    {
        Children =
        {
            new ActivityIndicator {
                IsRunning = true,
                Color = Device.OnPlatform(Color.White, Color.Default, Color.Accent),
            },
            new Label {
                Text = "Data loading...",
                TextColor = Color.White,
                XAlign = TextAlignment.Center,
            },
        }
    },
};

ListView にデータバインドして ItemAppearing のイベントハンドラを呼ぶあたりは以前と一緒です。

var cell = new DataTemplate(typeof(TextCell));
cell.SetBinding(TextCell.TextProperty, "TextItem");
cell.SetBinding(TextCell.DetailProperty, "DetailItem");

listLayer = new ListView
{
    ItemsSource = listItems,
    ItemTemplate = cell,
};
listLayer.ItemAppearing += ListLayer_ItemAppearing;

で、AbsoluteLayout の中に突っ込みましょう。上の行から順に呼び出されるので後に記述した frameLayer が手前に表示されます。

var abs = new AbsoluteLayout();

abs.Children.Add(listLayer);
abs.Children.Add(frameLayer);

Content = abs;

AbsoluteLayout は、画面サイズが決まった時に SizeChanged += AbsolutePageCS_SizeChanged; とイベントを呼んで配置するのが一般的なようですので、次のようなメソッドを用意しておきます。

private void AbsolutePageCS_SizeChanged(object sender, EventArgs e)
{
    AbsoluteLayout.SetLayoutFlags(frameLayer,
        AbsoluteLayoutFlags.PositionProportional);
    AbsoluteLayout.SetLayoutBounds(frameLayer,
        new Rectangle(0.5d, 0.5d,
        Device.OnPlatform(AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize, this.Width), AbsoluteLayout.AutoSize)); // View の中央に AutoSize で配置

    AbsoluteLayout.SetLayoutFlags(listLayer,
        AbsoluteLayoutFlags.PositionProportional);
    AbsoluteLayout.SetLayoutBounds(listLayer,
        new Rectangle(0d, 0d,
        this.Width, this.Height)); // View の左上から View のサイズ一杯で配置
}

SetLayoutFlagPositionProportional もデフォルトみたいな感じですね。で、SetLayoutBounds で配置場所を決めていきます。

奥に配置する ListView は画面全体なので new Rectangle(0d, 0d, this.Width, this.Height) (始点 X, 始点 Y, 幅, 高さ) で配置して、手前の frameLayer は画面真ん中(0.5d, 0.5d)に内容物の大きさでサイズを設定してくれる AbsoluteLayout.AutoSize で配置してあげると良い感じです。

WinPhone の ActivityIndicator は画面幅いっぱいに動くのが一般的なイメージで AutoSize だとしょっぱかったので Device.OnPlatfomWidth を指定しています。

ということで C#こんな感じ になりました。

Xaml
<AbsoluteLayout x:Name="abs">
    <ListView x:Name="listLayer"
                ItemsSource="{Binding}"
                ItemAppearing="ListLayer_ItemAppearing">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding TextItem}" Detail="{Binding DetailItem}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    <Frame x:Name="frameLayer"
            BackgroundColor="Black"
            IsVisible="False">
        <StackLayout>
            <ActivityIndicator IsRunning="True">
                <ActivityIndicator.Color>
                    <OnPlatform x:TypeArguments="Color"
                                iOS="White"
                                Android="Default"
                                WinPhone="Accent" />
                </ActivityIndicator.Color>
            </ActivityIndicator>
            <Label Text="Data loading..."
                    TextColor="White"
                    XAlign="Center" />
        </StackLayout>
    </Frame>
</AbsoluteLayout>

SizeChanged は ContentPage のイベントなので ContentPage 内に SizeChanged="AbsolutePageXaml_SizeChanged" を書いてあげて、後は C# でのイベントハンドラと同じやつで大丈夫です。

後は ListView に ItemAppearing="ListLayer_ItemAppearing" を追加して Frame を呼んでいるだけです。

簡単ですね!

所感

  • Frame が以外と便利
  • XAML なら特にですが、普通に作った Content を AbsoluteLayout で包んで、定型の SizeChanged イベントを追加して別クラスにした Frame を呼べば色んなページで使えますし、もしかしてこのやり方は便利かもしれません。
  • クルクルしてる間に操作できないようにするには、Frame の奥に 1枚透過黒の ContentView を配置すると良さそうです。(サンプルはこのやり方でアップデートしました。)

Xamarin 気になった方は

是非 ダウンロード(直接) / ダウンロード(弊社経由) して触ってみてください。 学習用リソースJXUG リンクページ に参考資料を纏めてますので併せてどうぞ。

Xamarin の情報が欲しい方はこのブログも購読いただいたりすると嬉しいです。

以上です。