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

ActivityIndicatorをProgressBarからProgressRingに変更する

Xamarin

Xamarin Formsの読み込み中などの状態を表現するActivityIndicatorですが、iOSとAndroidではグルグル回る円で描画され、UWPでは何故か左から右に粒が流れるProgressBarで描画されておりデザインの統一が取れなくて困ります。

iOS Android UWP
f:id:yamamoWorks:20160816184154g:plain f:id:yamamoWorks:20160816184158g:plain f:id:yamamoWorks:20160816184205g:plain

そこでカスタムレンダラーを用いてProgressRingに置き換えてみました。

UWP(Ring版)
f:id:yamamoWorks:20160816185830g:plain

Xamarin FormsはオープンソースになっているのでデフォルトのレンダラーActivityIndicatorRenderer.csをコピペして少し変更しただけです。

GitHubにソースコードを置いておきます。 github.com

TableViewにコレクションデータをバインドする

Xamarin

Xamarin FormsのTableViewListViewと異なりコレクションデータをバインドする機能はありません。 TableViewは設定画面の構築で使用する事を想定しているようですが、メールのアカウント設定のように可変なレコードを扱いたいケースがあるかと思います。 この場合、コードビハインドでレコードをループしてTableViewの中のTableSectionに行を追加するコーディングとなり美しくありません。 そこで今回はTableView(TableSection)にコレクションデータをバインドする機能を追加してみました。

なんちゃってデータバインドなので限定的な用途での参考程度とお考えください。

github.com

コードはGitHubを参照して頂くとして、ここでは使い方と実行イメージだけ載せておきます。

使い方

<?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:local="clr-namespace:XFApp1"
             x:Class="XFApp1.MainPage" Title="Settings" x:Name="root">
  <ContentPage.BindingContext>
    <local:MainPageViewModel />
  </ContentPage.BindingContext>
  <TableView Intent="Settings">
    <TableRoot>
      <TableSection Title="アカウント" local:CollectionBindings.ItemsSource="{Binding Accounts}">
        <local:CollectionBindings.ItemTemplate>
          <DataTemplate>
            <TextCell Text="{Binding Name}" Detail="{Binding Text}" />
          </DataTemplate>
        </local:CollectionBindings.ItemTemplate>
        <TextCell Text="アカウントを追加" Command="{Binding Source={x:Reference root}, Path=BindingContext.AddCommand}" />
      </TableSection>
    </TableRoot>
  </TableView>
</ContentPage>
public class MainPageViewModel : BindableObject
{
    public ObservableCollection<Account> Accounts { get; }

    public ICommand AddCommand { get; }

    public MainPageViewModel()
    {
        Accounts = new ObservableCollection<Account>(new[]
        {
            new Account { Name= "iCloud", Text="iCloud Drive, リマインダー, メモとその他2項目" },
            new Account { Name= "Outlook", Text="メール、連絡先、カレンダーリマインダー、メモ" },
            new Account { Name= "Gmail", Text="メール、連絡先、カレンダー" }
        });

        AddCommand = new Command(() =>
        {
            Accounts.Add(new Account { Name = "Yahoo!", Text = "メール、連絡先、カレンダー" });
        });
    }
}

public class Account
{
    public string Name { get; set; }

    public string Text { get; set; }
}

実行イメージ

f:id:yamamoWorks:20160730225601p:plain:w300  f:id:yamamoWorks:20160730225611p:plain:w300

Kaspersky 16.0.1.445 で Visual Studio 2015 が起動しなくなる場合の対処方法

