2013年1月26日土曜日

NotificationのBigPictureStyleで画像全体を表示させてみる

BigPictureStyleでは、画像の中央を中心にして、最大幅450dpに合う大きさへ画像がクロップされる。

でもそれだと、画像が縦長の場合は上下が大きくクロップされてしまい、何が写っているのかよく分からない。



画像の比率によらず、bigPicture部分で、常に画像全体が見られるようにしてみた。
private Bitmap getBigPicture(Resources res, String path) {
 Bitmap canvasBmp = null;

 final float WIDTH_DP = 450.0f;
 final float HEIGHT_DP = 192.0f;

 // ピクセル密度を取得する
 DisplayMetrics metrics = new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(metrics);
 float scaleDensity = metrics.scaledDensity;

 // ピクセル密度を加味してターゲットの幅と高さのピクセル数を計算する
 int targetWidthPx = (int) (WIDTH_DP * scaleDensity);
 int targetHeightPx = (int) (HEIGHT_DP * scaleDensity);

 // 画像読み込みオプションを作成
 BitmapFactory.Options options = new BitmapFactory.Options();
 // メモリが少なくなったらパージできるようにオプションを設定
 options.inPurgeable = true;
 // 画像サイズだけを読み込むようにオプションを設定
 options.inJustDecodeBounds = true;

 // 画像読み込み
 Bitmap srcBmp = BitmapFactory.decodeFile(path, options);

 // 画像の幅と高さのピクセル数を取得する
 int srcWidth = options.outWidth;
 int srcHeight = options.outHeight;

 // Matrixを取得する パディングは0で小さな画像は拡大しない
 Matrix mat = getMatrix(srcWidth, srcHeight, targetWidthPx, targetHeightPx, 0, false);

 // Paintを作成する
 Paint bmpPaint = new Paint();
 bmpPaint.setFilterBitmap(true);

 // ターゲット幅と高さで背景が透明のキャンバスを作成
 canvasBmp = Bitmap.createBitmap(targetWidthPx, targetHeightPx, Bitmap.Config.ARGB_8888);
 Canvas canvas = new Canvas(canvasBmp);

 // 画像サイズだけでなく画像そのものを読み込むようにオプションを設定しなおす
 options.inJustDecodeBounds = false;

 // 実際に画像を読み込み
 srcBmp = BitmapFactory.decodeFile(path, options);

 // 背景が透明のキャンバスに画像を合成する
 canvas.drawBitmap(srcBmp, mat, bmpPaint);

 // メモリ解放
 srcBmp.recycle();
 srcBmp = null;

 Log.i("#getBigPicture()", "scaleDensity=" + scaleDensity + " srcWidth="
   + srcWidth + " srcHeight=" + srcHeight + " targetWidthPx="
   + targetWidthPx + " targetHeightPx=" + targetHeightPx);
 return canvasBmp;
}

/**
 * @param sw ソース幅
 * @param sh ソース高さ
 * @param pw この幅にフィットさせる
 * @param ph この高さにフィットさせる
 * @param padding フィット時に考慮するパディング
 * @param enableMagnify 画像が小さい場合に拡大するか否か
 * @return フィットさせるためのスケール値
 */
private static final Matrix getMatrix(int sw, int sh, int pw, int ph,
  int padding, boolean enableMagnify) {
 float scale = getMaxScaleToParent(sw, sh, pw, ph, padding);
 if (!enableMagnify && scale > 1.0f) {
  scale = 1;
 }
 Log.i("#getMatrix()", "scale=" + scale);
 Matrix mat = new Matrix();
 mat.postScale(scale, scale);
 mat.postTranslate((pw - (int) (sw * scale)) / 2,
   (ph - (int) (sh * scale)) / 2);
 return mat;
}

private static final float getMaxScaleToParent(int sw, int sh, int pw,
  int ph, int padding) {
 float hScale = (float) (pw - padding) / (float) sw;
 float vScale = (float) (ph - padding) / (float) sh;
 return Math.min(hScale, vScale);
}

上記では、bigPictureの最大幅・高さのキャンバスを作り、そのキャンバスサイズに合わせて画像を縮小し、中央に配置する。

BigPictureStyleでは、450dpに合わせて、上下左右に隙間なく画像を表示するよう設計されている。しかしこの設計では、多くの場合に上下や左右に隙間ができても、常に画像全体が写るようにして、どんな画像か把握出来るようにしている。


上記メソッドを呼び出してBigPictureStyleを作るコードのスニペット。
NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(
      notificationBuilder);
bigPictureStyle.bigPicture(getBigPicture(res, path));
bigPictureStyle.setBigContentTitle(contentTitle);
bigPictureStyle.setSummaryText(contentText);
notificationBuilder.setStyle(bigPictureStyle);


結果、bigPicture部分はこんなふうに表示される。