Help us understand the problem. What is going on with this article?

[Android]画像をとりあえず Drawable フォルダに放り込むのをやめよう

はじめに

Android ではデバイスの表示画面に応じた、ビットマップ画像を格納する仕組みが用意されています。
それがぞくに言う Drawable フォルダというやつで、DPI に応じて画像を格納できるフォルダが用意されています。

密度修飾子 Drawable フォルダ DPI値 ピクセル比 スケーリング比 説明
LDPI drawable-ldpi 〜120 36x36 0.75 低密度(ldpi)の画面(〜120dpi)に適応するリソース
MDPI drawable 〜160 48x48 1.00
MDPI drawable-mdpi 〜160 48x48 1.00 中密度(mdpi)の画面(〜160dpi)に適応するリソース
HDPI drawable-hdpi 〜240 72x72 1.50 高密度(hdpi)の画面(〜240dpi)に適応するリソース
XHDPI drawable-xhdpi 〜320 96x96 2.00 超高密度(xhdpi)の画面(〜320dpi)に適応するリソース
XXHDPI drawable-xxhdpi 〜480 144x144 3.00 超超高密度(xxhdpi)の画面(〜480dpi)に適応するリソース
XXXHDPI drawable-xxxhdpi 〜640 192x192 4.00 超超超高密度(xxxhdpi)の画面(〜640dpi)に適応するリソース
NODPI drawable-nodpi - - - すべての密度に適用するリソース

今までなるほど〜という感じで、個人開発では適当な Drawable に画像を放り込んでいました。
そのような感じで適当な運用をしていたらレイアウトが崩れなどの問題が起きたので、
ここらで Drawable の仕組みについて調べてまとめてみたいと思います。

画面の DPI に応じたリソースに切り替えてくれる

Drawable フォルダは画面の DPI ごとにフォルダが区切られており、
Drawable フォルダに各画面のビットマップを格納しておけば、
画面の DPI に応じたリソースに自動で切り替えてくれるようになっています。

準備

表示するリソースを準備する

次の手順で 各 Drawable フォルダに画像を格納して、DPI の違う画面ごとにリソースを準備します。
今回は表示が切り替わっているが変わりやすいように、画像にピクセル比を載せたものを用意します。

  1. res フォルダをエクスプローラなどで開く
  2. Drawable フォルダを作成し、dot.pngという名称で格納する。
Drawable フォルダ ピクセル 画像
drawable-ldpi 36x36 36 x 36.png
drawable-mdpi 48x48 48 x 48.png
drawable-hdpi 72x72 72 x 72.png
drawable-xhdpi 96x96 96 x 96.png
drawable-xxhdpi 144x144 144 x 144.png
drawable-xxxhdpi 192x192 192x192.png

画像を表示するアプリを作成する

次の手順で Drawable フォルダに格納した画像である、
dot.png ファイルを表示するアプリケーションを作成します。

  1. activity_main.xmlを開く
  2. 画面中央に ImageView を配置し、src@drawable/dotを指定する。

image.png

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:src="@drawable/dot"/>

</FrameLayout>

実践

準備が整ったので、DPI が異なるデバイスでアプリを起動して動作を確認してみます。

LDPI

DPI が LDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-ldpi フォルダに格納した画像が表示されます。

Screenshot_1575987932.png

MDPI

DPI が MDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-mdpi フォルダに格納した画像が表示されます。

Screenshot_1575988096.png

HDPI

DPI が HDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-hdpi フォルダに格納した画像が表示されます。

Screenshot_1575988245.png

XHDPI

DPI が XHDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-xhdpi フォルダに格納した画像が表示されます。

Screenshot_1575988351.png

XXHDPI

DPI が XXHDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-xxhdpi フォルダに格納した画像が表示されます。

Screenshot_1575988462.png

XXXHDPI

DPI が XXXHDPI であるデバイスでアプリを起動してみます。
意図したとおり drwable-xxxhdpi フォルダに格納した画像が表示されます。

Screenshot_1575931567.png

というような感じで、 それぞれの DPI の Drawable フォルダに画像を
入れておくと Android が DPI にあわせて画像を切替えてくれます。
(DPI を指定せずに drawable に画像を格納する場合には mdpi の画像として扱われます)

このように Android では DPI ごとに用意した画像を表示する仕組みが整っており、
正しく利用すれば最適な品質で画像を表示することができるようになっています。

