2013年8月31日土曜日

LayerListを利用してカードUIを作る

カードUI(流行ってるの?)を作る手法はいくらか考えられるけど、
  1. Layout.xml上でカード風に見せるViewを定義する
  2. カードUI風の画像を用意する
  3. ShapeDrawableを利用する
  4. カスタムViewを実装してonDrawメソッドを実装する
個人的な考えとしては、1を選択するのは、ないなぁ。

とはいえ、本業がプログラマだと2のように素材をさくっと作ってしまおう!とはいかない。4が恐らく処理速度上でもっとも軽量で、カスタマイズ性も高いものの、ちゃんとしたカスタムViewを作成するには知識がいるので、一番お手軽なのは3の方法でしょうか。

Layer ListでShape Drawableを重ねれば、そこそこな見栄えのカードUIが作れると思います。

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item android:id="@+id/shadow" android:top="1dp" android:left="1dp">
    <shape >
      <solid android:color="#868686" />
    </shape>
  </item>
  <item android:id="@+id/front" android:bottom="1dp" android:right="1dp">
    <shape >
      <gradient android:startColor="#FFFFFF" android:endColor="#EDEDED" android:angle="270"/>
      <stroke android:color="#DBDBDB" android:width="1px" />
    </shape>
  </item>
</layer-list>

xmlに上のように定義して、利用する際はbackgroundのdrawableとして指定するだけ。なお、色はあまり考慮していないのと、State Drawableとして定義していないので、実用はしないでください。


AndroidにはiOSのCore Graphicsのレイヤ操作に相当する機能がないので、もう少しリッチにドロップシャドウを実現したい場合、9patchを用意するか、4を採用して、onDrawメソッドで実装することになります。

---

ところで「9patch画像を作成したら、/drawable/ディレクトリに保存する」とは公式にもそう書いてあるのだけど、「だから9patch画像は一つだけ用意すればいいんだ」という誤解が生じているような気がする。(9patch画像は生の/drawable/に保存する、と書いてあるチュートリアルが異様に多い)

公式ドキュメントが指す/drawable/ディレクトリとは、リソース分岐(/drawable-hdpi/とか)も含めた全体を指していて、9patch画像もピクセル密度に応じて複数の画像を用意する必要がある

このことはAndroid SDKをインストールしたディレクトリの、platforms/android-XX/data/res内を見れば分かります。Androidはボタンなどで9patchを多用しているのですが、ピクセル密度に応じた9patchを用意しているのです。

---

/drawable/に突っ込んだ9patch画像がなぜ問題かというと、実際には/drawable-mdpi/か何かとして認識されているから、例えばNexus 7などの中途半端なピクセル密度で表示されてしまうと、まず画像の本体部分が約1.3倍に引き伸ばされた後、9patchの指定位置ピクセルに補正が掛かるため、ここでズレが生じて破綻するケースがある。

それはまだマシなケースで、画像の拡大縮小の結果、9patchとして違法となった場合、getNinePatchChunk()でnullが返却され、Runtime Exceptionでアプリが強制終了することになる。

もっとも、ピクセル密度に応じて引き伸ばし位置を定義するのは、画像が複雑になると非常に面倒ですし、引き伸ばしだけでタイルパターンには使えないですし、デザイナーの方に9patchの詳細な仕様(Android開発者も知っているか怪しい)を伝える手間もありますし、なかなか難しいところです。

2013年8月30日金曜日

Effective Androidを読んでる。

「Effective~」というタイトルから、「デザインを定義するならばThemeを利用し、レイアウトと分離せよ」的な、格言めいた実用系ノウハウが載っているんだろうと思っていたんだけど、1章でいきなりその辺を外部の書籍に投げていて(というより自著の宣伝か)、出オチ感が…。

