ComponentOne Information

ComponentOne Studio/Wijmo/Xuniの最新情報を公開中

内閣府が公開した国民の祝日をカレンダーに表示する

国民的アニメに登場する少年が、6月は祝日も学校の休みも関係がなくつまらない月だと嘆き、勝手に祝日を作ってしまうお話がありました。
このエピソードにより、当時子供だった我々に「6月には祝日がない」という悲しい事実が深く記憶されたものです。

あれから数十年が経過した現在でも6月に祝日はありません。
3連休を増やすハッピーマンデー制度が登場し、7月と8月にも祝日が追加されましたが、6月はそのままです。6月だけは祝日がありません。もちろん勝手に追加できる制度もありません。

このように、日本の祝日は「国民の祝日に関する法律」で定められています。日付が変わる祝日や2つの祝日に挟まれる形で成立する祝日もあるので変動です。内閣府から確定した国民の祝日として公表されており、CSVファイル形式のオープンデータとして公開中です。

公開されている国民の祝日データがあるのは便利です。これを情報として取り込んで利用できます。 この記事では、国民の祝日データをXamarin.Fomrsで開発するアプリでカレンダーに表示する手順を解説します。 Xuni(ズーニー)のコンポーネントであるCalendarを使用するサンプルです。

内閣府が数年分を提供していることからもわかりますが、祝日は変則的な法則で決められているのでシステムに組み込む場合は工夫が必要です。
また、振替休日も取り入れたり、昨今では春節など日本以外の祝日にも対応要求がでてきます。
それらをロジックで対応していくのは困難です。祝日が追加された場合は対応できません。

そうなると設定ファイルを用意して読み込むのが現実的な手法になります。データを外部に切り出しておけばメンテナンスも容易です。
このサンプルはそのような状況を想定しました。


サンプルプロジェクトの作成

Xamarin.Formsを利用し、C#でAndroid、iOSのサンプルアプリを作成します。
Xuniでカレンダーを表示するアプリです。手順は以下になります。

  1. Xamarin.Formsのプロジェクトを作成
  2. Xuniを利用する設定
  3. CSVファイルを読み込み国民の祝日リストを作成
  4. リストを表示するクラスを作成
  5. 表示する画面の設定

1と2の説明は省略します。評価用のライセンスを組み込み済みでXuniの利用準備が整っているプロジェクトを公開していますので、これを改造して利用します。プロジェクトは以下からダウンロード可能です。

国民の祝日データを読み込む

CSVファイルの形で前述のWebサイトで公開されていますので、そのURLを指定して読み込むことができます。頻繁に内容が更新されるデータではありませんので、ダウンロードしたものをCSVファイルとしてプロジェクトに組み込み、それを読んで利用することも可能です。 なお、Xamarin.FormsでCSVファイルを読み込み、利用する手順については別記事で紹介しています。

また、Web上のCSVファイルの形式が変更された場合に、アプリ側の修正が必要になります。ここで紹介しているサンプルも、2017年3月1日時点で公開されている形式に基づいています。

実際に2017年2月28日までは、異なる形式で公開されていました。今回の更新ではよりアプリで利用しやすい形式のCSVに改良されています。
CSVファイルをVisual Studio Codeの Excel Viewerアドインを利用して表示したのが以下です。 IT利活用の視点で使いやすいのは右側であることがわかります。

f:id:ComponentOne_JP:20170302153650p:plain


Microsoft.Net.Httpの追加

それでは本題に戻ります。 Xamarin.Formsで、Web上に公開されたデータにHttp通信でアクセスしファイルを入手するにはHttpClientを利用します。
これはNuGetサーバーに公開されていますので、PCLプロジェクトにパッケージを追加してインストールします。
パッケージの追加でhttpをキーワードに検索すると表示されるMicrosoft.Net.Httpが対象です。

f:id:ComponentOne_JP:20170302161017p:plain

これをインストールすると以下がパッケージとして追加されます。下段の2つは自動で入ります。

  • Microsoft.Net.Http
    • Microsoft.Bcl
    • Microsoft.Bcl.Build
データを読み込むクラスを作成

次に、通信してデータを読み込むクラスを作成します。ここではHolidayDataSource.csという名称で、PCLプロジェクトにファイルを追加します。
このクラスでは、Http通信でCSVファイルを読み込み、国民の祝日データをDictionaryとして格納します。
Webアクセスを含むので、非同期処理のAsyncGetWebAPIDataメソッドを作成する機能の実装です。

