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

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

gif-writer (GifWriter.js) version 0.9.3 をリリースしました

2013 年にリリースした GIF Encoder のライブラリ “GifWriter.js” を npm モジュールにして “gif-writer” としてリリースしました。 バージョンは 0.9.3 です。 *1

変更内容としては以下のとおりです。

  • 各ファイルをモジュールに変更。
  • パッケージに TypeScript の型定義ファイル (.d.ts ファイル) を含めるように。

npm モジュールに型定義を含める話

TypeScript 1.6 からモジュール名解決を npm モジュールから行う機能が入りました。

今回のリリースでは、npm モジュールに TypeScript の型定義ファイルを含めています。 よって、TypeScript で書いているプロジェクトで “gif-writer” モジュールを使う場合、単に以下のように import するだけで型定義が有効になります。

import * as gw from "gif-writer";

これは便利!!

*1:バージョン 0.9.0 と 0.9.1 はパッケージングに失敗したもので、0.9.2 と 0.9.3 の差分は README のみです。

「DroidKaigi 2016」 と 「関モバ #11」 で発表しました

「DroidKaigi 2016」 にて 『5 年続く 「はてなブックマーク」 アプリを継続開発する技術』 というタイトルで、また 「関モバ #11」 で 『Components­Recycler­Adapter — RecyclerView で複数の view type や複雑なデータ構造を扱う』 というタイトルで、それぞれ発表しました。

DroidKaigi 2016

5 年前に最初のリリースが行われたアプリを継続的に開発しており、そのための取り組みを共有しました。

要点をまとめると以下のようになります。

  • テストは Testing Support Library に頼っている。
    • 最近は Android の標準のテストフレームワークが充実してきている。
    • まだほとんどテストを書いていないというプロジェクトでは、まずは標準のテストフレームワークを使い始めると良いと思う。
    • 今回の DroidKaigi でも、テストに関する多数のセッションがあり、参考になる。
  • CI については、社内の Jenkins サーバー上でテストの実行やリリースパッケージ作成の自動化を行っている。
    • 各種タスクを Gradle タスクとして定義し、タスクの実行をシェルスクリプトで記述。
    • 最近は Jenkins Workflow plugin への移行を進めている。 (Groovy での DSL が書きやすいとか、リリースパッケージのアップロード処理の前に人の確認を挟みやすいなどの利点のため。)
  • Preview 版 (production flavor の preview) を定義して、開発中の機能を preview 版で有効に。
    • 長い期間の開発でも他ブランチとのコンフリクトをしづらく & チーム内配布で確認しやすく。
  • Annotations Support Library を活用。
  • 必要に応じてライブラリ的なものを作る。
    • 例えば 「関モバ #11」 で発表した ComponentsRecyclerAdapter など。

Preview 版について補足

開発中の機能を Preview 版 (product flavor の preview) でのみ有効にする、という話がスライド中に出てきます。 これについて、具体的な方法がわからないということで質問いただきました。 (src/preview/java 下に preview 版専用のソースコードを書くとすると面倒くさすぎるのでは? という。)

具体的には、ソースコードは全て src/main/java 下に置いて、

  • build.gradle ファイル内で buildConfigField を使って preview 版専用の機能を有効にするかどうかの値を持ったフィールドを BuildConfig に生成し、
  • Java のコード中で BuildConfig のフィールドを見て if で条件分岐して機能を有効にしたり無効にしたりする

という感じです。

関西モバイルアプリ研究会 #11


ComponentsRecyclerAdapter を用いて複数の view type や複雑なデータ構造を扱う

複数の item view や複数のデータセットによる複雑なデータ構造を扱いやすくする RecyclerView.Adapter のサブクラス ComponentsRecyclerAdapter の紹介です。

Android の Drawable への着色について (Drawable tinting)

Android APIs を眺めていると “tint” って単語が良く出てきます (“android:backgroundTint” とか “android:progressTint” とか) が、tint についてあんまり日本語の情報がなかったのでまとめてみます。

ちなみに “tint” の英語としての意味は、名詞で 「色合い」 とか 「染料」、動詞だと 「色を付ける」 や 「染める」 などです。