原因不明で3回もOS再インストールするハメになったのでエントリー (# ゚Д゚)

Visual Studio 2015 Update 3 のパッチ KB3165756 を当てたら...が事の始まりだったのですが、実はパッチは関係なく直前にアップデートしたKasperskyが問題でした。

Visual Studio 2015を起動しようとするとCPUを30%使ったまま何時間放置しても起動せず、修復やアンインストールも同じくCPUを喰ったまま進まず、プロセスも殺せず、Windowsの再起動も出来ず、電源OFFするしかない状態に。

原因不明を探るべくOS再インストールから順に試したところ、Kaspersky 2016が犯人である事が判明しました。

Kasperskyのホームページからダウンロードした2016/7/26時点での最新バージョン 16.0.1.445 に問題があるようです。
※他マシンにインストールされているバージョン 16.0.0.614 では問題が発生していません。

なお、どのエディションのVisual Studio 2015でも起動しなくなるようです。

対処方法

Visual Studioのユーザデータを削除すると起動できるようになります!

"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" /ResetUserData

以上!

stackoverflow.com

Google Calendar Mobile Gateway 10周年

ASP.NET MVC GCMG Google カレンダー お知らせ

10年前、どんな携帯電話を使っていましたか?

今やスマートフォンが主流ですが当時はガラケーの全盛期でした。
一方、インターネットでは様々な便利なサービスが出現していましたが基本的にはパソコンのブラウザで利用するもので、携帯電話で利用できるサービスは限られていました。
Googleカレンダーもその1つです。当時は携帯電話でGoogleカレンダーの予定を見ることは出来なかったのです。(後に超使いづらいものが出てきましたが…)

その頃に自宅サーバを立ててWebアプリを作っていた私はGoogleの各種サービスがAPIを公開し始めているのを知り、Googleカレンダーの予定を参照できる携帯電話向けのWebアプリ「Google Calendar Mobile Gateway」を作り2006年7月に公開しました。

version 1 (2006/7)
f:id:yamamoWorks:20160529150344p:plain
version 2 (2007/1)
f:id:yamamoWorks:20160711234358p:plain
version 3 (2009/12)
f:id:yamamoWorks:20160711232426p:plain f:id:yamamoWorks:20160711232424p:plain

 

様々なブログや雑誌でも紹介して頂き、ピーク時(2010年4月)には月間59万PV、1万ユーザに達するなど、多くの方々に利用して頂いたことを大変嬉しく思います。また、フォーラムやブログにも沢山の感謝の言葉や様々な機種での動作報告のコメントを頂き、開発者冥利に尽きる思いでした。

 

少し技術的な面に触れておきますと、当初はASP.NET + SQL Serverの構成で自宅サーバでホストしていました。(現在はASP.NET MVC + Azure SQLをAzure仮想マシンでホスト)

当時の携帯電話のブラウザは1ページの容量が100KBまでで回線速度も遅かったので、ポストバック機構やビューステートで大量のコードをHTMLに吐き出すWebフォームは不向きでした。その為、ASP.NETなんだけどクラシックASP(Active Server Pages)っぽい作りにしていました。後にASP.NET MVCが出てきて「これだ!」と思いversion 3として作り直しました。

GoogleのAPIも初めはAtomベースのGDataでしたが数年前に廃止されJsonベースの新しいAPIに移行されていて時代の流れを感じます。

Google Calendar Mobile Gatewayは皆さんに便利なツールをお届けすると同時に、新しい技術を試す実験場でもありました。

 

スマートフォンの普及に沿うようにGoogle Calendar Mobile Gatewayのアクセス数も年々下がっていて、現在は月間6.5万PV、1500ユーザぐらいです。今のところまだ停止する予定は無いですが、そろそろ役目を終える時期が近づいているのではないかと。

しかし、10年はあっという間ですねぇ…

 

 

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

UWPではアイコンフォントがSymbol 列挙値として定義されていて簡単にアイコンフォントを表示する事ができますが、残念ながらXamarinにはこのような機能はありません。 f:id:yamamoWorks:20160702164355p:plain:w400

そこでXamarin Formsでも同じアイコンフォントを使えるようにしたいと思います。

準備

Symbol 列挙値で表示されるのはSegoe MDL2 Assetsというフォントで、Windows 10から組み込まれています。 UWPアプリの開発でハンバーガーメニューを表示する際などにもSymbolThemeFontFamilyとして用いられます。 f:id:yamamoWorks:20160702165747p:plain:w400

エクスプローラーで「C:\Windows\Fonts」を開き、フォントファイルを取り出します。他のフォルダにコピーすると分かりますがファイル名は「segmdl2.ttf」です。 f:id:yamamoWorks:20160702221833p:plain:w400


[追記]
Segoe MDL2 Assetsフォントのライセンスについて記載されたドキュメントが見つからない為、ソフトウェアに組み込んで配布可能であるか不明です。このブログ記事ではこれを許可や推奨するものではありません。


Font Awesome フォント

さて、UWPのアイコンフォントをiOSとAndroidでも表示したいというのが発端でしたが、せっかくなので他のアイコンフォントも表示してみます。 今回は「Font Awesome」のアイコンフォントを使います。ダウンロードかGitHubから「FontAwesome.otf」を入手します。

fontawesome.io

フォントの登録

UWP

「FontAwesome.otf」をUWPプロジェクトのどこかに格納しプロパティを以下に設定します。 今回はAssets配下に「Fonts」フォルダを作成して格納します。
 Assets/Fonts/FontAwesome.otf

 ビルドアクション : コンテンツ
 出力ディレクトリにコピー : コピーしない

Android

「segmdl2.ttf」と「FontAwesome.otf」をAndroidプロジェクトの「Assets」に格納しプロパティを以下に設定します。 今回は「Fonts」フォルダを作成して格納します。
 Assets/Fonts/FontAwesome.otf
 Assets/Fonts/segmdl2.ttf

 ビルドアクション : AndroidAsset
 出力ディレクトリにコピー : コピーしない

iOS

「segmdl2.ttf」と「FontAwesome.otf」をiOSプロジェクトの「Resources」に格納しプロパティを以下に設定します。 今回は「Fonts」フォルダを作成して格納します。
 Resources/Fonts/FontAwesome.otf
 Resources/Fonts/segmdl2.ttf

 ビルドアクション : BundleResource
 出力ディレクトリにコピー : コピーしない

次に「Info.plist」にフォント情報を追記します。「Info.plist」を右クリックし「ファイルを開くアプリケーションを選択」から「XMLテキストエディタ」を選択します。
「UIAppFonts」キーを作成してフォントファイルのパスをString配列で記載します。
※Resourcesフォルダからの相対パスです

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

※ダブルクリックで開くとPlistEditorが表示されますが、それだとフォント情報の登録ができません。

カスタムレンダラーの作成

AndroidではLabel.FontFamily等でフォント名を指定しても独自に追加したフォントは探してくれません。そこでカスタムレンダラーを作る必要があります。

[assembly: ExportRenderer(typeof(Label), typeof(XFApp1.Droid.CustomFontLabelRenderer))]

namespace XFApp1.Droid
{
    public class CustomFontLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var fontFamily = e.NewElement.FontFamily?.ToLower();
            if (fontFamily != null && (fontFamily.EndsWith(".otf") || fontFamily.EndsWith(".ttf")))
            {
                var lable = (TextView)Control;
                lable.Typeface = Typeface.CreateFromAsset(Forms.Context.Assets, e.NewElement.FontFamily);
            }
        }
    }
}