まずはHttpClientクラスをインスタンス化して利用し、指定したURLからシンプルな形でファイルを取得する処理です。ここでは直接CSVファイルのURLを指定し、非同期にSystem.Text.Streamクラスのオブジェクトに読み込みます。 メソッド内で利用する定義は以下です。Dictionaryは他のクラスで利用するのであらかじめpublicで定義しておきます。

// 祝日を格納するDictionaryの定義
public Dictionary<DateTime, string> holidaysDic;
// データのURLを設定
private static readonly string URL = "http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv";
// HttpClientの定義
private static HttpClient httpClient;

HttpClientを利用する部分のコードの抜粋です。

// HttpClientの作成 
httpClient = new HttpClient();
//非同期でWebからデータを取得してStreamに格納
Task<Stream> response = httpClient.GetStreamAsync(URL);
Stream result = await response;

Streamに読み込んだデータは1行づつ処理します。
データをみると1行目はヘッダーなので読み飛ばし、2行目以降を、ヘッダーに記述された形式「国民の祝日月日,国民の祝日名称」のデータとして読み込みます。
読み込んだデータは、年月日をキーにしたDictionaryに、祝日名称を値として格納します。

読み込み時はデータのエンコードを指定する必要があります。
このCSVデータはShift-JISでエンコードされたテキストとして公開されていますので、Shift-JISを指定して読み込みます。これまでの部分に読み込みを追加したコードの全体が以下です。

public class HolidaysDataSource
{
    // 祝日リスト用のDictionaryの定義
    public Dictionary<DateTime, string> holidaysDic;
    // データのURLを設定
    private static readonly string URL = "http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv";
    // HttpClientの定義
    private static HttpClient httpClient;

    public async Task<Dictionary<DateTime, string>> AsyncGetWebAPIData()
    {
        // 祝日を格納するDictionaryの作成
        holidaysDic = new Dictionary<DateTime, string>();
        // HttpClientの作成 
        httpClient = new HttpClient();
        //非同期でWebからデータを取得してStreamに格納
        Task<Stream> response = httpClient.GetStreamAsync(URL);
        Stream result = await response;

        // エンコードをShift_JISに設定
        var encoding_SJIS = System.Text.Encoding.GetEncoding("Shift_JIS");
        using (var sr = new System.IO.StreamReader(result, encoding_SJIS))
        {
            // ヘッダー部分の1行を読み飛ばす
            sr.ReadLine();
            while (!sr.EndOfStream)
            {
                //2行目以降1行ずつ読み込む
                var sn = sr.ReadLine().Split(',');
                if (sn.Length > 1 && sn[1].Trim().Length > 0)
                {
                    // 日付(DateTime)をキーにして、祝日名称をDictionaryに格納
                    holidaysDic[DateTime.Parse(sn[0].ToString())] = sn[1].ToString(); 
                }
            }
        }
        // Dictionary でデータを返す
        return holidaysDic;
    }
}

以上でCSVファイルからDictionary形式の国民の祝日リストができました。日付をキーにして祝日の名称が取得可能になります。

カレンダーを表示する画面レイアウトを作成

XuniのCalendarをXAMLに貼り付けることで、カレンダー表示が可能になります。 このカレンダーの1日を表示する領域は、カスタマイズしたレイアウトをテンプレートとして設定できるので、今回は下図のようなレイアウトにします。Gridレイアウトを利用した表組みです。

日付を左側に配置し、祝日の場合のみ画像領域に日の丸を表示し2段目は2つのセルを連結して祝日の名称を表示します。 このレイアウトが、月表示されたカレンダーの各日付領域になります。

f:id:ComponentOne_JP:20170302153826p:plain

この部分を表現するXAMLは以下です。

<!--  2行2列のGridレイアウトを定義 -->
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
    <Grid.RowDefinitions>
        <RowDefinition Height="20" />
        <RowDefinition Height="20" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="20" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <!--  上段左:日付を表示するLabel  上段右:画像を表示するImage  -->
    <Label 
            Grid.Row="0" Grid.Column="0"
            Margin="0,0,0,0" FontSize="15"
            VerticalOptions="FillAndExpand"
            HorizontalTextAlignment="Center"
            Text="{Binding Day}"
            TextColor="{Binding TextColor}" 
            />
    <Image 
            Grid.Row="0" Grid.Column="1"
            HeightRequest="9"
            Aspect="AspectFit"
            IsVisible="{Binding IsHoliday}"
            Source="{Binding FlagImage}" 
            />
    <!--  下段左右:祝日名称を表示するLabel  -->
    <Label 
            Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
            FontSize="8"
            HorizontalTextAlignment="Center"
            Text="{Binding HolidayName}" 
            />