このエントリの内容

  • API level 21 から使えるようになった Drawable への色付け (Drawable tinting) について説明します。
    • アルファマスクとして Drawable を定義し、表示時に色を指定するための機能です。
  • View の背景の Drawable への着色のための、あるいは ProgressBarImageView などで表示される Drawable への着色のためのメソッドXML の属性を紹介します。
  • これらの機能を API level 21 未満でも使用できるようにするための Android Support Library が提供するメソッドを紹介します。

Drawable への着色 (Drawable tinting)

API level 21 から、Drawable リソースをアルファマスクとして定義し、その Drawable に色を付けて表示する、ということができるようになりました。 同じ形で異なる色のアイコンを複数個用意したいというような場面で便利ですね。

Android 5.0(API レベル 21)以降では、アルファ マスクとして定義されたビットマップや 9-patch に色を付けることができます。 着色には、カラーリソースまたはカラーリソースに変換されるテーマ属性を使用できます(?android:attr/colorPrimary など)。

ドローアブルの使用 | Android Developers

bitmapあるいはnine-patcheをアルファマスクとして定義しcolorリソースやテーマの属性で色づけする

L Developer Preview マテリアルデザイン - アニメーション - Firespeed

Drawable クラスに、下記のメソッドが追加されています。

これらを使うことで、Drawable に着色することができます。

// 通常通り Drawable オブジェクトを取得し、#setTint メソッドや #setTintMode メソッドを呼ぶことで着色。
Drawable d = getDrawable(R.drawable.example);
d.setTint(ContextCompat.getColor(this, R.color.exampleTint));
d.setTintMode(PorterDuff.Mode.SRC_IN);

// 後はこの Drawable オブジェクトを使う。
imageView.setDrawable(d); // 例えば ImageView で表示する。

ちなみに、この機能が導入される前から、同様のことが Drawable#setColorFilter メソッド で実現できていたようです。 setColorFilter を呼んだ場合は、tint の設定は上書きされます。

Tint mode について

Drawable#setTintMode(PoterDuff.Mode) メソッドでは、元の Drawable と Tint の色をどのように混ぜるかを Porter-Duff のルールで指定します。 元の Drawable が destination で、着色する色が source です。

デフォルトでは Source In Destination 規則 (PorterDuff.Mode.SRC_IN) が使用されます。 これは、単純に tint が不透明な単色の場合、合成後の各ピクセルの不透明度は元の Drawable の不透明度になり、合成後の各ピクセルの色は tint の色になるというものです。 まさに Drawable がアルファマスクになるわけです。

Porter-Duff のルールについては以前このブログに書きました。 下記エントリを参照してください。

Support library による Drawable への着色のサポート

API level 21 から導入された Drawable への着色の機能ですが、v4 Support Library に含まれる DrawableCompat クラスを使うことで、古い API level でも同様の機能を実現できます。

Drawable d = DrawableCompat.wrap(ContextCompat.getDrawable(this, R.drawable.example));
DrawableCompat.setTint(d, ContextCompat.getColor(this, R.color.exampleTint));
DrawableCompat.setTintMode(d, PorterDuff.Mode.SRC_IN);

対象の Drawable を DrawableCompat#wrap メソッドに渡して tint 対応の Drawable に変換し、それを DrawableCompat#setTint メソッドなどに渡す、という使い方になります。

Drawable への着色の応用

実際に Drawable への着色をしたい場面というのは、例えば View の背景に Drawable をセットしてその Drawable に着色するだとか、ImageView で表示する Drawable に着色するだとか、あるいは ProgressBar の (プログレスバーそのものを表す) Drawable に着色するだとか、そういう場面が多いでしょう。 そのような場面で簡単に Drawable への着色が行えるように、Java クラスのメソッドやレイアウトの XML で使える属性などが用意されています。

各 view の背景の Drawable への着色 (Background tint)

API level 21 で導入された下記のメソッドXML 属性を使用して、簡単に背景の Drawable に着色できます。

