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 モジュールから行う機能が入りました。
- Typings for npm packages · Microsoft/TypeScript Wiki · GitHub
- TypeScript 1.6時代の.d.ts管理について意見を述べておく - Qiita
今回のリリースでは、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」 で 『ComponentsRecyclerAdapter — RecyclerView で複数の view type や複雑なデータ構造を扱う』 というタイトルで、それぞれ発表しました。
DroidKaigi 2016
5 年前に最初のリリースが行われたアプリを継続的に開発しており、そのための取り組みを共有しました。
要点をまとめると以下のようになります。
- テストは Testing Support Library に頼っている。
- CI については、社内の Jenkins サーバー上でテストの実行やリリースパッケージ作成の自動化を行っている。
- 各種タスクを Gradle タスクとして定義し、タスクの実行をシェルスクリプトで記述。
- 最近は Jenkins Workflow plugin への移行を進めている。 (Groovy での DSL が書きやすいとか、リリースパッケージのアップロード処理の前に人の確認を挟みやすいなどの利点のため。)
- Preview 版 (production flavor の
preview
) を定義して、開発中の機能を preview 版で有効に。- 長い期間の開発でも他ブランチとのコンフリクトをしづらく & チーム内配布で確認しやすく。
- Annotations Support Library を活用。
- 必要に応じてライブラリ的なものを作る。
- 例えば 「関モバ #11」 で発表した
ComponentsRecyclerAdapter
など。
- 例えば 「関モバ #11」 で発表した
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” の英語としての意味は、名詞で 「色合い」 とか 「染料」、動詞だと 「色を付ける」 や 「染める」 などです。
このエントリの内容
Drawable への着色 (Drawable tinting)
API level 21 から、Drawable リソースをアルファマスクとして定義し、その Drawable に色を付けて表示する、ということができるようになりました。 同じ形で異なる色のアイコンを複数個用意したいというような場面で便利ですね。
Android 5.0(API レベル 21)以降では、アルファ マスクとして定義されたビットマップや 9-patch に色を付けることができます。 着色には、カラーリソースまたはカラーリソースに変換されるテーマ属性を使用できます(
ドローアブルの使用 | Android Developers?android:attr/colorPrimary
など)。
bitmapあるいはnine-patcheをアルファマスクとして定義しcolorリソースやテーマの属性で色づけする
L Developer Preview マテリアルデザイン - アニメーション - Firespeed
Drawable
クラスに、下記のメソッドが追加されています。
Drawable#setTint(int)
メソッドDrawable#setTintList(ColorStateList)
メソッドDrawable#setTintMode(PoterDuff.Mode)
メソッド
これらを使うことで、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 に着色できます。
View#setBackgroundTintList(ColorStateList)
メソッド- 対応するレイアウト XML の属性は
android:backgroundTint
- 対応するレイアウト XML の属性は
View#setBackgroundTintMode(PorterDuff.Mode)
メソッド- 対応するレイアウト XML の属性は
android:backgroundTintMode
- 対応するレイアウト XML の属性は
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
への変換まで認識してくれないため。)
v7 appcompat library の compatible widget については以前エントリを書きましたので、詳しくは下記エントリを参照してください。
その他の Drawable の着色
ImageView
には、表示する Drawable に着色するためのメソッドが存在します。
ImageView#setImageTintList(ColorStateList)
メソッド- 対応するレイアウト XML の属性は
android:tint
- 対応するレイアウト XML の属性は
ImageView#setImageTintMode(PorterDuff.Mode)
メソッド- 対応するレイアウト XML の属性は
android:tintMode
- 対応するレイアウト XML の属性は
ProgressBar
でも Drawable への着色が可能です。
ProgressBar#setProgressTint(ColorStateList)
メソッド- 対応するレイアウト XML の属性は
android:progressTint
- 対応するレイアウト XML の属性は
ProgressBar#setProgressTintMode(PorterDuff.Mode)
メソッド- 対応するレイアウト XML の属性は
android:progressTintMode
- 対応するレイアウト XML の属性は
- など、各 Drawable に対応したメソッド (およびそれに対応する XML の属性) が定義されている
調べられていませんが、Drawable を扱う他の View クラスにも似たような機能があるかもしれません。
API level 21 未満でこのような機能を使えるようにする互換クラスは (今のところ) Android Support Library には含まれていないようですので、API level 21 未満では直接 Drawable に着色したうえで ImageView
にセットしたり ProgressBar
にセットしたりする必要がありそうです。
まとめ
- API level 21 から Drawable への色付けがサポートされました。 (Drawable tinting)
- 基本的には、アルファマスクとして Drawable を定義し、表示時に色を指定するという使い方が想定されています。
- View の背景の Drawable への着色や、
ProgressBar
やImageView
などで表示される Drawable への着色も簡単に行えるようになっています。 - Android Support Library を使うことで、Drawable への着色の機能や View の背景の Drawable への着色が API level 21 未満でも利用できます。
関連ページ
- 【Android】Support LibraryのVectorDrawableCompatが適応されるまでの仕組み - Qiita
- Drawable への着色ではなく Vector Drawable のサポートについてですが、内部でどういう処理がされているのか説明されています。 このエントリの内容とかぶっている部分もありますのであわせて読むと良さそうです。
【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 がいくつか含まれています。 いくつか下に挙げてみます。
AppCompatTextView
:TextView
の compatible widgetAppCompatButton
:Button
の compatible widgetAppCompatImageView
:ImageView
の compatible widgetAppCompatCheckBox
:CheckBox
の compatible widgetAppCompatSpinner
:Spinner
の compatible widget- などなど
これらのクラスは、新しい API level で導入された機能の一部を古いプラットフォームにも提供してくれます。 クラスによって提供される機能は違うものの、主には widget tinting 周りの機能 (+α) が提供されると考えて良さそうです。
appcompat ライブラリによる widget tinting については下のブログエントリを参照してください。
インフレート時に自動的に変換される
各 compatible widget のドキュメントを読むと、以下のような説明が書かれています。 (以下は AppCompatTextView
のもの。)
This will automatically be used when you use
AppCompatTextView | Android DevelopersTextView
in your layouts. You should only need to manually use this class when writing custom views.
つまり、レイアウト XML 中に <TextView ...>
って書いておけば、インフレート時に自動的に AppCompatTextView
になる、ってことですね。 便利です。
インフレート時に自動で変換される仕組み
便利なのはいいけどどういう仕組みなのかわかっておかないと嵌ったりすることもあるので、どういう仕組みなのか調べてみました。
LayoutInflater
に Factory をセットすることでインフレート時の挙動を変更できる
まずは LayoutInflater
について調べてみます。 インフレート時に XML ファイル中の要素名を各 view のクラスに変換する処理は、Factory (LayoutInflater.Factory
/LayoutInflater.Factory2
オブジェクト) が担っているようです。 下記メソッドを見てみると、ドキュメントにいろいろ書かれています。
AppCompatActivity
が LayoutInflater
に 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
メソッドが呼ばれていました。
まとめ
上で調べたように、AppCompatActivity
が LayoutInflater
に独自の Factory をセットすることで、インフレート時の自動変換が実現されています。 よって、インフレーション時については以下のようにまとめられます。
AppCompatActivity
で使う限り、LayoutInflater#from(Context)
メソッドで取得できるLayoutInflater
でのインフレート時に compatible widget への自動変換がはたらくと考えて良い。AppCompatActivity
を使わないのであれば、AppCompatDelegate#installViewFactory
メソッドを使うことで使うことで同等の機能が実現される。
インフレーション時以外に何か良しなに変換してくれたりはしないので、以下のことにも気を付けましょう。 (appcompat ライブラリを使っている環境での話です。)
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 を使用するのが簡単でしょう。
- TypeScript のビルド : npm の typescript モジュール
- React のソースコード : npm の react モジュール
- React の型定義 : npm の dtsm モジュールを使用して DefinitelyTyped から react.d.ts を取得
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 ファイルとして使用するためには、webpack や Browerify などを使用してパックしてやる必要があります。 今回は私は 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 パッケージを作成し、公開しました。
使用方法は簡単です。 まず、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
おわりに
はてなでは、より開発しやすく、よりメンテナンスしやすいコードを記述していこうとするエンジニアを募集しています!
明日の 「はてなデベロッパーアドベントカレンダー 2015」 の担当は id:motemen です。 お楽しみに!