Androidのdpについて

こんにちは丸山@です。

最近は2年ぶりくらいにAndroidを触る機会が増えてきました。そこで当時も曖昧のまま過ごしていたdpという単位についてまじめに考えてみたので、そのメモです。(間違っていたらごめんなさい)

背景

Androidは解像度、画角インチ、dpiが機種によってバラバラです。そんな状況でも様々な機種で同じように画面のレイアウトが見える工夫が必要となってきます。そこで生まれたのがdpという考え方です。

用語整理

  • 解像度
    • ディスプレイにどれだけのドット(ピクセル)が埋まっているか
    • e.g. Nexus5だと1980x1080ピクセル
  • 画角インチ
    • ディスプレイの対角線のインチサイズ
    • e.g. Nexus5だと4.95インチ
  • dpi(ppi)
    • 1インチあたりどれだけのドット(ピクセル)が入っているか(Dot Per Inchの略)
    • e.g. Nexus5だと445dpi

ここで重要になってくるのがdpiです。例えば「4インチ100dpiの端末A」と「4インチ200dpiの端末B」それぞれに縦横100pxの枠を表示した場合、端末Bでは端末Aの半分のインチで表示されてしまいます。これでは端末ごとにボタンやアイコンの大きさ(インチ)がまちまちになってしまい、非常に困ります。

そこでdpiに依存せずに「同じ画角の端末では同じオブジェクトは(だいたい)同じインチで画面に表示する仕組み」としてdp(Density-independent Pixels)という単位が考えられました。

dpiの区分とdp

Androidではまずdpiの大きさによって端末の種類を分けました。(c.f. Range of screens supported)

  • ldpi: ~ 120dpi
  • mdpi: ~ 160dpi
  • hdpi: ~ 240dpi
  • xhdpi: ~ 320dpi
  • xxhdpi: ~ 480dpi
  • xxxhdpi: ~ 640dpi

ldpi : mdpi : hdpi : xhdpi : xxhdpi : xxxhdpi = 0.75 : 1 : 1.5 : 2 : 3 : 4

というおなじみのものです。そしてこのmdpiでの1dpが1pxと定義しました。つまりレイアウトXML上で1dpと書いた場合、mdpi端末では1px、ldpi端末では0.75px(!?)、hdpi端末では1.5px(!?)、xhdpi端末では2pxと言った具合に表示されます。 ここで注意すべきなのは1dpと書くとldpiでは0.75px、hdpiでは1.5pxという半端なpxになってしまいます。なのでdpを指定するときは4の倍数にする必要があります。ただし、現在ldpi端末はほぼ存在しないので、2の倍数でも実用上は問題ないと思います。

画像

dpの話については以上です。でも「じゃあ実際何pxの画像を作れば良いんだ!」となります。これに対する答えは「対象とする端末を決めて解像度を求めて画像を作る」というものです。

現在だとAndroidでは5インチ/xxhdpi/アスペクト比16:9の端末が主流モデルです。なのでこの端末を対象にします。解像度はこの情報から求めるとだいたい1920x1080になります。つまり画面をPhotoshopなどでデザインする場合は1920x1080/xxhdpiという想定で作業をすることになります。そして画像を切り出してAndroidアプリ内に組み込む場合はxxhdpi用として読み込ませるように組み込みます。

ここで1つ気をつけることがあります。それは「pxをdpに変換した時に4の倍数になるようにする(ldpiを無視するなら2の倍数)」というものです。これは「dpiの区分とdp」で説明した理由によります。例えばxxhdpiで6pxの画像を作ると、2dpになります(xxhdpiで1dp = 3px)。2dpをldpiで表示すると1.5pxとなってしまいます。これがxxxhdpiで12pxの画像を作ると4dpとなり、ldpiで表示すると1pxとなって綺麗に表示されます。

まとめ

  • 現在主流のスペックをもとにインチ、dpi、解像度を決める
    • 今だと5インチ/xxhdpi/16:9
  • dpを指定するときは4の倍数にする(ldpiを無視するなら2の倍数)
  • 画像やマージンのpxをdpに変換した時にdpが4の倍数になるようにする(ldpiを無視するなら2の倍数)
    • すごく横着するとxxxhdpiまでを考慮して48pxの倍数でpxを指定すると何も考えなくてよくなる
    • 48pxという数字はどのdpiで使っても半端なpxにならない魔法の数字

うううう、Android面倒だ。デザイナーさんはこんなこと考えながらデザイン組んでるのか...世の中は厳しい。

今後さらに高dpiになるとどうなるのか?

現在だとおもにxxhdpiを対象にして画像を作れば良さそうです。しかし、今後さらにdpiが上がった場合、それに合わせた大きな画像を作る必要があるのだろうか?というかそもそも何故、低dpi用の画像を高dpiで使うと画像がぼけるのだろうか?

画像処理について詳しくないので、ここからは正しいのか不明な話になります。画像を拡大するときは通常、色の補完を行います。そうすると色の境界線あたりで微妙な色が配置されることになり、これがボケの原因だと思います。しかしdpiがどんどん高くなると人間の目がドットを認識できなくなり、低dpi用の画像を高dpiの端末で表示して拡大されてもボケに気づかなくなるのでは?と思っています。

空想実験として100万dpiと1万dpiの端末が存在して、1万dpi用につくった画像を100万dpiの端末で拡大して表示したとします。この時に色補完ありで拡大するので1万dpiでの1ドットを100分割して色補完したものが100万dpi上で表示されることになると思います。ただし人間の目で見ると1万dpiの1ドットも100万dpiの1ドットももう区別がつかないかなと思います。なので「100万dpi上で100ドット使って色補完ありで表示されている領域」と「1万dpiで1ドット使って表示されている領域」を区別することができずに、十分くっきり見えるのかなと想像しています。

ここで一つ疑問に思うのが、なぜAndroidのようなスマホでも色の補完を行うのだろうか?ということです。通常、画像を拡大するとインチが変わってしまうので、色の補完をしてエッジが立ちすぎないようにします。しかしAndroidの場合は、低dpi用の画像を高dpi端末で拡大してもインチサイズは変わらずpx数だけが変わります。ということは色補完をせずに拡大したとしても、少なくとも低dpi端末でみたのと同じようには見えるはずです。そして高dpiになればなるほど人間の目がドットを認識できなくなるので、ピクセルパーフェクトで表示されたものと色補完なしで拡大表示されたものは判別できなくなるのでは?と思いました。この辺りはまったく素人考えです。色補完を行って拡大する理由がちゃんとあると思うので、誰か教えて下さい。