API level 21 未満の環境でこれらの機能が使えるように、v7 appcompat library が各種 compatible widget を提供しています。 (ただし、対応されている View のクラスは一部のみ。) よって、v7 appcompat library を使うことで、以下のようにして API level 21 未満の環境でも background tint を使用できます。

<!-- v7 appcompat library を使用していると、この TextView はインフレート時に AppCompatTextView に変換される。 -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/example_text_view_background"
    app:backgroundTint="@color/exampleTextViewBackgroundTint"/>

ただ、上のような書き方をすると Android Studio でエラーが表示されるのが悩ましいところです。 (AppCompatTextView への変換まで認識してくれないため。)

f:id:nobuoka:20160213200931p:plain

v7 appcompat library の compatible widget については以前エントリを書きましたので、詳しくは下記エントリを参照してください。

その他の Drawable の着色

ImageView には、表示する Drawable に着色するためのメソッドが存在します。

ProgressBar でも Drawable への着色が可能です。

調べられていませんが、Drawable を扱う他の View クラスにも似たような機能があるかもしれません。

API level 21 未満でこのような機能を使えるようにする互換クラスは (今のところ) Android Support Library には含まれていないようですので、API level 21 未満では直接 Drawable に着色したうえで ImageView にセットしたり ProgressBar にセットしたりする必要がありそうです。

まとめ

  • API level 21 から Drawable への色付けがサポートされました。 (Drawable tinting)
    • 基本的には、アルファマスクとして Drawable を定義し、表示時に色を指定するという使い方が想定されています。
  • View の背景の Drawable への着色や、ProgressBarImageView などで表示される Drawable への着色も簡単に行えるようになっています。
  • Android Support Library を使うことで、Drawable への着色の機能や View の背景の Drawable への着色が API level 21 未満でも利用できます。

関連ページ

【v7 appcompat library を読む】 レイアウト XML のインフレート時に各種 view が compatible widget に変換される仕組み

Android アプリ開発時にお世話になる v7 appcompat library と LayoutInflater の話です。

この記事の内容は、v7 appcompat library のバージョン 23.1.1 をもとに記述しました。

v7 appcompat library の compatible widget

v7 appcompat library には、AppCompatTextView などの compatible widget *1 がいくつか含まれています。 いくつか下に挙げてみます。

これらのクラスは、新しい API level で導入された機能の一部を古いプラットフォームにも提供してくれます。 クラスによって提供される機能は違うものの、主には widget tinting 周りの機能 (+α) が提供されると考えて良さそうです。

appcompat ライブラリによる widget tinting については下のブログエントリを参照してください。

インフレート時に自動的に変換される

各 compatible widget のドキュメントを読むと、以下のような説明が書かれています。 (以下は AppCompatTextView のもの。)

This will automatically be used when you use TextView in your layouts. You should only need to manually use this class when writing custom views.

AppCompatTextView | Android Developers

つまり、レイアウト XML 中に <TextView ...> って書いておけば、インフレート時に自動的に AppCompatTextView になる、ってことですね。 便利です。

インフレート時に自動で変換される仕組み

便利なのはいいけどどういう仕組みなのかわかっておかないと嵌ったりすることもあるので、どういう仕組みなのか調べてみました。

LayoutInflater に Factory をセットすることでインフレート時の挙動を変更できる

まずは LayoutInflater について調べてみます。 インフレート時に XML ファイル中の要素名を各 view のクラスに変換する処理は、Factory (LayoutInflater.Factory/LayoutInflater.Factory2 オブジェクト) が担っているようです。 下記メソッドを見てみると、ドキュメントにいろいろ書かれています。

AppCompatActivityLayoutInflater に Factory をセットしている

AppComaptActivity#onCreate メソッドの中を見ると、以下のように AppCompatDelegate#installViewFactory() メソッドを呼んでいます。

getDelegate().installViewFactory();

ドキュメントには 『Installs AppCompat's LayoutInflater Factory so that it can replace the framework widgets with compatible tinted versions』 と書かれていて、このメソッドLayoutInflater に独自の Factory をセットすることがわかります。 実装を追っていくと、このメソッドの中では LayoutInflaterCompat.setFactory メソッドが呼ばれていました。

まとめ

