Android drag around many views on a superview
<p>I am starting to think that short of OpenGL I can't drag around a simple view.  Seriously, I have read through 10-15 tutorials for dragging views, and EVERY tutorial shows you how to drag a drawable WITHIN a view, or drag an image INSIDE of an imageview (by matrix transformations).  But NONE of the tutorials tell you how you can drag around a simple view in a superview.  Its really that simple.  I have a superview, I add lets say 10 subviews.  Whatever subview my finger is touching drags around.  I made an app for iOS <a href="http://www.facebook.com/MakeASnowman" rel="nofollow">http://www.facebook.com/MakeASnowman</a> (hits the app store any day) and am trying to build it for Android.  But there is no easy way to do things that are so simple in iOS (two finger rotate, drag around views, etc).  This is what I want:  I have a custom view that draws an image to its canvas.  I then instantiate one or more of those custom views onto a "superview".  I can drag/manipulate those views (meaning im not translating the drawables within those views, but the actual views themselves).</p>

<p>Here is my code that has the right manipulations, except it performs them of the drawable within the view:</p>

<pre><code>package com.spentaklabs.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;


public class MAView extends View
{
Drawable image;
private String imgName;
private float mPosX;
private float mPosY;

private float mLastTouchX;
private float mLastTouchY;

private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;

private ScaleGestureDetector mScaleDetector;

private float mScaleFactor = 1.f;

public MAView(Context context)
{
    super(context);
}

public MAView(Context context,String imgName)
{
    this(context);
    this.imgName = imgName;
    image = context.getResources().getDrawable(R.drawable.hatshairears0);
    image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}

public MAView(Context context, AttributeSet attrs, int defStyle)
{
    super(context,attrs,defStyle);
}

@Override
public void onDraw(Canvas canvas)
{
    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor);
    image.draw(canvas);
    canvas.restore();
}

@Override
public boolean onTouchEvent(MotionEvent ev)
{
    mScaleDetector.onTouchEvent(ev);

    final int action = ev.getAction();

    switch (action)
    {
        case MotionEvent.ACTION_DOWN:
        {
            final float x = ev.getX();
            final float y = ev.getY();

            mLastTouchX = x;
            mLastTouchY = y;

            mActivePointerId = ev.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE:
        {
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final float x = ev.getX(pointerIndex);
            final float y = ev.getY(pointerIndex);

         // Only move if the ScaleGestureDetector isn't processing a gesture.
            if (!mScaleDetector.isInProgress()) {
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;

                mPosX += dx;
                mPosY += dy;

                invalidate();
            }

            mLastTouchX = x;
            mLastTouchY = y;

            break;

        }

        case MotionEvent.ACTION_UP: 
        {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_CANCEL: 
        {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_POINTER_UP:
        {
            // Extract the index of the pointer that left the touch sensor
            final int pointerIndex = (action &amp; MotionEvent.ACTION_POINTER_ID_MASK) &gt;&gt; MotionEvent.ACTION_POINTER_ID_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);

            if (pointerId == mActivePointerId)
            {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = ev.getX(newPointerIndex);
                mLastTouchY = ev.getY(newPointerIndex);
                mActivePointerId = ev.getPointerId(newPointerIndex);
            }
            break;
        }
    }
    return true;
}


private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();

        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

        invalidate();
        return true;
    }
}

}
</code></pre>
