Android: ギャラリーのような画像ドラッグ(仮)

コメントにて、Androidに元々入っているギャラリーのような動きをさせるその後はどうなったんだ!?という質問を受けましたので、公開してしまおうと思います。ただ、当然ながらこれは私の独自のやり方なので、はっきりいって正しいかどうかはわかりません。

あと、画像の拡大・縮小にはAnimationを使っております。ドラッグ移動はできますが、ビヨーンといった跳ね返りとかは実装していません(そこまで精神がもたなかった)。やろうと思えばできるんでしょうけどねぇ〜。
イベントはダブルタップとかドラッグとか使うので、OnGestureListenerとかOnDoubleTapListenerは必須。implementsしておいてください。

で、画像の拡大・縮小はそんなに難しくないので、省略するとして(オィ)、問題はドラッグ移動なわけです。
画像のドラッグについては、下のメソッドで動くはず。なんで動くのか?を説明するのは結構難儀ですが。
ちなみに、拡大率は2倍固定です(面積は4倍)。

// private Rect mRect
// private Matrix mMatrix = new Matrix();
// private float[] mValues = new float[9];
private void imageMove(ImageView iv, MotionEvent e) {
	if (e.getHistorySize() > 0) {
		int x = (int) e.getHistoricalX(0) - (int) e.getX();
		int y = (int) e.getHistoricalY(0) - (int) e.getY();
		iv.scrollBy(x, y); // ①

		int new_x = iv.getScrollX();
		int new_y = iv.getScrollY();

		mRect = iv.getDrawable().getBounds();
		mMatrix = iv.getImageMatrix();
		mMatrix.getValues(mValues);

		// ②
		// 画面上の画像の横サイズ
		int iw = (int) ((int) mRect.width() * mValues[Matrix.MSCALE_X]);
		// 画面上の画像の縦サイズ
		int ih = (int) ((int) mRect.height() * mValues[Matrix.MSCALE_Y]);
		// 画像の横サイズの半分
		int iw_harf = iw / 2;
		// 画像の縦サイズの半分
		int ih_harf = ih / 2;
		// 縦方向の黒くなっているところの高さ
		int black_out_w = (iv.getWidth() / 4) - iw_harf;
		// 縦方向の黒くなっているところの高さ
		int black_out_h = (iv.getHeight() / 4) - ih_harf;

		Log.d("imageMove", "new_x=" + Integer.toString(new_x) + ", new_y="
				+ Integer.toString(new_y));
		Log.d("imageMove", "_x=" + Integer.toString(iw) + ", _y="
				+ Integer.toString(ih));
		Log.d("imageMove", "black_out_w=" + Integer.toString(black_out_w)
				+ ", black_out_h=" + Integer.toString(black_out_h));

		// 拡大画像がはみ出ないようにする処理
		if (Math.abs(new_x) > black_out_w || Math.abs(new_y) > black_out_h) {
			// new_x, new_yが0の場合を考慮する必要あり
			if (new_x == 0 && new_y == 0) {
				// 何もしない ③
			} else if (new_x == 0) {
				iv.scrollTo(0, new_y
						/ Math.abs(new_y)
						* (int) Math.min(Math.abs(new_y), Math
								.abs(black_out_h))); // ④
			} else if (new_y == 0) {
				iv.scrollTo(new_x
						/ Math.abs(new_x)
						* (int) Math.min(Math.abs(new_x), Math
								.abs(black_out_w)), 0); // ⑤
			} else {
				iv.scrollTo(new_x
						/ Math.abs(new_x)
						* (int) Math.min(Math.abs(new_x), Math
								.abs(black_out_w)), new_y
						/ Math.abs(new_y)
						* (int) Math.min(Math.abs(new_y), Math
								.abs(black_out_h))); // ⑥
			}
		}
	}
}

では説明しまーす。

まず、①で、普通のドラッグ移動を行います。
ヒストリーが取れるので、前の場所から移動した分だけスクロールするよって処理です。

②で、画像の大きさや画面からはみ出る範囲や背景が黒くなっている部分の大きさを計算・取得します。正直ここが一番面倒というか、わかると簡単なんですが、辿り着くのに数日かかりました。アホです。図にして考えるとやりやすい(手書きでガシガシ書いたので、残ってない…)。

Matrixから画像の拡大率を取ります。これは何故かというと、画面よりもでかい画像を読んだら縮小して表示されるんですが、Rect.width()に本来の画像の横サイズが入っているので、それに拡大率をかけることによって画面内における画像の横幅を取得します。縦幅も同様に。

で、画像が画面とまったく同じ大きさでもない限り、黒抜きにされるから、その黒抜きの幅を取得します。これは表現として合ってないかもしれませんが。

③です。これ以降が、画像どこかしらが画面の端っこにきている場合の処理です。普通の場合はscrollByをしていましたが、それ以外の場合はscrollToで上書きする感じになります。

iv.getScrollX()は、親要素に対してスクロールした量だったかな…。割り算をやるので、0の場合は処理しないように制御しないと、例外が発生してアプリが落ちますので、ご注意を。それらが、③、④、⑤。基本は⑥の処理。

という感じです。すみません、眠い頭で書いてるので、説明がグダグダかもしれませんが、メソッド自体は合ってると思います。(但しアニメーションで画像を2倍にしたときに限る)

以上です。


タグ Android, Java | パーマリンク.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です