2012/10/15

Android:xhdpiリソースしか持たないアプリが抱える問題


Androidのdrawableに関する情報、特にマルチスクリーン対応に関する話題で
わざわざ各density毎にリソース(画像等)を作らなくても、
xhdpiリソースのみアプリに持たせて、より低いdensityのhdpiやmdpi向けには
xhdpiリソースを縮小したもので代用すれば良いのではないか?
という声を聞くことがあります。

今回はこの考えのメリットとデメリットについての考察です。
※明確な答えを知っている訳ではないので間違いがあるかもしれません。

たしかにxhdpiリソースを他densityのリソースとして代用すれば
  • 画像を複数用意する手間が省ける
  • apkサイズを小さく出来る
  • 画像の管理がチョッとは楽
等々、メリットがいくつかあります。

他にも、mdpi画像をxhdpi端末で表示すると拡大表示されることになり、画像の品質が低下しますが、
xhdpi画像をmdpi端末で表示すれば縮小表示されることになり、画像の品質低下は先程より軽微に思えます。

これでは、mdpiやhdpiリソースを用意する意味があるのか疑いたくなりますが、
xhdpiリソースだけでは不都合があるため今の仕組みがあるはずです。


●mdpi端末でxhdpi画像を表示する場合の品質劣化

Developersサイトには下記の記載があります。
スケーリングによる画像の拡縮によって、画像が不自然に見えることがある。
これを防ぎ、美しい画像の表示を保証するために各画面密度用の画像を用意すべきである。
(引用元)http://developer.android.com/guide/practices/screens_support.html#support

mdpi端末でxhdpi画像を表示した場合、品質はどの程度劣化するのでしょうか?
1つ実験してみました。

■実験内容
細かい網目状の左画像をxhdpiリソースとして用意。

これを各Densityの端末で
 そのままのサイズで表示した場合 or どのDensityでも同じ見た目に揃えた場合
どのように表示されるか試してみます。


端末はエミュレータを使用し、表示結果をDDMSのスクリーンショットで取得します。
各Density毎のエミュレータ設定は下記。
MDPI:
・Skin: HVGA
・Density: 160(mdpi)

HDPI:
・Skin: WVGA800
・Density: 240(hdpi)

XHDPI:
・Skin: WXGA720
・Density: 320(xhdpi)

画像を表示するため、レイアウトファイルに下記ImageViewを定義。
<ImageView
    android:id="@+id/test"
    android:layout_width="@dimen/img_width"
    android:layout_height="@dimen/img_height"
    android:src="@drawable/test" />
また、後者の"どのDensityでも同じ見た目に揃えるケース"では、
各Densityリソースに下記dimenリソースを用意します。
・values-mdpi
<resources>
    <dimen name="img_width">50dp</dimen>
    <dimen name="img_height">50dp</dimen>
</resources>
・values-hdpi
<resources>
    <dimen name="img_width">75dp</dimen>
    <dimen name="img_height">75dp</dimen>
</resources>
・values-xhdpi
<resources>
    <dimen name="img_width">100dp</dimen>
    <dimen name="img_height">100dp</dimen>
</resources>

■実験結果
各Densityでの表示結果が下。
低いDensityだと網目が潰れてしまうことがわかります。
これは"そのままのサイズで表示した場合"、"どのDensityでも同じ見た目に揃えた場合"
どちらでも同じです。

・実行結果


■まとめ
風景や人物画像等、多少圧縮されても表示上問題ない画像でも
小さなアイコンや細かなUI部品といった1pxの潰れが無視できない画像の場合は
ちゃんと各Density毎の画像を用意するほうがよさそうです。


●mdpi端末でxhdpiを表示する場合のパフォーマンス低下

mdpi端末で結局圧縮・縮小されるxhdpiリソースを読み込むのは明らかに無駄です。
mdpi画像を用意すれば、圧縮・縮小の必要はなく無駄にサイズの大きなデータを読み込む必要がありません。

では、mdpi端末でxhdpi画像を読み込んだ場合のパフォーマンス低下はどれ程でしょうか?

■実験内容
画像のmdpi版とxhdpi版を準備。

これをmdpi端末で読み込み、
 xhdpi版のみ準備したケース or mdpi版も準備したケース
で表示速度を計測します。

計測区間はsetContentViewの前後としています。
また、キャッシュによる高速化を回避するため、測定の都度プロセスをkillします。


■実験結果
測定の結果、
xhdpi版のみ準備したケースでは約513msec
mdpi版も準備したケースでは約239msec
となりました。

倍以上の差がでました。
xhdpiの画像サイズはmdpiの倍なので妥当な結果と言えそうです。


■まとめ
mdpi端末でxhdpiリソースを表示するとパフォーマンスが低下します。
大きなxhdpi画像をmdpi端末で読み込ませることは、無駄なオーバーヘッドに繋がり得策ではなさそうです。


●全体まとめ

xhdpi画像を低いDensity端末で表示すると画像が潰れることがある。
低いDensity端末でxhdpi画像を読み込むと、適切なDensityの画像を読み込む場合と比べてパフォーマンスが悪くなる。

安易に"xhdpiリソースのみ用意する方法"を選択すべきではないようです。
上記の悪影響を踏まえた上で、実際に動作を確認して選択する必要がありますね。

また、apkのサイズを減らしたいのであれば.9.pngやShapeによる描画等のテクニックが用意されています。

UIはアプリの印象を決める大きな要素です。
綺麗で洗練されたUIはユーザを強く惹きつけますね。
品質に大きく関わる部分でもありますのでこの辺は注意したいところです。

以上です。