</Grid>

LabelやImageに表示する内容は、データバインディング機能を利用します。 例えばLabelのTextプロパティは{Binding Day}、TextColorプロパティは{Binding TextColor}を指定しています。これは後述するビューモデルをコンテキストとしてバインディングする場合、データモデルのDayTextColorがバインディングソースになります。

作成したGridレイアウトは、Xuni CalendarのDaySlotTemplateとして定義しました。 最終的に以下のXAMLに設定した内容を、XuniCalendarに追加します。

<xuni:XuniCalendar x:Name="calendar">
    <xuni:XuniCalendar.DaySlotTemplate>
        <DataTemplate>
            <xuni:CalendarViewDaySlot>
                <xuni:CalendarViewDaySlot.View>

                <!--  この部分に作成したレイアウトが入る  -->

                </xuni:CalendarViewDaySlot.View>
            </xuni:CalendarViewDaySlot>
        </DataTemplate>
    </xuni:XuniCalendar.DaySlotTemplate>
</xuni:XuniCalendar>

カレンダーを表示するコードを作成

次に作成したXAMLのコードビハインドを作成します。
コードビハインドでは行う処理は以下です。 * 祝日用Dictionaryの作成指示 * カレンダーの設定 - 曜日の設定 - 日付領域の休日判断 - 日付領域に表示するデータのバインディング

まず、前に作成した祝日データの格納用のクラスをインスタンス化して実際の処理実行を指示します。 以下のような関数をコンストラクタから呼び出せるように定義します。

// 非同期でデータ取得のメソッドを実行する関数
async  Task getHolidayList()
{
    try
    {
        // 取得したデータをHolidaysListに設定
        HolidaysList = await new HolidaysDataSource().AsyncGetWebAPIData();
    }
    // エラー表示処理
    catch (System.Exception ex)
    {
        await DisplayAlert("Error", ex.Message.ToString(), "OK");
    }

}

コンストラクタではこの関数を呼び出します。

var task = getHolidayList();

次はカレンダーの設定です。カレンダーでは曜日領域と日付領域でカスタマイズした処理を入れます。日本では土曜日、日曜日そして祝日の色を平日と異なる色にするのが一般的です。その処理を追加します。 曜日領域はDayOfWeekSlotLoadingイベントで処理します。

calendar.DayOfWeekSlotLoading += (sender, e) =>
{
    // 曜日スロットを取得して曜日領域に表示する内容を設定
    var dayofweekSlot = e.DayOfWeekSlot as Xuni.Forms.Calendar.CalendarDayOfWeekSlot;
    dayofweekSlot.DayOfWeekTextColor = getColor(e.DayOfWeek); //曜日のテキスト色を設定
};

また日付領域については、祝日の設定とあわせてDaySlotLoadingイベントで処理します。 日付領域はDaySlotContextのビューモデルクラスを作成しそれですべてを管理します。以下がそのクラスです。

// 日付領域のビューモデル
public class DaySlotContext
{
    public DaySlotContext(DateTime date, Color color, string holidayName = null, bool isholiday = false)
    {
        this.Day = date.Day;
        this.TextColor = color;
        this.HolidayName = holidayName;
        this.IsHoliday = isholiday;

        if (isholiday)
        {
            // 祝日の場合のみリソースの画像を利用
            this.FlagImage = ImageSource.FromResource(imgID);
        }
        else
            this.FlagImage = null;
    }
    // 日付領域にバインドするためのプロパティ
    public int Day { get; set; }                // 日付
    public Color TextColor { get; set; }        // テキスト色
    public string HolidayName { get; set; }     //祝日の名称
    public bool IsHoliday { get; set; }         // 祝日フラグ
    public ImageSource FlagImage { get; set; }  // 祝日画像を設定

    // 画像用リソースIDを定数で定義
    private static readonly string imgID = "Xuni_QuickStart.Resources.Images.jp_flag.png";
}

