• スポンサードリンク

イメージの拡大縮小・移動・回転できるImageViewを作ってみた〜その2〜Matrix操作編

Android ImageView widget

前回はイメージを拡大縮小・移動・回転できるImageView拡張の下ごしらえを書きました。今回はその続きです。

まずは拡大縮小から。
ズーム状態時にタッチ位置を移動させると、まずrestoreMatrixにより、最後に保存した状態(setStateを呼んだ時点でのMatrixの状態)までMatrixを巻き戻します。Matrixは累積的に拡大縮小・移動・回転を適用可能なのですが、誤差も累積してしまうので、ユーザー操作の開始時点まで毎回巻き戻しています。
続いて、タッチ位置から拡大率を計算して範囲チェック後拡大率をMatrixに適用します。

Matrixを使ったイメージの拡大縮小には数種類のメソッドが用意されていますが、今回使用しているのは基点を指定可能なMatrix#postScale(float sx, float sy, float px, float py)です。前の2つの引数がそれぞれ横・縦方向の拡大率で、後ろ2つの引数が拡大縮小の基点座標・・・拡大縮小した時に移動しない座標・・・となります。なお、今回の使い方ではImageViewのローカル座標系※で指定します。このメソッドはMatrixが変更されるとtrueを返してくれますので、変更された場合のみ続きの処理を行います。
なお、この時点では自前のMatrixを変更しただけで、ImageView自体には適用されていません。ImageView様にMatrixにしたがって表示するようにお願いするには、ImageView#setImageMatrixを呼び出します。APIレファレンスにはこのメソッドの説明は1行もありませんね。それぐらいは読んで分かれよって偉大な魔法使いの思し召しかな?

※ローカル座標:それぞれのViewの左上を(0,0)とし、右方向および下方向が正となる座標系です。

拡大率は座標そのものではなく、座標の差(タッチ位置間の距離)を使って計算します。三平方の定理ってやつですね。
x,y座標それぞれの差の2乗和の平方根ってことで、普通であればこんな風に書きます。

でも実はMathクラスには同じ計算を高速で誤差も少なく計算してくれる便利なメソッドが有るのです。それがMath#hypotです。上式の右辺の計算を一発でしてくれます。

Math#hypotを使うとこうなります。

ということで、拡大率の計算はこちら。ピンチによる拡大縮小の定番ですね。

引き続いて、タッチ&ドラッグによるイメージの移動です。タッチ位置から移動距離を計算してMatrixに適用。Matrxiが変更されればImageView様にお願いします。
コアとなる部分はこんな感じで、拡大縮小とほとんど同じになります。違うのはMatrix#postScaleの代わりにMatrix#postTranslateを呼び出すことです。
このメソッドでもMatrixが実際に変更された場合にtrueを返してくれますので、Matrixが変更された時だけ処理を続けます。

そして最後は回転。こんな感じです。流石に見飽きて来ましたよね。
回転角度を計算した後、呼び出すのがMatrix#postRotate(float degrees, float px, float py)になるだけです。

タッチ位置から回転角度を求めるのはこちらになります。
2点タッチ位置をベクトルとみなして、最初にタッチした時のベクトルと、現在のタッチから求めたベクトル間の角度を求めます。
ベクトルとして扱うには1本目の指と2本目の指を区別する必要がありますので、それぞれのIDをmPrimaryIdフィールドとmSecondaryIdフィールドに保存してあります。

2ベクトル間の角度の計算って今でも高校生で習うのかな?
ベクトルZa=(x0,y0)とベクトルZb=(x1,y1)のなす角度=Φとすると、
$latex \cos \; \phi =\frac{Z_{a}Z_{b}}{\left| Z_{a} \right|\left| Z_{b} \right|}=\frac{\left( x_{0}x_{1}\; +\; y_{0}y_{1} \right)}{\sqrt{\left( x_{0}^{2}\; +\; y_{0}^{2} \right)\left( x_{1}^{2}\; +\; y_{1}^{2} \right)}} &s=2$なので、
$latex \phi =\cos ^{-1}\left( \cos \; \phi \right) &s=2$から角度Φを計算します。
ただし、cos-1(Math#acos)の結果は0-π[ラジアン](0〜180[度])なので、
ZaとZbの外積を使って0±2π[ラジアン](0±360[度])に拡張します。

回転をしようとするといきなり数式が出て来ますが避けては通れません。こんなもんだと諦めましょう。
拡大縮小・移動に回転のコア部分は以上です。やったやった出来た〜って大喜びしたいところですが、肝心なところがいっぱい抜けてますね。タッチ操作とか移動範囲のチェックとか。でも長くなってきたので次回。お疲れ様でした。

全体のソースはこちら(GitHub)

« »

  • スポンサードリンク