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

ひだまりソケットは壊れない

id:nobuoka が情報系の話を書いたり日常の話を書いたりします

まじめなことを書くつもりでやっています。 適当なことは 「えっちなのはいけないと思います」 に書いています。

Windows Runtime Support Lib for JavaScript version 0.1.0 をリリースしました

Windows Runtime Support Lib for JavaScript (WinRSJS) の最初のバージョンである version 0.1.0 をリリースしました。

どんなライブラリか

Windows ストアアプリ、および Windows Phone アプリを JS で開発する際に使用できる便利な機能を詰め込んだライブラリです。 C# で書かれた Windows Runtime コンポーネントJavaScript (TypeScript) で書かれた便利クラス群、およびそれらの API の TypeScript 型定義ファイル (.d.ts ファイル) で構成されています。

JS からの直接の生成方法がわからない Windows Runtime 型のオブジェクトの生成を行う機能や Google Analytics のクライアントが含まれていたりします。

バージョン 0.1.0 のターゲットプラットフォームは Windows 8.1Windows Phone 8.1 です。

セットアップ方法

バージョン 0.1.0 では NuGet パッケージの配布は行っていませんので、直接 ZIP ファイルをダウンロードして展開して、ライブラリを使用するソリューションのディレクトリ中に展開して使用してください。

  1. 下記のダウンロードページから ZIP ファイルをダウンロードする。
  2. ダウンロードした ZIP ファイルを展開し、ソリューションのディレクトリに展開する。 (ソリューションの中の libs ディレクトリなどに置く。 配置場所はどこでも良い。)
  3. ライブラリを使用するプロジェクトの References に、ライブラリの WinRS.winmd ファイルを追加する。
  4. ライブラリを使用するプロジェクトの HTML ファイル (default.html ファイルなど) に 「/js/winrsjs.js」 ファイルの読み込みを追加する。
    • <script src="/js/winrsjs.js"></script> という感じ。
    • この JS ファイルはライブラリ中に PRIResource として含まれているので、別途用意する必要はない。
  5. TypeScript から使用する場合は、ライブラリ中の winrsjs.d.ts ファイルを reference path に追加する。

これでセットアップは完了です。 後は JavaScript あるいは TypeScript のコードを書いて実際に使用していきます。

将来的には NuGet パッケージも配布したいと思います。

ライブラリに含まれる機能

Windows Runtime 型のオブジェクトの生成機能

Windows Runtime 型のオブジェクトの一部は、JS から直接生成することが困難です。 例えば IMapのオブジェクトを生成することは、Windows Runtime API を使用するだけではできません *1。 そのようなオブジェクトを生成する機能があります。

IMap<K, V> オブジェクト

WinRSJS.Collections.createMap(keyType: string, valType: string) メソッドにより、IMap<K, V> オブジェクトを生成できます。 Key および Value の型は、文字列によって C# での型を指定する必要があります。

//  IMap<string, string> オブジェクトを生成。 (C# において String 型は System.String。)
var map = <Windows.Foundation.Collections.IMap<string, string>>WinRSJS.Collections.createMap("System.String", "System.String");
Guid オブジェクト

WinRSJS.Guid.newGuid() メソッドにより、Guid 型の値を生成できます。 Windows Runtime の Guid 型は JS では string 型で現されるので、実際に JS で扱う際は文字列となります。

UUID (GUID) を JS で生成したい場合に使用できます。

// 文字列の形式は "12345678-abcd-abcd-abcd-123456789abc" というハイフン区切りの 16 進数表記。
var guid: string = WinRSJS.Guid.newGuid();

このメソッドは、Guid.NewGuid() メソッドのラッパーです。

HResult に関する機能

Windows Runtime API 内部でエラーが発生して HResult 値が返された場合、JS 側には例外として WinRTError オブジェクトが届きます。 そして、WinRTError#number プロパティを見ることで HResult 値を得ることができます。

一般的に、HResult 値は 16 進数文字列で表示されますが、WinRTError#number プロパティには値を符号付 32 ビット整数値として解釈された数値が入っています。 ユーザーに表示する場合 (HResult 値をユーザーに表示するのがいいかどうかは謎ですが)、HResult 値を符号無 32 ビット整数値として解釈して 16 進数文字列に変換して表示すべきです。 そのための関数がライブラリに含まれています。

