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

ぴよぴよエンジニアの日記

クラウドベンダーに勤める見習いSEの日記です。発言は私自身の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Xamarin.FormsでOxyPlotを使ってみた

Xmarin.Formsでグラフ描画ライブラリ"OxyPlot"を使ってみました.

OxyPlotの紹介はこちら.
santea.hateblo.jp


開発環境



OxyPlotの導入方法

f:id:Santea:20160430212125p:plain

Nugetで導入できます.

プレリリースのパッケージなので,"プレリリースを含める"のチェックが必要です.



グラフ描画の前段階

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

namespace OxyPlotSample
{
    class PopulationModel
    {
        public int Year { get; set; }
        public int YouthPopulation { get; set; }
        public int WorkingAgePopulation { get; set; }
        public int AgedPopulation { get; set; }

        public static List<PopulationModel> GetPopulationList()
        {
            var list = new List<PopulationModel>() {
                new PopulationModel()
                {
                    Year = 21,
                    YouthPopulation = 17011,
                    WorkingAgePopulation = 81493,
                    AgedPopulation = 42715
                },
                new PopulationModel()
                {
                    Year = 22,
                    YouthPopulation = 16839,
                    WorkingAgePopulation = 81735,
                    AgedPopulation = 43678
                },
                new PopulationModel()
                {
                    Year = 23,
                    YouthPopulation = 16705,
                    WorkingAgePopulation = 81342,
                    AgedPopulation = 44460
                },
                new PopulationModel()
                {
                    Year = 24,
                    YouthPopulation = 16547,
                    WorkingAgePopulation = 80175,
                    AgedPopulation = 45986
                },
                new PopulationModel()
                {
                    Year = 25,
                    YouthPopulation = 16390,
                    WorkingAgePopulation = 79010,
                    AgedPopulation = 47501
                }
            };

            return list;
        }
    }
}

今回のサンプルデータとなる日本の年齢別人口のModelクラスです.



using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace OxyPlotSample.Droid
{
    [Activity(Label = "OxyPlotSample", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);

            // Xamarin.Forms.Forms.Init()の後にOxyPlot.Xamarin.Forms.Platform.Android.Forms.Init()を呼ぶ
            OxyPlot.Xamarin.Forms.Platform.Android.Forms.Init();

            LoadApplication(new App());
        }
    }
}

各プラットフォームからOxyPlotの初期化メソッドを呼び出す必要があります.

各プラットフォームごとの初期化メソッドは以下のようになります.

  • OxyPlot.Xamarin.Forms.Platform.Android.Forms.Init();
  • OxyPlot.Xamarin.Forms.Platform.iOS.Forms.Init();
  • OxyPlot.Xamarin.Forms.Platform.WinPhone.Forms.Init();



ソースコードに直書きするパターン

<?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="OxyPlotSample.MainPage"
             xmlns:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms">

  <oxy:PlotView  x:Name="PlotView" VerticalOptions="Center" HorizontalOptions="Center" />
  
</ContentPage>

各プラットフォーム共通のViewになるMainPage.xamlです.

xmlns:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms"

上記を宣言し,名前空間に割り当てを行います.



using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace OxyPlotSample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            var model = new PlotModel();
            model.Background = OxyColors.White;
            var list = PopulationModel.GetPopulationList();

            var axisX = new CategoryAxis();
            axisX.Title = "年度";
            axisX.ItemsSource = list;
            axisX.LabelField = "Year";
            axisX.Position = AxisPosition.Bottom;
            model.Axes.Add(axisX);

            var axisY = new LinearAxis();
            axisY.Title = "人口[千人]";
            axisY.Position = AxisPosition.Left;
            model.Axes.Add(axisY);

            var seriesYouth = new ColumnSeries();
            seriesYouth.ItemsSource = list;
            seriesYouth.ValueField = "YouthPopulation";
            seriesYouth.IsStacked = true;
            model.Series.Add(seriesYouth);

            var seriesWorkingAge = new ColumnSeries();
            seriesWorkingAge.ItemsSource = list;
            seriesWorkingAge.ValueField = "WorkingAgePopulation";
            seriesWorkingAge.IsStacked = true;
            model.Series.Add(seriesWorkingAge);

            var seriesAged = new ColumnSeries();
            seriesAged.ItemsSource = list;
            seriesAged.ValueField = "AgedPopulation";
            seriesAged.IsStacked = true;
            model.Series.Add(seriesAged);

            this.PlotView.Model = model;
        }
    }
}

MainPage.csです.

ソースコード側にて要素を直書きしています.


f:id:Santea:20160502015210p:plain

こちらが実行結果になります.



MVVMで書くパターン

残念ながらできませんでしたorz


f:id:Santea:20160502013655p:plain

OxyPlot.Xamarin.FormsにはPlotViewしか定義がないので,OxyPlot.~で参照できるかなと思ったのですが,それも参照が取れませんでした.

StackOverFlowあたりを見てもPlotModelとバインドする方法しか載っていなかったので,現状無理なのかもしれません...

一応,実現したかったコードを載せておきます.



<?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="OxyPlotSample.MainPage"
             xmlns:local="clr-namespace:OxyPlotSample;assembly=OxyPlotSample"
             xmlns:oxy="clr-namespace:OxyPlot;assembly=OxyPlot">

  <ContentPage.BindingContext>
    <local:MainPageViewModel />
  </ContentPage.BindingContext>

  <oxy:PlotModel>

    <oxy:Axes>
      <oxy:Axes.CategoryAxis ItemsSource="{Binding PopulationList}"
                        LabelField="Year"
                        Title="年度"/>
      
      <oxy:Axes.LinearAxis Title="人口[千人]"
                      Position="Left" />
      
    </oxy:Axes>

    <oxy:Series.ColumnSeries IsStacked="True"
                      ItemsSource="{Binding PopulationList}"
                      ValueField="YouthPopulation" />

    <oxy:Series.ColumnSeries IsStacked="True"
                      ItemsSource="{Binding PopulationList}"
                      ValueField="WorkingAgePopulation" />

    <oxy:Series.ColumnSeries IsStacked="True"
                      ItemsSource="{Binding PopulationList}"
                      ValueField="AgedPopulation" />

  </oxy:PlotModel>

</ContentPage>
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace OxyPlotSample
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OxyPlotSample
{
    class MainPageViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        List<PopulationModel> populationList;

        public List<PopulationModel> PopulationList
        {
            protected set
            {
                if (populationList != value)
                {
                    populationList = value;
                    OnPropertyChanged("List");
                }
            }
            get { return populationList; }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
        }

        public MainPageViewModel()
        {
            this.PopulationList = PopulationModel.GetPopulationList();
        }
    }
}

.xamlで書けると.csのコードビハインドがものすごくすっきりするのでぜひこの方法を使いたいのですが...

何か方法を知っている方がいらっしゃればぜひ教えてください!



まとめ

Xamarin.Formsでグラフ描画ライブラリ"OxyPlot"を使ってみました.

コードビハインドでは問題なく記述できます.

MVVMなパターンでは現状実現できません(確証はありません).
(PlotModelとバインドすることはできますが,これはあまり旨みがないと思います).

以上です.