このイベントでは、引数として描画する日付の情報などをCalendarDaySlotLoadingEventArgsクラスの引数として取得します。その引数で提供される日付で判断し、祝日とテキスト色を変更します。 イベント内の処理は以下です。 * 日付領域は引数で取得するDaySlotCalendarViewDaySlotクラスにキャスト
* 日付の種別にあわせてDaySlotContextを作成
* XAMLで定義した日付領域用の'BindingContext'に設定
これを対象月の日付領域および、カレンダー上に表示される前後の隣接月の日付領域用に処理します。

//日付スロットの処理
calendar.DaySlotLoading+= (object sender, CalendarDaySlotLoadingEventArgs e) => 
{
    var dayslot = e.DaySlot as CalendarViewDaySlot;
    if (!e.IsAdjacentDay)
    {
        // 日付領域の描画対象になった日付が、祝日Dictionaryに存在するかどうか確認
        if (HolidaysList.ContainsKey(e.Date))
        {
            // 祝日の場合(日付、テキスト色、祝日名称を設定)
            dayslot.BindingContext = new DaySlotContext(e.Date, getColor(System.DayOfWeek.Sunday), HolidaysList[e.Date].ToString(), true);
        }
        else
        {
            // 祝日以外の場合(日付とテキスト色を設定)
            dayslot.BindingContext = new DaySlotContext(e.Date, getColor(e.Date.DayOfWeek));
        }
    }
    else
    {
        // 隣接日の場合(日付は設定されるがテキスト色は設定しても表示には反映しない)
        dayslot.BindingContext = new DaySlotContext(e.Date, getColor(e.Date.DayOfWeek));
    }
};

日付の色を設定する関数は以下です。祝日の場合は日曜日を指定して色を取得します。

// 曜日で判断して色を返す関数
private Color getColor(System.DayOfWeek dayofWeek)
{
    Color color;
    switch (dayofWeek)
    {
        case System.DayOfWeek.Saturday: // 土曜日は青
            color = Color.Blue;
            break;
        case System.DayOfWeek.Sunday: // 日曜日は赤
            color = Color.Red;
            break;
        default:
            color = Color.Black;  // 既定値は黒
            break;
    }
    return color;
}

こちらの手順は別の記事でも解説しています。

プラットフォームごとの追加設定

Android、iOSで設定が異なる部分についてはそれぞれのプロジェクトで設定する必要があります。

Androidアプリのパーミッションを設定

以上で準備が整いましたので実行します。その前にCSVファイルをWebサイトから取得しますので、Androidアプリにインターネットアクセスの権限を設定します。
Androidプロジェクトのオプション画面で「必要なアクセス許可」に以下の3種についてチェックを入れます。 同じ手続きはPropatiesフォルダにあるAndroidManifest.xmlを編集することでも可能です。
* AccessFineLocation
* AccessNetworkState
* Internet

f:id:ComponentOne_JP:20170302153952p:plain

iOSアプリで動作するための改良

iOSアプリの場合は、Shift_JISのエンコードが正しく処理できません。そのためCSVファイルの読み込みに失敗して祝日が反映されない状態で表示されます。

f:id:ComponentOne_JP:20170302154011p:plain

前述では解説していませんがこれを回避するために、NuGetパッケージ「Portable.Text.Encording」を利用しています。これを利用し、データソースを作成するHolidayDataSource.csでエンコードを指定する記述を以下のようにしています。この改良はPCLプロジェクトが対象です。

// iOSでShift_JISエンコードを使用するためにPortable.Text.Encording を利用
var encoding_SJIS = Portable.Text.Encoding.GetEncoding("Shift_JIS");
// AndroidのみであればSystem.Text.Encording が利用できる
// var encoding_SJIS = System.Text.Encoding.GetEncoding("Shift_JIS");

このこともあわせ、実際はエンコードをUTF-8に指定したCSVファイルをリソース格納して取り込む方法を推奨します。

祝日が設定されたカレンダー

これでカスタマイズが完了し、実行した結果が以下です。
5月のゴールデンウィークと、6月には祝日が無いことを確認できます。

f:id:ComponentOne_JP:20170302154034p:plain

XAMLで定義したレイアウト通りに表示できました。

f:id:ComponentOne_JP:20170303084956p:plain

まとめ

このように、ファイルに保存した祝日情報を利用することでカレンダーのカスタマイズが可能です。
日付領域はXAMLで作成することができるので、他のコントロールを組み込んだ設定もできます。

