はじめにの前に
ゆくゆくはスマホやタブレットでのアプリ作ろうと思っているけど、まだまだ作成というよりは、部品を作ってどういう実装ができるかイメージしてみようとしている感じ。
とりあえず、取っ掛かりとしては、最初はPCにはない、スマホやタブレット独自の実装の分野に手を出してみよう!という考えで、なにか実装してみようと思ってました。
はじめに
そこで、スマホやタブレットぽい操作っていうとどんなのだろう?って考えると指を使った操作で、スワイプやピンチインピンチアウトだなぁって思いました。
将来的に拡大縮小をするアプリを考えていたので、画像をピンチアウトピンチインして拡大縮小するものを実装してみました。
今回は
Androidで動くもので、Mono for Androidで、今回はImgViewのクラスを拡張してみました。
将来的にアプリを実装する際に必要なライブラリがC#で書かれていて、portingもある程度できそうなので、Mono for Androidを使っています。あんまり使う人いないけど、備忘の意味も含めてソースをさらします。
ライセンスは、new BSDで。
参考にしたのは、以下か2つです。
http://stackoverflow.com/questions/12594918/mono-android-pinch-zoom-whilst-overriding-ondraw
http://stackoverflow.com/questions/12169905/zoom-and-panning-imageview-android
注意点
指で2点を抑えていて、1点を離す直前の最後に動いた座標を取得しておかないと、1点になった際に動きが乱れるようです。
そこを注意して実装してみました。間違えている点あったら教えて下さい。
ソース
ソースは以下です。
class ImageViewExt :ImageView { private static int INVALID_POINTER_ID = -1; private float mPosX; private float mPosY; private float mLastTouchX; private float mLastTouchY; private float mLastGestureX; private float mLastGestureY; private int mActivePointerId = INVALID_POINTER_ID; private ScaleGestureDetector mScaleDetector; private static float mScaleFactor = 1.0f; public ImageViewExt(Context context):base(context) { mScaleDetector = new ScaleGestureDetector(Context,new ScaleListener()); } public override bool OnTouchEvent (MotionEvent e) { mScaleDetector.OnTouchEvent (e); switch (e.Action & MotionEventActions.Mask) { case MotionEventActions.Down: if (!mScaleDetector.IsInProgress) { float x = e.GetX (); float y = e.GetY (); mLastTouchX = x; mLastTouchY = y; mActivePointerId = e.GetPointerId (0); } break; case MotionEventActions.Pointer1Down: if (mScaleDetector.IsInProgress) { float gx = mScaleDetector.FocusX; float gy = mScaleDetector.FocusY; mLastGestureX = gx; mLastGestureY = gy; } break; case MotionEventActions.Move: if (!mScaleDetector.IsInProgress) { int pointerIdx = e.FindPointerIndex (mActivePointerId); float x = e.GetX (pointerIdx); float y = e.GetY (pointerIdx); float dx = x - mLastTouchX; float dy = y - mLastTouchY; mPosX += dx; mPosY += dy; Invalidate (); mLastTouchX = x; mLastTouchY = y; } else { float gx = mScaleDetector.FocusX; float gy = mScaleDetector.FocusY; float gdx = gx - mLastGestureX; float gdy = gy - mLastGestureY; mPosX += gdx; mPosY += gdy; Invalidate (); mLastGestureX = gx; mLastGestureY = gy; } break; case MotionEventActions.Up: mActivePointerId = INVALID_POINTER_ID; break; case MotionEventActions.Cancel: mActivePointerId = INVALID_POINTER_ID; break; case MotionEventActions.PointerUp: int pointerIdx2 = (int)(e.Action & MotionEventActions.PointerIndexMask) >> (int)MotionEventActions.PointerIndexShift; int pointerId = e.GetPointerId (pointerIdx2); if (pointerId == mActivePointerId) { int NewPointerIndex = pointerIdx2 == 0 ? 1 : 0; mLastTouchX = e.GetX (NewPointerIndex); mLastTouchY = e.GetY (NewPointerIndex); mActivePointerId = e.GetPointerId (NewPointerIndex); } else{ int TempPointerIdx = e.FindPointerIndex(mActivePointerId); mLastTouchX = e.GetX(TempPointerIdx); mLastTouchY = e.GetY(TempPointerIdx); } break; } return true; } protected override void OnDraw (Canvas canvas) { canvas.Save (); canvas.Translate (mPosX, mPosY); if (mScaleDetector.IsInProgress) { canvas.Scale (mScaleFactor, mScaleFactor, mScaleDetector.FocusX, mScaleDetector.FocusY); } else { canvas.Scale (mScaleFactor, mScaleFactor,mLastGestureX,mLastGestureY); } base.OnDraw (canvas); canvas.Restore(); } private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener { public override bool OnScale (ScaleGestureDetector detector) { mScaleFactor *= detector.ScaleFactor; //小さすぎず大きすぎず処理 mScaleFactor = Math.Max(0.1f, Math.Min(mScaleFactor, 10.0f)); return true; } } }
動画
ピンチインして、うごかして、ピンチアウトしてキモい画像がアップになって、怖いもの見たさで、ピンチアウト拡大したけど、あまりのキモさにピンチインして小さくしたっていう動画です。