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

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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

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