上で調べたように、AppCompatActivityLayoutInflater に独自の Factory をセットすることで、インフレート時の自動変換が実現されています。 よって、インフレーション時については以下のようにまとめられます。

  • AppCompatActivity で使う限り、LayoutInflater#from(Context) メソッドで取得できる LayoutInflater でのインフレート時に compatible widget への自動変換がはたらくと考えて良い。
  • AppCompatActivity を使わないのであれば、AppCompatDelegate#installViewFactory メソッドを使うことで使うことで同等の機能が実現される。

インフレーション時以外に何か良しなに変換してくれたりはしないので、以下のことにも気を付けましょう。 (appcompat ライブラリを使っている環境での話です。)

  • 独自 view のクラスを定義する場合は、compatible widget のクラスを拡張する。
    • 例えば TextView のサブクラス定義したい場合は、TextView ではなく AppCompatTextView のサブクラスにする。
    • そうしないと background tinting が有効にならないという問題が起こる。
  • 直接コンストラクタを呼んで view を生成したい場合は、compatible widgetコンストラクタを呼んで compatible widget を生成する。
    • 例えば、TextView オブジェクトを生成したい場合は、AppCompatTextViewコンストラクタを呼ぶ。
    • LayoutInflater でインフレートしたものは background tint を設定できるのに、直接コンストラクタを呼ぶと設定できない」 という問題を回避するため。

*1:『compatible widget』 という表現は、AppCompatDelegate#installViewFactory() メソッドのドキュメントから。

UWP アプリ開発に TypeScript + React を導入することの検討 (Node.MSBuild.Npm の紹介)

こんにちは! 株式会社はてなにて、主に 「はてなブックマークAndroid アプリの開発を行っている id:nobuoka です。 この記事は、「はてなデベロッパーアドベントカレンダー 2015」 の 14 日目の記事です。 昨日は id:hatz48 による 「TypeScript だけで Web アプリケーションを作る」 でした。

今日は、昨日に引き続き TypeScript の話題となります。 主にクライアントサイド (特に UWP アプリ) での TypeScript と React の組み合わせについて検討したいと思います。

(この記事の内容は、UWP アプリへの導入を目的としたときの TypeScript + React の環境の一例であり、ベストプラクティスではありません。 より良い方法などがありましたら教えてくださいませ。)

UWP アプリと TypeScript + React

UWP アプリとは 「Universal Windows Platform アプリ」 のことで、Windows 10 で導入された Windows プラットフォーム向けのアプリの一種です。 Windows 8 で導入された Windows ストアアプリや Windows Phone 8.1 向けの Windows Phone アプリの進化版という感じですね。

現在のところ株式会社はてなでは UWP アプリの開発は行っていませんが、Windows ストアアプリ 「はてなブックマーク」 を 2012 年にリリースしています。 *1

JS を快適に書く : TypeScript の導入

Windows ストアアプリや Windows Phone アプリと同様に、UWP アプリも JS + HTML + CSS で開発することができます。 Windows ストアアプリ 「はてなブックマーク」 も JS + HTML + CSS の組み合わせで書かれています。

JS は Web 開発者にとって馴染みのある言語ですので、アプリ開発に取り掛かりやすいというのは利点ですね。 一方で、素の JS には変数に型がないためリファクタリングなどがしづらいという難があります *2。 それを解決するため、いわゆる Alt JS を使用することが検討されると思いますが、UWP アプリ開発で使用するのであればまず TypeScript が候補にあがるでしょう。 UWP アプリ開発に使用される統合開発環境 (IDE) Visual Studio 2015 では標準で TypeScript がサポートされているため、UWP アプリのプロジェクトに容易に TypeScript を導入できます。 Microsoft が TypeScript のリリースを行っているあたりも TypeScript 導入にあたっての安心感につながっています。 よほど TypeScript と比べて有利な言語があればそれを使うとよいかもしれませんが、私自身は現状では TypeScript を使うのが一番だと考えています。

実際に 「はてなブックマークWindows ストアアプリ *3 でも TypeScript を導入して開発しており、素の JS を使うのに比べて楽に開発を進めることができています。