if (err instanceof WinRTError) {
    var hresultHexStr = WinRSJS.HResults.convertHResultStyleFromInt32To8DigitHexStr(err.number);
    // 必要に応じて hresultHexStr をユーザーに表示。
}

HTTP 通信に関する機能

application/x-www-form-urlencoded 形式の POST リクエストを投げるための機能も含まれています。 WinRSJS.HttpUtils.postWwwFormUrlEncodedContent(uriStr: string, data: { [key: string]: string; }) メソッドです。

Google Analytics のクライアント

Google Analytics のクライアントとして WinRSJS.GoogleAnalytics.GAClient クラスが含まれています。

var gaClient = new WinRSJS.GoogleAnalytics.GAClient("tracking_id", "app_name", "app_version", "client_id");
// Send Appview.
gaClient.sendAppview("screen_name");
// Send Event.
gaClient.sendEvent("category", "action", "label");

是非ご利用ください

最近 MADOSMA がリリースされましたしもうすぐ Windows 10 がリリースされて Windows ストアアプリも便利になりますし、この機会に Windows / Windows Phone アプリを開発してみようという方は是非ご利用ください!

フィードバック等頂けると嬉しいです。

*1:いろいろ調べた結果できないという結論に達しましたが、もしかしたら実はできるのかもしれません。

WinJS 4.0 では HTML コントロールにスタイルを当てるためにクラスを明示的に指定する必要がある

WinJS 4.0 がリリースされましたね!! めでたい!

2015 年 6 月 14 日時点では、Try WinJS のダウンロードページのリンク先がバージョン 4.0.0 になっていますが、最新バージョンは 4.0.1 です。 (Windows Phone 10 におけるバグの修正がされたようです。)

私も Windows ストアアプリ開発で使用している WinJS のバージョンを 4.0 にしてみましたが、便利なコントロールがいくつか追加されていたり、見た目がかっこよくなっていたりして良いです。 ロードマップを見たところ、バージョン 4.1 のリリースは Windows 10 リリースと同時とのことなので、Windows ユニバーサルアプリ開発には WinJS 4.1 を使えそうです。 UX に磨きがかけられるとのことなので、それも今から楽しみですね。

WinJS 4.0 での変更点

WinJS 4.0 では、API の変更も UI/UX の変更もいろいろと行われています。 詳細は GitHub 上の Changelog を見ましょう。

スタイルの適用方法の変更

変更点のうちの 1 つ、スタイルの適用方法についてこのエントリでは紹介します。

Styling

Styling of intrinsic elements is no longer by default

Changelog · winjs/winjs Wiki · GitHub

Changelog に書かれているように、HTML の通常の要素にはデフォルトでは WinJS のスタイルが当たらないように変更されました。 WinJS 3.0 では、例えば button 要素そのものに WinJS 用のスタイルが当たるように CSS が書かれていましたが、WinJS 4.0 では button 要素を単に書くだけでは WinJS のスタイルは適用されません。 WinJS のスタイルを適用したい場合には、明示的にクラスを指定する必要があります。

ボタンの場合は、win-button クラスです。

<button class="win-button">WinJS のスタイルが適用されたボタン</button>
<button>WinJS のスタイルは適用されないボタン</button>

詳細は次のページに書かれています。

ボタンのようにスタイルが当たっていないことがすぐにわかるものは動作確認時に気づきやすいのですが良いのですが、h1 要素や progress 要素など、スタイルが当たっているのかどうか動作確認時に見てもわかりづらいものもあるので注意しましょう。 body 要素に win-type-body クラスを付与することが推奨されている のも移行当初は見落としがちなので気を付けたいですね。 win-button-primary の存在も見落としやすそうです。

Android の Canvas#saveLayer メソッドと xfermode について

Android アプリ開発に関して Canvas クラスの saveLayer メソッドPaint の xfermode について調べたのでまとめておきます。

Canvas#saveLayer メソッド

saveLayer メソッドのドキュメントには、『This behaves the same as save(), but in addition it allocates and redirects drawing to an offscreen bitmap.』 と書かれています。

  • save メソッドと基本的には同じ。
  • 異なる点は、キャンバス外 (offscreen) のビットマップを用意し、以降の描画処理をそちらにリダイレクトするようにする、ということ。

save メソッドは何をするのか

