こんにちは、android事業室の平井(@shinbashi)です。

唐突ですが
「android端末の、画面横幅に合わせて比率を崩さずに画像を配置したい」
なんてことがよくありますよね。よくありますよね。
僕は最近そんな事がありました。そこで困った問題にぶち当たったので、そのことを書きたいと思います。
androidでサイズの問題と言えば 「Density-independent Pixels」いわゆるdipでサイズを
決定すればイイッ!って話になりますよね。
でも今回は全然関係ないです。そもそもdipってpixelに依存しないって意味ですし。
今回はpixelに依存したいという話なので。

それではどうしたのか書きたいと思います。1番と2番は試してみたけど出来なかった、
もしくは試したくもなかった方法ですので、3番から読むと良いと思います。 

やりたいこと

画面の横幅いっぱいに背景を表示して、その画像の上にピンポイントで指定した位置に
背景のスケールと同じスケールで画像を表示したい。


720ox
理想のイメージ

1. dpで指定するッ!

試しに、720x1240の画面サイズで、xhdpi(320dpi)の画面密度の端末にたいして
横幅いっぱいを指定する場合、dipで指定すると360dp(1dp=2px)となります。
他の代表的な画面サイズや画面密度の組み合わせだと以下の様になります。
 
xhdpihdpimdpi
720x1240360dp480dp720dp
540x900270dp360dp540dp
480x800240dp320dp480dp

色の付いている部分が組み合わせとして多いパターンですね。
画面サイズの比率と、画面密度の比率は一致しません。dpであらゆる端末で横幅ピッタリに
指定することは不可能でした。

というか、画面横幅に合わせて画像を表示したいのであれば
ImageViewのScaleTypeをCenterCropにすればよいという話ですね。
はい、画像一つ表示するだけならそれでオッケーです。

今回は、背景となる画像の上に被せて、指定した位置ピッタリに画像を表示したい。
なんならアニメーションしたい。
そんな話だったとしましょう。
正しい座標に、正しいスケールで表示しなければなりません。
CenterCropとか使っちゃったら、背景の画像がどんな比率で拡大されたのか知る術はありません(あるかもしれません)。特に考えずに画像を設置した場合、端末が変わるとずれます。
背景をCenterCrop、ImageViewで画像を適当に配置した場合、下の例だと1.125倍くらいずれます。
はみ出してしまったポチもなんだか悲しげですね。
720ox480px
横幅720 xhdpi     横幅480 hdpi

2. layout-000x000で指定するッ!

drawable-xhdpiのようなディレクトリのように、layoutもハイフン以降に色々指定して
条件に合わせて変えることができます。
「layout-small」とか
「layout-480x320」
こんな形で指定することができます。
やった!コレで解決!第三部完!
でも少なくとも僕はレイアウトが変更になるたびに何個も似たようなファイルのサイズを計算
しなおして修正するなんてしたくないです。

3. 画面サイズを取得して動的にサイズを指定する

はい、結局こういう手段に落ち着きました。
横幅720pxというものを基準に全ての画像を作成し、とりあえずdrawable-xhdpiにぶち込みます。
他のdpiのディレクトリにも、正しい比率で拡縮したものを入れておきます。
dpで指定出来ないので、画像のサイズはonCreateなどで全てBitmapを作成し、ImageViewにsetImageBitmapメソッドでセットするとします。

画面サイズに応じた画像のサイズ=実際の画像サイズ×(画面横幅÷(720×scaleDensity))
*720 = 今回基準にしたサイズ *scaleDensity = 画面密度の比率。xhdpiだったら2.0,hdpiだったら1.5

こんな計算で出るんじゃないでしょうかコードにすると
 
public static int is2px(Activity activity, int is)
{
	WindowManager wm = (WindowManager)activity.getSystemService(Activity.WINDOW_SERVICE);
	Display disp = wm.getDefaultDisplay();
	DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
	int width = 0;
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
		Point point = new Point();
		disp.getSize(point);
		width = point.x;
	} else {
		width = disp.getWidth();
	}
	float ratio = (float)width / (float)(IMAGE_BASE_WIDTH * metrics.scaledDensity) ;
	return (int)(is * ratio);
}
こんなかんじでしょうか、isは多分image sizeの略だと思います。
Bitmap bmp = BitmapFactory.decodeResource(activity.getResources(), resourceId);
bmp = Bitmap.createScaledBitmap(bmp, is2px(activity, bmp.getWidth()), is2px(activity, bmp.getHeight()), false);
こんな風に使えると思います。
全部こうして指定してやれば、背景の比率に合わせて画像を表示したり、座標を指定したりできます。
めでたしめでたし。

4. 横に合わせたら縦がはみ出た

そうは問屋がおろしませんでした。横幅だけ気にしてたら、縦にはみ出ました。
そんなわけで、横幅に合わせた場合、当然縦は合いません。
一番小さなものに合わせた場合、画面は縦にどのくらい使えるのか調べました。

画面サイズ比較どのサイズまで対応するかにもよりますが、日本の普通の携帯端末だったら小さくても320x480が多いと思いますので、変な画面サイズに対応する気がないのであれば、720x1280を基準にした場合には、下に200pxほど余白が生まれるように作れば、画面からはみ出すことは無さそうです。





















そんな訳で、あまり役に立たなそうではありますが、最近僕がandroidのアプリ開発で困った出来事について書かせて頂きました。
もっと上手いやり方があるような気がしてならないので
「そんなことしなくても、こうすればいいのに」
というような事がございましたら、是非コメントをいただければと思います。