View の処理を快適にするために : React の導入を検討

TypeScript を導入することで JS の変数に型付けが行われて IDE のサポートを受けやすくなり、JS の世界については快適になります。 しかしながら TypeScript を導入するだけでは view 操作 (「何らかのデータを画面に表示する」 という処理) の部分はあまり改善されません。 UWP アプリ開発のための JS のライブラリとして WinJS というものがあり、画面にデータを表示するためのテンプレート・バインディングの機能が提供されているのですが、これがなかなか素朴な仕様で、型の恩恵を受けづらいためです。

はてなブックマークWindows ストアアプリの開発でも、やはり View を扱う部分がきれいに書けず、よりよい方法を取り入れたいと考えています。

そこで、何らかのライブラリを導入することを検討しました。 候補としては、以下のものを考えました。 いずれも JS 界隈で話題になりやすいライブラリですね。

  • React
    • JSX 構文を使って JS 側に HTML 構造を記述する。
    • TypeScript との相性が良い。
    • アプリ開発をするうえで扱いやすそう。
  • Polymer
    • 標準の Web Components を意識して開発されており、将来性はあるように見える。
    • 独自の HTML タグを定義して HTML 側で使用できるので、(React と比べて) サーバーサイドで HTML を生成するような場面で扱いやすそう。
    • TypeScript との相性は不明。
  • AngularJS
    • 現在 Angular 2 が開発されており、バージョン 1 と 2 の互換性がなくなりそうなことから、現在 Angular を導入するのはリスキーだと考えられる。
    • View 部分を手軽に扱うためだけに導入するには規模が大きいように感じる。

全部試してみてどれが良いか評価できればよかったのですが、まずは TypeScript と相性がよく、アプリ開発で扱いやすそうな React だけを試してみました。

TypeScript と React (JSX) の相性

React では JSX 構文 (JSX syntax) を用いて JS の中に DOM 構造を記述します。 そして、TypeScript では JSX 構文がサポートされています。

JSX 構文での型チェックなどもサポートされているので、React を使うことで View の処理の部分でも IDE のサポートが受けられやすくなります。 プラグインなどではなく TypeScript の処理系に JSX 構文の処理が組み込まれているので、TypeScript と JSX の相性は非常に良いといえます。

TypeScript + React の環境

さて、ここからは実際に TypeScript + React を UWP アプリ開発で用いるための環境について考えます。 UWP アプリは、ローカルファイル上の HTML + JS + CSS をブラウザ上に表示するのと似たような仕組みで動きますので、まずはローカルファイルシステム上でビルドしてブラウザ上に表示できるようにする環境を考えます。

TypeScript + React の環境を整えるためには、npm を使用するのが簡単でしょう。

React を使うにあたっては CommonJS モジュールシステムを使うことが推奨されていますので、TypeScript を記述する際はモジュール形式で記述し、CommonJS 形式でコンパイルするようにしました。

We recommend using React with a CommonJS module system like browserify or webpack. Use the react and react-dom npm packages.

Getting Started | React

コンパイルした JS をブラウザ上で実行するために、また、UWP アプリの JS ファイルとして使用するためには、webpackBrowerify などを使用してパックしてやる必要があります。 今回は私は webpack を使用してみました。 (webpack を選択した理由は特にありません。 同僚からは、「webpack は大艦巨砲という感じなので、Browserify で事足りるなら Browserify で良さそう」 という意見をもらいました。)

これらのツールを使用して TypeScript + React のビルドを行うようにしたサンプルプロジェクトを GitHub で公開しています。 (ここではビルドシステムとして Gradle を使用しています。)

UWP アプリのプロジェクトへの TypeScript + React 開発環境の導入 : Node.MSBuild.Npm の紹介

さて、最後に UWP アプリのプロジェクトに上述の環境を構築することを考えましょう。

上で紹介した Gradle を使ったプロジェクトでは、Gradle から Node.js と npm を使用するために Gradle Plugin for Node を使用しています。 このプラグインは、ビルド時に Node.js と npm をダウンロードしてきて、セットアップしてくれるというものです。