Xamarin.FormsとXuniを活用することで、見た目や内容をカスタマイズしたカレンダーを、Android/iOSアプリに表示できます。
その処理のほとんどは共通化したPCLプロジェクトに記述しているので、少ない工数でクロスプラットフォームアプリ開発を実現します。 XuniはXamarin.Formsで利用可能な各種コンポーネントを提供していますので、あわせてご利用ください。


参考情報

[NuGet Gallery | Portable.Text.Encoding] (https://www.nuget.org/packages/Portable.Text.Encoding/)

[WinRT/Metro TIPS:シフトJISのEncodingオブジェクトを取得するには?- @IT] (http://www.atmarkit.co.jp/ait/articles/1509/30/news039.html)


追記:この記事で紹介している「Xuni(ズーニー)」は、2017年7月に提供を終了し、新たに「ComponentOne Studio for Xamarin」として提供しています。

OSS採用によるお悩みをAngularとWijmo(ウィジモ)で解決!

とあるプロジェクトリーダーのお悩み

“オープンソースソフト(OSS)を採用して開発効率を上げよう”

    → このOSSも使えばもっと便利になるんじゃない?よし、採用!
    → そのOSSを使うにはこのOSSも必要になる?
    → 精通したエンジニアを育てないとな…自前で開発するより効率がいいし
    → あのOSSもいいな。でもOSSを管理できるエンジニアはまだ足りないし
    → 本当にこの方針でいいのか不安だな…  ←イマココ

 

OSSは、Web開発においては特に必要不可欠な存在となりつつありますが、利用するOSSが増えるにつれ、それらのOSSに精通したエンジニアと学習期間をどう確保するかが課題となってきます。

この課題への対策として、「長く使っていけそうなOSSを厳選すること」という方法が挙げられますが、Angularは次のような特長から、この課題を解決するポテンシャルを秘めています。

 

【フルスタック】

  ひとつで広範囲の技術をカバーするフレームワーク

【Web標準】

  ECMAScriptの標準仕様に基づいた設計

【安心感】

  Google社が携わり企業やコミュニティが開発に参加

 

さらに、Angular CLIというコマンドラインツールを使うと、プロジェクトやコンポーネントのテンプレート作成、webpackを用いたビルド、単体テストなどを効率よく行うことが可能になります。

ブログでは、Angular CLIと業務システム向けUIコンポーネント「Wijmo(ウィジモ)」を用いてAngularアプリケーションを簡単に作成する方法をご紹介していますので、ぜひご覧ください。

hatena.c1.grapecity.com

 

この記事はグレープシティが発行するメールマガジン「PowerNews」の第465号(2017/2/23発行)に掲載された記事を、加筆修正して転載したものです。  
≫グレープシティの最新情報を発信!メールマガジン「PowerNews」
製品の最新情報、プログラミングに役立つコラムのほか、プレゼント企画やセミナーのご案内など、日々の開発業務に役立つ情報を、毎月2回・第2、4木曜日にお送りしています。 
PowerNews - メールマガジン | GrapeCity Developer Tools 

FlexGrid for ASP.NET MVCで非連結データを挿入する (2):AJAXでJSONデータを取得する

非連結のFlexGridにデータを挿入する方法として、前回はサーバー側のCollectionViewサービスを使用する方法をご紹介しました。このブログでは、AJAXコールを使用して非連結のグリッドにセルごとのデータを挿入する方法について説明します。また、サーバーから取得したDictionary形式のデータを、クライアント側に送信する前にJSON形式にシリアル化しています。

 

≫ ComponentOne Studioのダウンロードはこちら

 

本例では、サーバー側のSalesクラスをモデルとしています。
「Product」、「Country」、「Amount」の3つの列のデータを挿入します。
非連結グリッドであるため、データをクライアント側でグリッドに挿入しなければなりません。

 

まず、プロジェクトを設定しましょう。

  1. Visual Studioにて、 [C1 ASP.NET MVC 5 アプリケーション] テンプレートを使用して新規プロジェクトを作成します。
  2. プロジェクトウィザードで[クライアントIntelliSenseの有効化]チェックボックスをオンにしてクライアント側の TypeScript インテリセンスを有効にします (クライアント側でのTypeScriptの使用をお勧めしますが、必須ということではありません)。

 

続きを読む

FlexGrid for ASP.NET MVCで非連結データを挿入する (1):CollectionViewサービスを使用する

非連結のFlexGridにデータを挿入する方法について2回に渡って説明します(FlexGridは、非連結と連結データの両方を対応しています)。

 

≫ ComponentOne Studioのダウンロードはこちら

 

本例では、サーバー側のSalesクラスをモデルとしています。
「Product」、「Country」、「Amount」の3つの列のデータを挿入します。
非連結グリッドであるため、データをクライアント側でグリッドに挿入しなければなりません。

 

非連結のFlexGridにデータを挿入する方法は2つあります。

このブログでは、より簡単な方法であるCollectionView サービスを使用する方法について説明します。

 

続きを読む

ナレッジベース更新情報(2017/2/20~2/24)

以下の13文書を追加・更新しました。

ComponentOne製品のナレッジベースは随時更新中です。
≫ ナレッジベースはこちら

 

Xamarin.Formsでカスタムフォントを利用したアイコンを表示する

アイコンとは視覚的に操作が直感的にわかりやすいように、小さく図案化したものです。 各種アプリケーションで利用されているのでいまさら説明するまでもありません。

最近では「保存」を意味するアイコンが、本当にその形で良いのか?という疑問もありますが、 可能な限り多くの人に、わかりやすい形でデファクトスタンダードの方式で一般的なものが使われています。

アイコンを表示するためには、アイコン用の画像ファイルを利用するのが一般的でしたが、昨今ではアイコンを文字として定義し、その文字に対応したフォントでそれぞれのアイコンを示すこともあり、 フォントによるWebページやアプリ制作の現場で利用されています。

有名なのはFontAwesomeです。

Ver.4.7.0(2017年2月現在)では、675個のアイコンがフォントとして提供されています。
一般的なアイコンだけにとどまらず、著名企業やサービスのブランドロゴも含まれます。

これらはXamarinで開発するアプリでも利用可能です。
この記事では、このフォント「FontAwesome」をXamarin.Fomrsアプリで利用する手順を解説します。 Xuni(ズーニー)のコンポーネントであるFlexChartで表示したグラフのX軸ラベルに、おなじみのブランドアイコンを表示します。

フォントの配布
今回はアプリの配布は行いませんが、FontAwesomeはオープンフォントライセンスなのでアプリにフォントを添付できます。
“FontAwesome” is lisenced under the SIL Open Font License 1.1
SIL Open Font License


サンプルプロジェクトの作成

Xamarin.Formsを利用し、C#でAndroid、iOSのサンプルアプリを作成します。
Xuniでチャートを表示するアプリです。手順は以下になります。

  1. Xamarin.Formsのプロジェクトを作成
  2. Xuniを利用する設定
  3. フォントファイルをプロジェクトに組み込む
  4. 表示用データを作成するクラスを作成
  5. 表示する画面の設定

1と2の説明は省略します。すでにXuniの利用準備が整っているプロジェクトを公開していますので、これを改造して利用します。プロジェクトは以下からダウンロード可能です。

フォントをプロジェクトに組み込む

FontAwesomeは前述のWebサイトで公開されていますので、ダウンロードして利用できます。 ダウンロードしたファイルFontAwesome.otfは、Android/iOSそれぞれのプロジェクトに保存してください。

それぞれの保存先は以下です。 また、各ファイルのプロパティも設定します。通常はファイルを追加した時点でこの設定になっています。

プロジェクト ファイル保存先 プロパティ設定
Android Assets/Fonts ビルドアクション:AndroidAsset
出力ディレクトリにコピー:コピーしない
iOS Resources/Fonts ビルドアクション:BundleResource
出力ディレクトリにコピー:コピーしない

iOSプロジェクトの設定

次に各プロジェクトからフォントを利用する設定です。
iOSでは、前述の場所にフォントファイルを格納し、フォントの利用をinfo.plistに記載するだけです。

info.plistの指定

今回はVisual Studio for Macを利用します。

f:id:ComponentOne_JP:20170221155823p:plain

  1. Visual Studio for Mac でinfo.plistを開いて[ソース]のタブを選択
  2. 「新しいエントリの追加」をクリックして、ドロップダウンからFonts provided by applicationを選択
  3. 文字列にフォントファイルを保存したResources以下のパスFonts/FontAwesome.otfを記述

f:id:ComponentOne_JP:20170221155719p:plain

Xamarin Studioでも同じ作業が可能ですがVisual Studioの場合はIDE画面での設定ができません。XMLファイルとして直接編集して以下の内容を追記してください。

<plist version="1.0">
  <dict>
       : 省略 
    <key>UIAppFonts</key>
    <array>
        <string>Fonts/FontAwesome.otf</string>
    </array>
</dict>
</plist>

Androidプロジェクトの設定

Androidは特に指定する必要はありません。
しかし、そのため設定したフォントをコードで指定しても、追加したフォントの場合は適用してもらえず表示されません。
プラットフォーム固有の機能を追加して、Androidアプリにフォントの設定を追加をする必要があります。

Webサイトのナレッジベース
iOSアプリの入力領域を角丸の枠線で囲む方法 (Xamarin.Forms)
で利用したEffects機能で、Androidアプリのみに適用する設定を追加します。

Effectsの作成
  1. Androidプロジェクトに、新たにFontEffect.csクラスファイルを追加します。
  2. 作成したEffectを参照するためのassembuly属性を以下のように記述します。
[assembly: ResolutionGroupName("Effects")]
[assembly: ExportEffect(typeof(FontEffect), "FontEffect")]

3.PlatformsEffect クラスを継承し、OnAttachedメソッドをオーバーライドします。
4.FlexChaertコントロールに対して、指定したフォントからTypeFaceを作成して設定します。

上記を記述したC#のコードです。

using System;
using Android.Graphics;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xuni_QuickStart.Droid;
using Com.GrapeCity.Xuni.FlexChart;

[assembly: ResolutionGroupName("Effects")]
[assembly: ExportEffect(typeof(FontEffect), "FontEffect")]
namespace Xuni_QuickStart.Droid
{
 public class FontEffect : PlatformEffect
 {
    protected override void OnAttached()
    {
        // FlexChart に特化した設定
        if (Control.GetType().Equals(typeof(Com.GrapeCity.Xuni.FlexChart.FlexChart)))
        {
            // フォント格納場所のパス
            var fontPath = "Fonts/FontAwesome.otf";
            // FlexChartにキャスト
            var flexChart = (FlexChart)Control;
            // Assetsに設置したフォントファイルからTypeFaceを作成して設定
            flexChart.AxisX.LabelFontTypeface = Typeface.CreateFromAsset(Forms.Context.Assets, fontPath);        }
    }
    protected override void OnDetached()
    {
        //処理は省略
    }
 }
}

フォントの指定

フォントは共有ライブラリ(PCL)で設定します。
今回フォントを指定する対象はチャートのX軸に表示されるラベルです。
ラベルに冒頭のようなブランドロゴを設定します。

サンプルデータの作成

チャートを表示するためのサンプルデータを作成します。
ここでは、一つの要素に対して乱数で発生させた数値を組み合わせて一つのデータにしています。 データを生成するコードは以下です。 文字コードで指定している部分は、FontAwesomeのコードです。
FontAwesomeの一覧表から、各ロゴに対応する文字コードを選択しました。 現状は文字コードで記述していますが、表示時はこれが各種ロゴとしてチャートに表示されます。

// データソースを作成するクラス FlexChartのBindingContextに設定
public class FontAwesomeData
{
    private List<dataModel> appData;
    // データリストを取得するプロパティ
    public List<dataModel> Data
    {   get { return appData; }     }

    public FontAwesomeData()
    {
        Random rnd = new Random();
        appData = new List<dataModel>();
        // ブランドロゴ表示用のコード
        var LogoNames = "\uf179, \uf17b, \uf17a, \uf113, \uf293, \uf099, \uf230, \uf270, \uf1a0, \uf167".Split(',');
        for (int i = 0; i < LogoNames.Length; i++)
        {
            // ロゴと乱数で発生させた数値をデータとしてリストに格納
            var dm = new dataModel();
            dm.Logo = LogoNames[i].ToString();
            dm.NumData = rnd.Next(60, 100);
            appData.Add(dm);
        }
    }
}
// データモデル
public class dataModel
{
    public string Logo { get; set; }  // ロゴ
    public int NumData{ get; set; }  // サンプルデータの数値
}

FlexChartの設定

チャートを表示するための設定を行います。
画面用のXAMLには、チャートを表示するためのchartStacklayoutに入れています。
また、データを表示するための系列(Series)を定義し、データソースのNumDataBindingしています

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="Xuni_QuickStart.Xuni_QuickStartPage"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Xuni_QuickStart"
             xmlns:xuni="clr-namespace:Xuni.Forms.FlexChart;assembly=Xuni.Forms.FlexChart">

    <StackLayout VerticalOptions="FillAndExpand" 
                 Orientation="Vertical" 
                 Padding="0,20,0,20">
        <xuni:FlexChart x:Name="chart"
                        BindingX="Logo"
                        ItemsSource="{Binding Data}"
                        VerticalOptions="FillAndExpand">
            <xuni:FlexChart.Series>
                <xuni:ChartSeries 
                        x:Name ="S1"
                        Binding="NumData" 
                        Color  ="#b50f52"/>
            </xuni:FlexChart.Series>
        </xuni:FlexChart>
    </StackLayout>
</ContentPage>

コードビハインドでFlexChartの設定を行います。

  1. データソースをBindingContextに設定
  2. チャートの種類を曲線面チャート(SprineArea)に設定
  3. チャートのX軸の設定 ここでAndroid、iOSのプラットフォームを判断して、設定を変えます。
    Androidの場合: こちらで作成したEffectを適用
    iOSの場合:X軸のラベル用フォントをLabelFontFamilyプロパティに指定
  4. Y軸の設定 最大値、最小値そして目盛りの幅の単位を指定
public partial class Xuni_QuickStartPage : ContentPage
{
    public Xuni_QuickStartPage()
    {
        InitializeComponent();
        // データソースをFlexChartにバインディング
        chart.BindingContext = new FontAwesomeData();
        // チャート種類を曲線面チャートに設定
        chart.ChartType = Xuni.Forms.FlexChart.ChartType.SplineArea;
        // チャートのX軸の設定
        chart.AxisX.LabelFontSize = 30;

        // プラットフォームを判定して処理を分割
        if (Device.OS == TargetPlatform.Android)
        {
            // Androidプロジェクトに作成したEffectsを適用
            // X軸のラベルにフォントが適用される
            chart.Effects.Add(Effect.Resolve("Effects.FontEffect"));
        }
        else if(Device.OS == TargetPlatform.iOS)
        {
            // iOSの場合はResourceに追加したフォントを
            // infpo.plist に追加指定すると利用可能になり適用される
            chart.AxisX.LabelFontFamily = "FontAwesome";
        }

        // チャートのY軸の設定
        chart.AxisY.MajorUnit = 10;
        chart.AxisY.Min = 50;
        chart.AxisY.Max = 100;
    }
}

ブランドロゴを表示

実行した結果が以下の画像です。データの名称部分、つまりX軸のラベルに設定された文字コードが、フォントを利用した表示になり、ブランドロゴになりました。

f:id:ComponentOne_JP:20170221155454p:plain

今回利用したすべてのブランドアイコンはそれぞれの所有者の商標です。  
また、アイコンはすべてそのブランドを意味していますが、チャート上のデータはランダムな数値を利用しており、
アイコンおよび各ブランドとの関連はありません。  

まとめ

このように、カスタムフォントを組み込むことで、アプリ画面のデザインが変わるだけでなく、ユーザー操作を促すことができます。FontAwesomeで提供されるアイコンを駆使してインタラクションの優れたアプリ開発が実現できます。

なお、フォントの埋め込みには各フォントで決められています。フォントの配布が許可されているフォントであれば、今回の例のようにアプリに埋め込んで利用できます。それぞれの利用条件を確認してご利用ください。


参考情報

Xamarin Formsでアイコンフォントを表示する - yamamoWorks

jamesmontemagno/XamDroid.RobotoText: Roboto text everywhere!


追記:この記事で紹介している「Xuni(ズーニー)」は、2017年7月に提供を終了し、新たに「ComponentOne Studio for Xamarin」として提供しています。

Angular & Wijmoクイックスタート - データ連結

この記事では、前回の記事で作成したAngularアプリケーションを元にして、WijmoのInputNumber(数値入力)コンポーネントでデータ連結を設定する方法を紹介します。

Wijmoコンポーネントは、Angularのデータ連結をサポートします。Angularには、3種類のデータ連結方法が存在します。

  1. プロパティ連結:一方向のデータ連結とも呼ばれます。親コンポーネント(この記事ではメインページ)で設定した値が子コンポーネント(この記事ではInputNumberコンポーネント)に渡されます。
  2. 双方向のデータ連結:親コンポーネントで設定した値が子コンポーネントに渡され、同時に、子コンポーネントで設定した値が親コンポーネントに渡され、親子のコンポーネントの値が同期されます。
  3. イベント連結:子コンポーネントで特定の操作を行ったときにイベント処理を実行します。

f:id:ComponentOne_JP:20170214103608p:plain

続きを読む
ComponentOne