既存のフォントの指定と追加したフォントの指定とを区別する為、後者はファイル名で指定する仕様としました。

XAMLの記述

LabelのFontFamilyを以下のルールで記載します。

iOS
 既存のフォント : フォント名
 追加したフォント : フォント名

Android
 既存のフォント : フォント名
 追加したフォント : フォントファイルのパス
 ※Assetsフォルダからの相対パス

UWP
 既存のフォント : フォント名
 追加したフォント : フォントファイルのパス#フォント名
 ※プロジェクトフォルダからの相対パス

    <Label Text="FontAwesome" />
    <Label Text="&#xF099; &#xf230; &#xf09b; &#xf179; &#xf17b; &#xf17a;" FontSize="24">
      <Label.FontFamily>
        <OnPlatform x:TypeArguments="x:String"
                    iOS="FontAwesome"
                    Android="Fonts/FontAwesome.otf"
                    WinPhone="Assets/Fonts/FontAwesome.otf#FontAwesome">
        </OnPlatform>
      </Label.FontFamily>
    </Label>

    <Label Text="Segoe MDL2 Assets" />
    <Label Text="&#xE8FA; &#xE8E1; &#xE74E; &#xE907; &#xE909; &#xE8A3;" FontSize="24">
      <Label.FontFamily>
        <OnPlatform x:TypeArguments="x:String"
                    iOS="Segoe MDL2 Assets"
                    Android="Fonts/segmdl2.ttf"
                    WinPhone="Segoe MDL2 Assets">
        </OnPlatform>
      </Label.FontFamily>
    </Label>

※アイコンに対応する文字コードは下記を参照
Font Awesome Cheatsheet
Segoe MDL2 アイコンのガイドライン

これで各プラットフォームでアイコンフォントが表示できるようになりました。 f:id:yamamoWorks:20160702234900p:plain