一般的な UWP アプリのプロジェクトのビルドシステムである MSBuild でも同様の機能を使えれば、上で紹介したプロジェクトの Gradle 部分を MSBuild に置き換えることで、UWP アプリのプロジェクトに TypeScript + React の開発環境を導入できるでしょう。

MSBuild に似たような機能を提供する NuGet パッケージがないかどうか探したのですが、NuGet パッケージの中に node.exe や npm の各種ファイルを含むようなものは見つけられたものの、ビルド時にセットアップするタイプのものはなさそうでした *4。 そこで、そのような機能を持つ Node.MSBuild.Npm という NuGet パッケージを作成し、公開しました。

www.nuget.org

使用方法は簡単です。 まず、Visual Studio で NuGet パッケージマネージャを起動して 「Node.MSBuild.Npm」 を検索してインストールしてください。 あとは package.json を記述して、ビルド時に実行したい処理を build スクリプトとして package.json に定義するだけです。

例えば、TypeScript + React 環境を構築してビルド時に TypeScript のビルドや webpack による変換を実行するには、以下のように記述します。

{
  "name": "my-app",
  "private": true,
  "devDependencies": {
    "react": "^0.13.3",
    "webpack": "^1.12.9",
    "typescript": "^1.7.3",
    "dtsm": "^0.13.0"
  },
  "scripts": {
    "build": "dtsm install & tsc -p ts --outDir built\\typescript & webpack"
  },
  // 一部略
}

ビルド時に実行したい処理の記述方法に関しては、もう少し扱いやすいようにできると良いなぁと思っています。

ぜひご利用ください。

TypeScript + React 環境に対する評価

まだ軽く試している段階ですが、現時点での評価は以下のような感じで、個人的には他のライブラリ (Polymer や AngularJS) を試すまでもなく UWP アプリ開発に導入して良さそうという気がしています。

  • (良い) TypeScript が JSX をサポートしているので、JSX 構文内でも IDE のサポートを受けやすくて記述しやすい。
    • これは本当に便利です。
  • (良い) React 自体は大きなフレームワークではなく、導入がしやすい。
    • 画面内の一部の DOM 構造の構築のみに React を使用するなど。
  • (良い) React により View のコンポーネント化ができ、開発しやすくなる。
  • (良い) WinJS 側で React 用のアダプタが用意されており、WinJS と一緒に使いやすい。
  • (微妙) TypeScript + React の環境を作るのが少し面倒。 慣れれば問題はなさそう。
    • TypeScript をモジュールとして記述するかどうかや、モジュールとして記述する場合には browserify を使うか webpack を使うか、といったことで悩みそうです。 (React は CommonJS のモジュールシステムを使用することを推奨している。)
  • (不明、問題なさそう) 将来にわたってメンテナンスしやすいかどうか。
    • Facebook によりリリースされているので、ライブラリのメンテナンスの心配はそれほどなさそう。
    • 使い方次第ではあるが、React を使いながら徐々に別のライブラリに移行するというのも難しくはなさそう。

はてなブックマーク」 の Windows ストアアプリを更新する際には、こういった技術を導入することでより開発しやすく、メンテナンスしやすいコードを書けるようになりそうです。 *5

おわりに

はてなでは、より開発しやすく、よりメンテナンスしやすいコードを記述していこうとするエンジニアを募集しています!

hatenacorp.jp

明日の 「はてなデベロッパアドベントカレンダー 2015」 の担当は id:motemen です。 お楽しみに!

*1:以下 UWP アプリ向けの話をしますが、Windows ストアアプリでも基本的に同様です。

*2:UWP アプリに限らず

*3:UWP アプリではないですが、大体一緒です。

*4:NuGet パッケージ内に node.exe や npm を含むようなタイプでも悪くはないのですが、exe ファイルを複数プロジェクトで共有したかったり、プロジェクトの階層が深い場所にあると npm のパッケージのインストールに失敗するという問題があったりするので、ビルド時にホームディレクトリにインストールしてくれるタイプのものが欲しいのでした。

*5:この文は 「はてなブックマークWindows ストアアプリの大幅な更新を予告するものではありません。