じゃあ save メソッドは何をするのか調べましょう。 save メソッドのドキュメントには、『Saves the current matrix and clip onto a private stack. Subsequent calls to translate, scale, rotate, skew, concat or clipRect, clipPath will all operate as usual, but when the balancing call to restore() is made, those calls will be forgotten, and the settings that existed before the save() will be reinstated.』 と書かれています。

  • 呼びだし時点の座標変換行列とクリッピングの設定を保存する。
  • それ以降に、座標変換行列を変化させるメソッド (translatescale など) やクリッピング設定を変化させるメソッド (clipRectclipPath) が呼ばれると通常通り適用される。
  • 対応する restore メソッドが呼ばれると save メソッド呼び出し前の状態に戻される。

つまり、描画される位置を決定するための情報が保存され、あとから復元することができるようになる、という感じですね。 描画されているビットマップの情報が保存されるわけではないので注意しましょう。

ちなみに、save(int) メソッドを使い、引数として Canvas.MATRIX_SAVE_FLAGCanvas.CLIP_SAVE_FLAG を渡すことで、保存・復元の対象を座標変換行列だけにしたり、クリッピングの設定だけにしたりできます。 (が、両方を保存・復元の対象にする方が単純で速いので、できるだけ使わない方がいいみたいです。)

何度も save した後、指定のところまで一気に復元する

save メソッドを何度も呼び出した場合、restore メソッドを同じ回数呼び出すことで元の状態に戻せます。 しかし何度も呼ぶのは面倒ですね。 save 時の返り値を保持しておき、復元時にその値を restoreToCount メソッドに渡すと、その状態まで一気に復元できます。

// 返り値を保持しておく。
final int sc = canvas.save(); // 位置 A

/* ... ここで何度も save メソッドを呼ぶ。 ... */

// 途中で何度 save メソッドを呼んでいたとしても、位置 A のときの状態まで復元される。
canvas.restoreToCount(sc);

キャンバス外のビットマップに描かれたものはどこへ?

さて、saveLayer メソッドの話に戻りましょう。 save レイヤーと同じように座標変換行列とクリッピングの設定の保存ができることについては説明は不要だと思います。

問題は、saveLayer メソッド呼び出し後の描画がキャンバス外のビットマップにリダイレクトされて、最終的にそのビットマップに描かれたものがどうなるのか、です。 メソッドの説明には次のように書かれています。

Only when the balancing call to restore() is made, is that offscreen buffer drawn back to the current target of the Canvas (either the screen, it's target Bitmap, or the previous layer).

restore メソッドの呼び出しが行われて初めてターゲットとなるキャンバス (スクリーンだったり、Bitmap だったり、より前に作られたレイヤーだったりする) に描き戻されるわけですね!

引数の Paint オブジェクトの属性は描き戻し時に適用される

saveLayer メソッドは引数として Paint オブジェクトを受け取ります。 この Paint オブジェクトの属性のアルファ値と xfermode、そして ColorFilterstore メソッドが呼ばれて描き戻される際に適用されます。 Xfermode については下で説明します。

Xfermode と PorterDuffXfermode

Xfermode について

Xfermode クラスのドキュメントには 『Xfermode is the base class for objects that are called to implement custom "transfer-modes" in the drawing pipeline.』 と書かれています。 「xfermode」 は transfer-mode を表すみたいですね。 (これが世間一般での命名なのか Android の世界だけの命名なのかよくわかりません。)

ちゃんとドキュメントには書かれていませんが、2 つの画像を合成する場合や、既に何かが描かれているところに新たに描画する際に、どのように合成するのかを表すもののようです。

PorterDuffXfermode

昔 Porter さんと Duff さんが画像合成の 12 通りのルールを論文にしたそうで、それが Porter-Duff ルールと呼ばれているそうです。

具体的にどういうものかは上のページを見るとわかりやすいです。 例となる画像があります。

これらのルールは、Android SDK では PorterDuffXfermode クラスで表現されます。 (Android SDK では 12 通り以上のモードが定義されているので、Porter-Duff ルールに含まれないものも入っているのかも?)

例えば、次のページに書かれているように PorterDuff.Mode.CLEAR を使うことで対象となるキャンバスの内容 (destination) も描くもの (source) も両方消去することができます (つまり消しゴムにできる)。

ちなみに、ImageView#onDraw メソッドなどに渡されてくる Canvas オブジェクトは背景が透明ではないので、透明にしようとしても透明にならないようです。 (真っ黒になった。)

saveLayer メソッドと xfermode のサンプルコード

赤い四角をもとのキャンバス (destination) に描き、青い円を新しく用意したレイヤー (source) に描き、Porter-Duff の Overlay モードで合成するサンプルコードです。

// 必要な import 文。
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;

// 使用する Paint オブジェクトの用意。
Paint redPaint = new Paint();
redPaint.setAntiAlias(true);
redPaint.setColor(getResources().getColor(android.R.color.holo_red_light));
Paint bluePaint = new Paint();
bluePaint.setAntiAlias(true);
bluePaint.setColor(getResources().getColor(android.R.color.holo_blue_bright));
Paint xfermodePaint = new Paint();
xfermodePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY));
// Canvas 用意。
Bitmap bm = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
bm.setDensity(DisplayMetrics.DENSITY_XXHIGH);
Canvas c = new Canvas(bm);
// 四角を描画。
c.drawRect(0, 0, 210, 210, redPaint);
// 新しいレイヤーを準備。
int sc = c.saveLayer(null, xfermodePaint, Canvas.CLIP_TO_LAYER_SAVE_FLAG);
// 新しいレイヤーに円を描画。
c.drawCircle(180, 180, 120, bluePaint);
// 新しいレイヤーに描かれたものを描き戻す。
// (saveLayer 時に指定した xfermodePaint の xfermode である Porter-Duff の OVERLAY モードで。)
c.restoreToCount(sc);
// 表示してみる。 (imageView は android.widget.ImageView オブジェクト。)
imageView.setImageBitmap(bm);