「すぐに使えるテクニック集」と「その辺に転がってるチュートリアル」と「マニアックな知識」が玉石混交で、ネイティブアプリ開発者もWeb系開発者もデザイナーもごった煮で対象としていて、ノリとしてはAndroid関係者向けの専門誌のような感じの本でした。

以下は、読んでて「う~~ん…」とか、「うおー!」ってなった部分。

DP値を実行時に取得するならば、dimension resourceで名前をつけて取得しよう。

<resources>
    <dimen name="card_padding">8dp</dimen>
</resources>
getResource().getDimension(R.dimen.card_padding);

Androidはレイアウトとスタイルとコードを分離できるとはいえ、コード上でdpとpx間の相互変換を行うこともよくある。ユーティリティメソッドを用意してもいいけれど、dimension resourceを使用し、dp値に名前をつけて管理した方がソースの可読性も高く保つことができる。

dimension resourceとリソースのバージョン分岐を組み合わせると、端末のフラグメンテーション対策にも応用できる。dimension resourceはもっと普及されるべきだと思うんだけど、なぜかプッシュする人が少ない印象…。何かトラップでもあるのだろうか。

AndroidでレイアウトをするならMetrics and Gridsは読もう。

最近Illustratorなども触っているので、3章のデザイナー視点で使いやすいpxなどを述べたお話は、面白い視点を提供してくれたのだけど、もうちょっと掘り下げて欲しいなという部分もあった。

  • 現実としてAndroidの案件の多くは、iOSとのマルチや移植であり、UITableViewCellが44ポイントな限り、3で割れない素材との戦いは終わらない。44ポイント(44px&88px)の素材に対してどう対抗すればいいのか?
  • さらにtvdpi(というかNexus 7)の登場せいで3の倍数で用意すれば万事解決、というほど単純でなくなってしまった。
  • dpに対してspが存在する説明がゼロだけど、spはユーザーがフォントサイズに対する多少の自由度を得るためのものだから、pt固定値素材を用意してしまうのは、ユーザービリティの低下に繋がるデメリットがあることも説明すべきでは。
  • iOSのユーザーインターフェースガイドラインほど厳密ではないものの、Androidにも類似したデザインガイドラインは提供されている。特にMetrics and Gridsは全ての開発者が読むべき。48dpのリズム、8dpの「mind the gap」(この言葉遊びのセンスが素晴らしい)は知っていて当然であって欲しい。

The AndroidManifest.xml Fileに一度は目を通そう

章が進むごとに内容が濃くなっていって、5章辺りからすごく面白くなる。

ただ、「AndroidManifestを活用しよう」というタイトルとは裏腹に、マニアックすぎて実用的か疑問符をつけたくなるような、AndroidManifest.xmlのマイナー設定の解説記事なのがもったいない。

著者的にはリファレンスに書いてあるからそれを読めば分かるでしょ?ってことなのかもしれないけど、現実的にはclearTaskOnLaunchとかnoHistoryとかの存在を知らずに、コード上で実装してるのをちらほらみたことがある。

実際、リファレンスは長い上に意味が通じない箇所もちらほらあるし、真面目に読むと非常に疲れるからね。

それでも、一度でいいからThe AndroidManifest.xml Fileを通して見るべきだと思う。

流し読みで「こんな設定があったのかー」と知るだけでも十分。それだけでもAndroidの可能性が広がったように感じる。まあ錯覚で終わると思うけど。

Eclipseの設定ファイルが何をやっているのか理解しよう

6章ではEclipseの設定ファイルに関する解説を行っているのだけど、これは必見だと思う。

Androidのプロジェクトをバージョン管理して、多人数で開発する方法論として、自分レベルのヌルい知識で語ると、
  • Eclipse関連のファイルをcommitせず、pullした純粋なAndroidコードをExisting Android Code Into Workspaceでインポートする
  • .projectや.classpathもバージョン管理の対象とした上で、eclipse import existing projects into workspaceでインポートする
の二通りが思いつく。

