• スポンサードリンク

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

Android ImageView widget

AndroidのImageViewを拡張してイメージの拡大縮小・移動・回転できるImageViewを作ってみました。

タッチ操作で拡大縮小・移動できるのはこことか、こことかいろいろな人が作成されているみたいですが、回転まで出来るのはなさそうなので、自分で作ってしまえ、ってことで。

皆さんImageView#onDrawをオーバーライドしたりイメージをBitmapに変換してフィールドとして保持してたりと苦労されているみたいです。でも実はMatrixの設定だけで拡大・縮小・移動・回転までぜ〜んぶ出来てしまいました。処理の重くなるonDrawのオーバーライドも必要ありません。もちろんタッチの操作への応答とか、移動範囲のチェックとかは必要ですけどね。

まず最初に基本的なところから。
イメージの拡大縮小・移動・回転は全てMtarixクラスというのを使うことで行います。OpenGL系で2D/3D描画をしたことの有る人ならば、いやというほどでてきますよね。ImageViewの場合は2Dのみなので少し簡単です。

Viewから直接継承して作るのあれば、Viewの大きさを決めるためのonMeasureから描画を行うonDrawまで全部実装しないといけなくなるので大変なのですが、幸いなことにImageViewには標準機能としてMatrxを使用して描画するモードが備わっています。
それが、謎のScaleType.MATRIXImageView#setImageMatrix/getImageMatrixなのです(^^)

作成の基本方針としては、

  1. 拡大縮小・移動に加えて回転もできるようにする。でなきゃ後発品の意味が無いよねってことで。イメージを回転させること自体は超簡単なのですが、回転させた時の移動範囲のチェックが曲者で少し苦労する羽目に…予想はしていたけど(;_;)
  2. 画像の管理・描画は全て継承元のImageView本体に任せる。偉い魔法使い達の作ったImageView様にひよっこ魔法使いでは太刀打ちできませんから。
  3. 拡張するのは基本的に拡大縮小・移動・回転とそれに付随したタッチ操作への応答・移動範囲等のチェックのみ。元のImageViewの機能を損ねるようなことは出来る限りしない。
  4. タッチ操作への応答はGestureDetectorではなく自前で実装する。いや別にGestureDetectorが嫌いなわけではありませんけど。
  5. 表示されているイメージ全部または一部分を選択して取得するためのヘルパーメソッドを追加する。これもMatrxiを扱わないとダメなのでおまけでくっつけました。
  6. 名前はZoomImageViewとする。どこかで聞いたような名前かも。安直ですみません。ZoomIngeViewにしようかとも思ったんですけどね(^_^;)

まず最初にオーバーライドするのは定番のImageView#onLayoutです。
このメソッドはLayoutParamや内包するイメージの大きさ、ScaleTypeなどに応じてImageViewの大きさが決定されてから呼び出されるので好都合なのです。ちなみに、ImageView#setImageResourceとかsetImageBitmapとかsetImageDrawableとかでImageViewにイメージをセットすると、最終的にはonLayoutが呼び出されるので、イメージ変更時にも追加ですること特にありません\(^o^)/

これだけ見てもまったくわけわかりませんね。mStateってのはとりあえず置いといて処理の実体はinitに有ります。

ZoomingImageView#initですることは、

  1. イメージのアスペクト比(高さと幅の比)を保ったまま、ImageViewの表示領域に収まるようにImageView様にお願いして拡大縮小・移動してMatrixとイメージ矩形をセットしてもらう。偉い魔法使いの作った使い魔様ですからね、自分でするより確実です。
  2. ImageView様にセットして頂いたMatrxの値を頂戴して保存しておく。
  3. その時点での拡大率を最低拡大率として保存しておく。
  4. 拡大縮小等を行わない元のイメージの大きさだけを取得して保存しておく。イメージ自体はImageView様任せです。
  5. 後々の移動範囲の制限用のフィールドなども設定する。
  6. 最後にごめんなさいやっぱり自前のMatrixを使いたいです、とImageView様にお願いする。

1.の「ImageView様へのお願い」はこんな感じ。

諸先輩方も触れられているように、ImageView#setScaleTypeやsetImageMatrixとかを呼んでも直ぐにそれが適用されるわけではありません。特に、最近になって(4.3とか4.4)ImageView内でのMatrixの扱いが変わってちょっと面倒です。でも、上の2行目ImageView#setFrameを呼び出せば、scaleTypeと保持しているイメージに合わせてMatrixとイメージ矩形(イメージはDrawableまたはその継承クラスとして格納されています)を設定してくれます。

本当に呼び出したいのはImageViewのprivateメソッドなので将来的にImageViewの実装が変わると動かなくなるかもしれませんが、ソースを読んだ限りでは副作用なしで使えそうなのはこのメソッドだけでした。実装が変わって動かなくなった時は・・・リフレクションに頼るか自前で古いImageViewのソースから作るしか無いですが、Viewの移動・サイズ変更のためのメソッドなのでそんなには変わらないだろうと勝手に思ってます。

« »

  • スポンサードリンク