結果は以下の画像のようになります。

f:id:nobuoka:20150612002459p:plain

DST_ATOPSRC_ATOPSRC_OUT などを使うとマスク処理みたいなこともできますし、面白いですね。

備考

  • Xfermode を指定して画像の合成をする方法はいろいろあって、Canvas#saveLayer メソッドを使う必要は必ずしもありません。

Windows Runtime の IMap<K, V> インターフェイスを実装したオブジェクトを JS で使用する (ワークアラウンド)

Windows Runtime の API を JS から使用する際に問題となることの一つとして、JS ではインターフェイスを扱うことができない、というものがあります。 Windows Runtime の API には、引数として IIterable<IKeyValuePair<string, string>> オブジェクトや IMap<string, string> オブジェクトを取るものがありますが、そういったインターフェイスを実装するオブジェクトを JS から作成するのは Windows Runtime API を使うだけでは難しそうでした。 *1

しょうがないので、ワークアラウンドとして C# などで独自の Windows Runtime コンポーネントを生成するという方法を採ります。

具体的な問題の例

例えば、Windows.Web.Http.HttpFormUrlEncodedContent クラスインスタンスを生成したいとします。 HttpFormUrlEncodedContent のコンストラクタは、Windows.Foundation.Collections.IIterable<IKeyValuePair<string, string>> オブジェクトを引数に取ります。

なので、JS で IIterable<IKeyValuePair<string, string>> オブジェクトを生成できれば良いのですが、その方法が見つかりませんでした。 下記フォーラムで質問を見つけましたが、ちゃんとした解決には至っていませんでした。

ちなみに

ちなみに IIterable<IKeyValuePair<string, Object>> というインターフェイスIMap<string, Object> というインターフェイスを実装したオブジェクトが欲しい場合は PropertySet クラスを使うと良いです。

ワークアラウンド : 独自の Windows Runtime コンポーネントを生成して JS から使用する

Windows.Foundation.Collections.IMap<K, V> インターフェイスIIterable<IKeyValuePair<K, V>> インターフェイスを継承しているので、ここでは IMap<K, V> オブジェクトを返すことを考えてみます。 言語は C# を使います。 (C++VB でも可能なはずです。)

まずはカスタムコンポーネント用のプロジェクトを作成します。 次のエントリが参考になります。

プロジェクトができたら、C# でクラスを作成します。 次のような感じです。 下の例は IMap<string, string> オブジェクトを返すメソッドの例です。 (IMap<K, V> インターフェイスC# では System.Collections.Generic.IDictionary<K, V らしい。)

<feff>using System;
using System.Collections.Generic;