前者はpureなAndroidコードのみをバージョン管理の対象とするため美しく思えるけど、この方法はまずプロジェクトの規模が大きくなるとまず破綻する。

複数のプロジェクトが絡んで、class pathが入り乱れているような場合、Eclipseの設定ファイルが何をやっているのか理解していないと、自力でビルドパスを解決できずにハマったりするからだ。

幸いなことにEclipseの設定ファイルは人間にも読みやすい形式となっていて、マージツールで差分を取ることで原因追及することはできた。この記事にもう少し早く出会えていれば…。

gcm.jarはdeprecatedなので、Google Play Service APIを使いましょう

11章の内容はやや古い(7月ごろにGoogle Cloud Messaging APIの一部刷新があったのですが、反映されていない)ため、公式のものを読んだ方がいいです。有志の方による日本語翻訳も最新仕様には対応してません。

また用語も公式に定義されているものと対応していない(Registration IDをIDと大雑把に扱ったり、Sender IDをproductIDと呼称するなど)ので、技術書としても微妙な内容と思います。

結論.Theme ResourceとかFragmentの活用とかそういう話がもっと欲しかった

Android開発者の標準レベルって、流行り始めた2.3とかで止まってるような気がする。

巷には基本中の基本しか触れていない入門書が多いし、面白い本も何冊か知ってはいるけれど、進化スピードの速いモバイルの世界ではすぐに陳腐化してしまう。「先」を目指すとなると、英語の公式ドキュメントを読み漁るしかない。

「Effective~」系書籍は小手先のテクニックじゃなくて、フレームワークを十全に引き出すエッセイ集のようなイメージがあって、しかも同人というスピード感が加われば、「先」を切り拓いてくれるんじゃないかという期待があった。まあ勝手にイメージを押し付けてるだけなんだけど。

例えば、Activityと密結合を回避するための様々なテクニック、モデルクラスを作成することでActivity非依存のテストコードを書けるようにするとか、AsyncTaskをAsyncTaskLoaderにリプレースするとか、AlertDialogよりもDialogFragmentを好むべきだとか、それで全て上手くいくわけではなく、メリットもあれば落とし穴もある…。

…そういうノウハウが載っているのかな~と期待して買ったので、満足はできなかったけど、一部のマニアックな話については面白かった。増補改定にも期待。

2013年8月25日日曜日

iOS開発とAndroid開発の比較

iOS開発 vs. Android開発

という記事を読んで面白かったので、両端末開発が可能な(しかし最近は.NETが面白いので仕事以上では触っていない。iOS7についても少し調べる時間を作らないと…)自分も何か書いてみる。

iOS開発


最初に覚えることは多い。Objective-C、Cocoa Touch API。一般的な入門書で扱っている知識はここまでだけど、iOS Developerを名乗るのであれば、blocksやGCDなどモダンな知識、MRCやObj-C2.0以前のKVO、KVCなどレガシーな知識、Cocoa Touchの土台であるCore系技術についても習得する必要がある。

Core Imageは現状ではiOS端末で実用的な動作を実現できないのでどうでもいいとして、最低限Core GraphicsとCore Animationは覚えたい。Core Dataを覚えればフレームワークの性能を引き出し、iCloudとの連携についても取り計らってくれるのだけど、Core Dataフレームワークを理解するのは結構しんどいので、業務上で使うときには、FMDB経由でSQLiteを使うという方法が安牌かもしれない。

まあとにかく覚えることは多いけど、言語として面白いのでそれほど苦にならなかった。上の記事ではドキュメントの質が低いとあるけれど、確かにiOSのドキュメントはあまり整備されないのでバージョンがバラバラなドキュメントが入り乱れていて、困るときもあるのだけれど、Class Referenceに関して言えばAndroid DevelperのJava Docよりも充実していると思う。Discussionに何度助けられたことか。

ネイティブレベルの英語力があれば、読めるドキュメントの幅も広がるのでまた見えるものが違うのかもしれない。

