「ピクセル密度」ってナニよ?

 さて。
 端末でアプリを実行すると、「画像表示(View)が拡大されてしまう」不具合について。
 (参照:「画面サイズ設定」ってナニよ?)
 原因は、画面サイズだと思ったんだけど、そうではなかったみたい。
 振り出しに戻る…。
 でも、ヒントはあった。
 それが「ピクセル密度」

「ピクセル密度」ってナニよ?

 ピクセル密度は、「1dotを何pixelで表すか?」というもの。
 何の役に立つのかというと、「表示されるものは、どの環境でも、同じ大きさで表示される」。
 17インチモニターでも、15インチモニターでも、そこに表示されるものは、同じ大きさになるってワケ。

 じゃ、なんで、画像が拡大表示しまうのか?
 それは画面と画像のピクセル密度が合っていないから。
 画像のピクセル密度が、画面の密度より低いため、拡大表示されてしまうんだ。
 PhotoShopやGIMPなんかで、印刷解像度を低くした時と似てる。
 「ピクセル密度 = 印刷解像度」みたいに捉えるとわかりやすいかもね。
 Androidでの表示は、WYSWYGの仕組みに近いんだ。

ピクセル密度を調べる

 ピクセル密度は、下記の方法で調べられる。
 実行すると、ログにピクセル密度が書き出されるよ。

	DisplayMetrics metrics = getResources().getDisplayMetrics();

	Log.d( "TEST", "density=" + metrics.density );

 DisplayMetrics では、画面に関する他の情報も取得できる。

	DisplayMetrics metrics = getResources().getDisplayMetrics();
	
	Log.d( "TEST", "density=" + metrics.density );
	Log.d( "TEST", "densityDpi=" + metrics.densityDpi );
	Log.d( "TEST", "scaledDensity=" + metrics.scaledDensity );
	Log.d( "TEST", "widthPixels=" + metrics.widthPixels );
	Log.d( "TEST", "heightPixels=" + metrics.heightPixels );
	Log.d( "TEST", "xDpi=" + metrics.xdpi );
	Log.d( "TEST", "yDpi=" + metrics.ydpi );

 ちなみに。
 「画面サイズ設定ってナニよ?」で画面サイズを調べた時、画面サイズは 533×400 だった。
 それぞれに、ピクセル密度:1.5 をかけると、800×600 になる。
 ちょうど、normalScreens での値とlargeScreens での値になるね。

画像リソースのピクセル密度設定(画像解像度設定)

 拡大表示される原因は、ピクセル密度の違いにあることがわかった。
 じゃ、画面のピクセル密度に、画像のピクセル密度を合わせればいいワケだ。
 でもソレってどうやるの…?

 印刷解像度をいじくったリソースを使用してもダメ。
 ピクセル密度は、画像リソースだけで行う設定じゃないんだ。

 ピクセル密度の設定は、Eclipseプロジェクトでも行うんだ。
 正確には、設定ではないんだけどね。(^_^;

 大抵、画像リソースは、drawable ディレクトリに置くよね?
 それと同じで、それぞれのピクセル密度のディレクトリに、最適化した画像リソースを置くんだ。

	drawable-ldpi … 120dpi( low )
	drawable-mdpi … 160dpi( medium )
	drawable-hdpi … 240dpi( high )

 たとえば、medium で533x400dot の画像の場合、drawable-hdpi に800x600dot に拡大した画像リソースを置く。
 そうすることで、動作環境がdensityDpi = 240(high)だったら、画像リソースはdrawable-hdpi から使用される。
 Android は動作環境に合わせて、それぞれから最適なピクセル密度のリソースを使用してくれるって仕組み。

 最適なリソースがない場合、Android が自動的に拡縮調整してくれる。
 ただし、基準はmedium(160dpi)。
 つまり、drawable にだけ置くと、リソースをmedium として拡縮処理をしてしまう。
 high(240dpi)の環境で表示すると、拡大表示されてしまうんだね。

 これは便利な仕組みなんだけど…ゲームなんかでは困った仕組みだよ。
 ゲームの場合、テレビのようにdpi調整はしないで欲しい…
 この場合、drawable-nodpi ディレクトリにリソースを置く。

	drawable-nodpi … dpi調整をしない

 今回、drawable ディレクトリにあったリソースを、drawable-nodpi へ移すことで、拡大表示されてしまう不具合は解消されたよ。

プログラムからピクセル密度(画像解像度)を設定する

 ピクセル密度は、リソース・ディレクトリで対応できることがわかった。
 でも、ソレってデータがリソースの時だけ。

 画像データを外部参照する時は…?

 外部参照だけじゃなく、プログラム側からピクセル密度を設定したい場合もあるよね。
 そこでピクセル密度を設定できるか、ざっと調べてみたよ。

◆Canvas

	int	 getDensity()
	void	 setDensity(int density)

http://developer.android.com/reference/android/graphics/Canvas.html

◆Bitmap

	int 	getDensity()
	void 	setDensity(int density)

	int 	getScaledHeight(int targetDensity)
	int 	getScaledWidth(int targetDensity)

http://developer.android.com/reference/android/graphics/Bitmap.html

◆BitmapDrawable

	void	 setTargetDensity(int density)
	void	 setTargetDensity(DisplayMetrics metrics)
	void	 setTargetDensity(Canvas canvas)

http://developer.android.com/reference/android/graphics/drawable/BitmapDrawable.html

 以上からすると、今回の不具合には、Viewで使ってるCanvas にピクセル密度を設定すればいいみたい。
 そこでこんな風↓にしてみたよ。

	Canvas canvas;
	Bitmap scrImg = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );
	
	scrImg.setDensity( 160 );
	Log.d("TEST", "scrImg density = " + scrImg.getDensity() );
	
	Canvas scrImg_canv = new Canvas( scrImg );
//	scrImg_canv.setDensity( 160 );
	Log.d("TEST", "Canvas density = " + scrImg_canv.getDensity() );

 Canvas に直接、設定するのではなく、元になるBitmap の密度を設定してみた。
 Bitmap の設定がCanvas に引き継がれるか、確かめたかったため。
 実行してみると、設定値はちゃんと引き継がれていた。

 表示する画像データは、drawable ディレクトリに置いた画像リソース。
 medium(160dpi) と認識されるので、setDensity で設定する値も160にしてある。

 以上で、拡大表示される不具合は解消できたよ。

 この設定は、他のことにも使えそう。
 たとえば、動的に値を変えて、拡縮操作の代わりにするとか。
 まぁ、setBounds の方が使い勝手はいいから、意味はないだろうけどね。(^_^;

Comments are closed.