namespace YourNameSpace
{
    public sealed class CollectionsUtil
    {
        /// <summary>
        /// <code>Windows.Foundation.Collections.IMap&lt;string, string></code> オブジェクトを返す。
        /// JS でこのインターフェイスを実装したオブジェクトを生成する方法がわからないので苦肉の策。
        /// </summary>
        public static IDictionary<string, string> CreateStringMap()
        {
            return new Dictionary<string, string>();
        }
    }
}

System.Type を使って JS 側から文字列で型を指定してオブジェクトを生成させるとかもできそうだけど今のところ必要ないのでそこまではやってません。

どう考えてもおかしい

こんなことしないといけないのはどう考えてもおかしいのでもっといい方法があるはず……!!

*1:もしかしたら方法があるのかもしれませんが、見つけられませんでした。

ストアアプリのターゲットを Windows 8 から 8.1 に変更する際は Flexbox レイアウトの CSS の変更に注意

前置き : ストアアプリにおける可変ボックス (Flexbox) レイアウト

Windows ストアアプリを HTML + JS で構築する際のレイアウトの選択肢は以下のページにいろいろと挙げられています。

そのうちの一つに可変ボックスレイアウトがあります。 CSS Flexible Box Layout Module を使うというものです。

可変ボックスレイアウトの Windows 8 プラットフォームと 8.1 プラットフォームでの違い

Windows 8 プラットフォーム向けのストアアプリでは、2012 年 3 月 22 日版の 「Flexbox」 草案に基づいた実装になっているようです。

仕様が古く、また、-ms- ベンダプレフィクスが必要 *1 です。

Windows 8.1 プラットフォーム向けのストアアプリでは、2012 年 9 月の勧告候補に基づいた実装になっているようです。 おそらくですが。

さて、Windows 8 プラットフォーム向けに記述したベンダプレフィクス付きの CSS プロパティや値は、Windows 8.1 プラットフォームでは対応するベンダプレフィクス無しのものとして解釈されるようです。 例えば以下のようになりました。

  • display: -ms-flexbox;display: flex; として扱われ、
  • -ms-flex: 0 0 auto;flex: 0 0 auto; として扱われる。

よって、基本的には Windows 8 プラットフォーム向けに書いた CSS はそのまま Windows 8.1 プラットフォームでも同様に解釈されるのですが、一部仕様が変わっている部分があるので注意が必要です。

flex (-ms-flex) プロパティの初期値の変更

例えば、Windows 8 プラットフォームにおいて -ms-flex プロパティの初期値は none (0 0 auto と同等) でした。 一方、Windows 8.1 プラットフォームにおいて flex プロパティの初期値は 0 1 auto に変更されました。

そのため、次のような HTML を書いていた場合、ターゲットが Windows 8Windows 8.1 かで表示がかわってしまいます。

<div style="display: -ms-flexbox; -ms-flex-direction: column; height: 300px; width: 300px">
    <div><!-- ← Windows 8 プラットフォームでは `-ms-flex: none;` 相当。 8.1 では `flex: 0 1 auto;` 相当。 -->
        <div style="height: 200px; background-color: rgba(200, 200, 0, 0.5);">1</div>
    </div>
    <div style="-ms-flex: 1 1 auto;">
        <div style="height: 200px; background-color: rgba(0, 200, 200, 0.5);">2</div>
    </div>
</div>

実際にそれぞれのプラットフォームでどのような表示になるかは次の図をご覧ください。 (ボーダーなど一部の CSS は別に指定しています。)

f:id:nobuoka:20150506172113p:plain

初期値の変更ということで、明示的に -ms-flex プロパティの値を指定していた場合は影響がないですが、省略していた場合はターゲットを Windows 8 から 8.1 に変更した際に影響があるかもしれません。

他の CSS プロパティについて Windows 8 と 8.1 で変更があったものがあるかどうかは調べていませんが、他にもあるかもしれません。

ストアアプリのターゲットを Windows 8 から 8.1 に変更する際の参考文献

ストアアプリのターゲットを Windows 8 から 8.1 に変更する際に下記ページ群が参考になります。 ただし、今回の可変ボックスレイアウト周りの変更については触れられていなかったりするので気を付けましょう!

*1:Windows 8.1 上で Windows 8 プラットフォーム向けにビルドしたストアアプリを動かしたところベンダプレフィクスがなくても動きましたが、多分 Windows 8 だと必要な気がします。