かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Xamarinで処理中を表すインジケーターを出したい

先日Cognitive Serviceを使った笑顔判定機を作りました。

こいつですが、Web上のAPIを呼び出して回線状況に応じては、そこそこ時間がかかるにも関わらず、処理中を示すUIが表示されていませんでした。

Xamarin.Formsでは、ActivityIndicatorクラスを使うことで、簡単にインジケーターを出すことができます。

写真はおっさんの顔になってしまうので省きますが…

使い方は簡単です。画面の全体を覆うようにGridを置いて、そこにメインコンテンツとActivityIndicatorを置くだけです。そして、ActivityIndicatorIsRunningプロパティにTrueかFalseを設定することで表示・非表示を切り替えます。XAML的には以下のような感じ。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="SmileXamarinApp.Views.MainPage"
             Title="MainPage">
  <ContentPage.ToolbarItems>
    <ToolbarItem Text="写真"
                 Command="{Binding TakePhotoCommand}" />
  </ContentPage.ToolbarItems>
  <Grid>
    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
      <Image Source="{Binding ImageSource}" />
    </StackLayout>
    <ActivityIndicator IsRunning="{Binding IsBusy}" />
  </Grid>
</ContentPage>

IsBusyがVMに追加したプロパティでAPI呼び出し中だけTrueになるように制御しておきます。

using Microsoft.ProjectOxford.Face;
using Plugin.Media;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace SmileXamarinApp.ViewModels
{
    public class MainPageViewModel : BindableBase, INavigationAware
    {
        private ImageSource imageSource;

        public ImageSource ImageSource
        {
            get { return this.imageSource; }
            set { this.SetProperty(ref this.imageSource, value); }
        }

        private IPageDialogService PageDialogService { get; }

        public DelegateCommand TakePhotoCommand { get; }

        private bool isBusy;

        public bool IsBusy
        {
            get { return this.isBusy; }
            set { this.SetProperty(ref this.isBusy, value); }
        }

        public MainPageViewModel(IPageDialogService pageDialogService)
        {
            this.PageDialogService = pageDialogService;
            this.TakePhotoCommand = new DelegateCommand(async() => await this.TakePhotoAsync());
        }

        private async Task TakePhotoAsync()
        {
            var file = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
            {
                DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Front,
                AllowCropping = false,
            });
            if (file == null) { return; }

            this.ImageSource = ImageSource.FromStream(() => file.GetStream());

            try
            {
                this.IsBusy = true;
                var client = new FaceServiceClient("Your API Key");
                var result = await client.DetectAsync(file.GetStream(), returnFaceAttributes: new[]
                {
                    FaceAttributeType.Smile,
                });
                if (!result.Any()) { return; }
                await this.PageDialogService.DisplayAlertAsync("Smile point", $"Your smile point is {result.First().FaceAttributes.Smile * 100}", "OK");
            }
            finally
            {
                this.IsBusy = false;
            }

        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {
        }
    }
}

これで、処理中なのかなどうなのかな?という疑問を持たなくてすむようになりました。コードは同じくGitHubのリポジトリにあげています。

github.com