デバイスの DPI にあったリソースがない場合、リスケールされる

それじゃ全部の Drawable フォルダに画像を入れて、
おかなきゃいけないのかというとそうでもないみたいです。

デバイスの DPI にあった Drawable フォルダに画像がない場合は、
別の DPI の Drawable フォルダに格納されている画像を選択するそうです。
しかもこの場合は リソースを表示する際にデバイスのDPIにあったサイズにリスケールしてくれます。

準備

表示するリソースを準備する

次の手順で drawable-xxxhdpi にのみ画像を格納します。
画像は先程のものと同じものを利用します。

  1. res フォルダをエクスプローラなどで開く
  2. Drawable フォルダを作成し、dot.pngという名称で格納する。
Drawable フォルダ ピクセル 画像
drawable-xxxhdpi 192x192 192x192.png

画像を表示するアプリを作成する

先程と同じアプリを利用します。

実践

準備が整いましたので DPI が違うデバイスでどのように表示が変わるか確認していきます。

LDPI

DPI が LDPI であるデバイスでアプリを起動してみます。
XXXHDPI に格納した画像が LDPI にあわせてリスケールされて小さくなってます。

Screenshot_1575988909.png

HDPI

DPI が HDPI であるデバイスでアプリを起動してみます。
XXXHDPI に格納した画像が HDPI にあわせてリスケールされて小さくなってます。

Screenshot_1575988805.png

というような感じで 他の Drawable フォルダに画像がある場合は、
それを選択しまた DPI に合わせてリスケールしてくれます。

このようにリスケールをしてくれるので雑に Drawable フォルダを
管理しててもおおよそは問題なく画像を表示してくれるはずです。
ですが画像が意図しない大きさで表示されることにつながるので注意が必要です。

全ての DPI で同じリソースを使うには nodpi に入れる

全ての DPI で同じ Drawable 画像を使いたいんだけどってときもあると思います。
そういうときは drwable-nodpi に画像を入れることで全ての DPI で同じサイズの画像を利用できます。

準備

表示するリソースを準備する

次の手順で drawable-nohdpi にのみ画像を格納します。
画像は先程のものと同じものを利用します。

  1. res フォルダをエクスプローラなどで開く
  2. Drawable フォルダを作成し、dot.pngという名称で格納する。
Drawable フォルダ ピクセル 画像
drawable-nohdpi 1000x1000 dot.png

画像を表示するアプリを作成する

先程と同じアプリを利用します。

実践

準備が整いましたので DPI が違うデバイスでどのように表示が変わるか確認していきます。

LDPI

DPI が LDPI であるデバイスでアプリを起動してみます。
意図したとおりリスケールされていない画像が表示されています。

Screenshot_1575989879.png

HDPI

DPI が HDPI であるデバイスでアプリを起動してみます。
意図したとおりリスケールされていない画像が表示されています。

Screenshot_1575990184.png

という感じで DPI が異なるデバイスでもリスケールせずに同じ画像を表示できます。
なので特別な理由で同じ画像を使いたいときは drawable-nodpi が使えます。

おわりに

次のような特徴を理解して、とりあえず Drawable フォルダに画像を放り込むのはやめましょう。

  • DPI ごとに用意される Drawable フォルダに画像を格納することで、
    画面の大きさごとに最適化された画像を表示することができる。
  • DPI を指定せずに drawable に画像を格納すると mdpi の画像として取り扱われる。
  • デバイスの DPI にあった Drawable フォルダに画像がない場合は、
    別の Drawable フォルダの画像が適応される。
    その際には画像はリスケールされるので注意が必要です。
  • 全 DPI で同じリソースを利用した場合には drawable-nodpi に画像を格納すればよい。

参考文献

kaleidot725
組み込みエンジニア ➔ Androidエンジニアになりました。なので最近は主に Android やってます。あとはサーバーサイド Kotlin もやっていこうと思っています。
https://medium.com/kaleidot725
yumemi
みんなが知ってるあのサービス、実はゆめみが作ってます。スマホアプリ/Webサービスの企画・UX/UI設計、開発運用。Swift, Kotlin, PHP, Vue.js, React.js, Node.js, AWS等エンジニア・クリエイターの会社です。Twitterで情報配信中https://twitter.com/yumemiinc
http://www.yumemi.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away