Interface Builderはデザイナーとしては使えない(特にUIAppearanceが実行時にしか反映されないのは厳しい)けど、「オブジェクトを配置して相関を定義してシリアライズするもの」として割り切れば、「使い物にならない」と切り捨てるほどでもないと思う。ただ4インチ対応が必要になって以降、Auto Layout関連の挙動が直感的じゃないので、ひどく使いにくくなったのは事実。

Android開発


Javaの知識がある程度(Effective Javaが読める程度)あれば、Androidフレームワークを齧りながらでも開発ができるので敷居は低い。

しかしiOSのように低レベルフレームワークにアクセスすることができないのは致命的な弱点でもあって、例えばAndroidにはCore Textに相当するような低レベルで高品質なテキストレンダリングエンジンがないので、Androidの電子書籍アプリは端末によって見栄えの違いが生じたり、動作がもっさりしていたりと総じて質が低くなってしまっている。

「Androidといえばフラグメンテーション(断片化)問題」と思い込んでいる人が多いけれど、実際のところ多画面、多バージョン対応用向けの機能は充実していて、リソース分岐や9patchを駆使することができれば、遥かに多種多様なデバイスに対応しなければならないWebサイトよりも、ずっとずっと楽だと思う。

この辺り、Androidフレームワークを理解した上でそれに合わせて多画面でも動作するアプリケーションを作る、というやり方をせずに、固定サイズの画面に向けた既存のデザインをAndroidに流用しようとするから無理が出るだけじゃないのかなぁと思っている。

Androidの画面作りは、Webの画面作りに似たところがあって、まずボタンなどのコンポーネント見栄えをThemeリソース(CSSに相当)で定義して、それをXMLで定義していくというボトムアップなアプローチが向いている。GUIのレイアウトエディタは実際の編集に使うのではなく、実機出力のプレビューとして使う方がいい。Android Studioで実用的な速度で使えるようになったけど、XMLでの書き方を覚えた方が効率的なビューを作れる。

そういえば昔はエミュレータでしか階層ビューワが使えなくて、そのためだけにエミュレータを使っていたのだけど、今では実機でも使えるようになったので久しくエミュレータを起動していない。

長期的視点


Androidは2から4でまるで別物のように生まれ変わったけど、開発者視点ではJDK7への対応が異様に遅かったり(そういえばGAEもなぜかJDK7対応は異様に遅かった)や、JUnitはいまだに3で書かないといけないし、進化はのんびりしている。

それでいて、バックポートライブラリが提供されるため、4系で新規に追加された機能群を2系に対応することができる。遅すぎる感はあるものの、ようやくAction Barも対応されました。

このためAndroid 2.3で作成したアプリを4で動かすこともできるし、4向けAPIであるAsyncTaskLoaderやActionBarを利用したアプリを作っても、2系でも起動することができる。

対するiOSはバージョンが変わる度にAPIの内部仕様が目ぐるましく変化し、言語の構文や規則までもが変わるといったことがままにある。このためiOSの開発者で居続けるためには、常に最新の情報にアップデートしていく必要がある。

「ユーザーの端末もほとんどが最新のものに切り替わっていくので、iOSにはフラグメンテーションの問題はない」みたいな話を良く聞くけど、それは環境などによる。iOS4以前に作られたレガシーなフレームワークを利用したいときもあるし、流行り始めたiOS4時代のAPIで書かれたアプリをiOS6~7に対応せよというメンテナンス案件やら、サポートするリスクリターン考えずにiOS4からの対応にせよという命令も多いので。

このため長期的な保守については、現時点ではAndroidの方が圧倒的に楽だと思う。もっともiOSも成熟期に入り、今後マルチタスク化などの大きなパラダイムシフトはないと思うし、XCode5でテスト環境が充実していくという話もあるので、今後の見通しは分からないけれど。