title: Issue with touch detection on the bitmaps in Canvas
body: <p>I am developing an application in which the user can add images in the canvas and can play with them. </p>
  
  <p>Following is my requirement :</p>
  
  <ol>
  <li><p>To add bitmaps in the canvas one after the another and if a bitmap has been added in the canvas and moved after adding on the canvas then the next time the user adds another bitmap , the position of the already added bitmap needs to be preserved ----- Canvas already saves it by default</p></li>
  <li><p>I have three classes for detecting the gestures </p>
  
  <ol>
  <li>MoveGestureDetector</li>
  <li>ScaleGestureDetector</li>
  <li>RotateGestureDetector</li>
  </ol></li>
  </ol>
  
  <p><strong>PROBLEM:</strong> I am able to successfully detect the touch events on the  bitmap which I add on the canvas but not on the previous bitmaps that are previously added on the canvas. So if I add one bitmap on the canvas I can successfully detect touch events on it, but if I add another bitmap then I would not be able to detect touch events on the older one and would be able to detect touch events on the new one only</p>
  
  <p>I have tried almost everything but I am not able to get any insights.</p>
  
  <p>This is how I added the canvas view </p>
  
  <pre><code>FrameLayout gcfl = (FrameLayout) findViewById(R.id.gestureControlledFrameLayout);       
      final GestureViewPort gvp = new GestureViewPort(this);
      gcfl.addView(gvp);
  </code></pre>
  
  <p>This is my GestureViewPort.java</p>
  
  <pre><code>public class GestureViewPort extends View {
  final static int FLIP_VERTICAL = 1;
  final static int FLIP_HORIZONTAL = 2;  
  private Context context;
  private static Bitmap bitmap;
  public static List&lt;Layer&gt; layers = new ArrayList&lt;Layer&gt;();  
  private List&lt;Bitmap&gt; bitmapList;
  
  public GestureViewPort(Context context) {
      super(context);  
      this.context = context;
  
      this.bitmapList = ProductDetailActivity.bitmapList;      
  
      if(bitmapList!=null)
      {  
          for (int bitmapIndex = 0; bitmapIndex &lt; bitmapList.size();bitmapIndex++)
          {                           
              if(bitmapList.get(bitmapIndex)!=null )
              {
                  Layer l2 = new Layer(context, this, bitmapList.get(bitmapIndex));
                  for(int layerIndex = 0 ; layerIndex &lt; layers.size();layerIndex++)
                  {
                      if(bitmapList.get(bitmapIndex).sameAs(layers.get(layerIndex).getBitmap()))
                      {                                                                   
                          if(layers.get(layerIndex).getMoveMatrix()!=null)
                          {   
                              l2.setMoveMatrix(layers.get(layerIndex).getMoveMatrix());
                          }
                          layers.remove(layers.get(layerIndex));  
                          break;
                      }
                  }                       
                  layers.add(l2);
                  invalidate();
              }
          }
          }
      }
  
  @Override
  protected void onDraw(Canvas canvas) {
          for(Layer l : layers)
          {
              if(l.getMoveMatrix() != null)
              {
                  l.draw(canvas,l.getMoveMatrix());
              }
              else
              {
                  l.draw(canvas,null);    
              }
          } 
  
  private Layer target;
  
  @SuppressLint("ClickableViewAccessibility") @Override
  public boolean onTouchEvent(MotionEvent event) {
      if (event.getAction() == MotionEvent.ACTION_DOWN) {
          target = null;
          for (int i = layers.size() - 1; i &gt;= 0; i--)        
          {
              Layer l = layers.get(i);
              if (l.contains(event)) {
                  target = l;
                  layers.remove(l);
                  layers.add(l);
                  invalidate();
                  break;
              }
          }
      }
      if (target == null) {
          return false;
      }       
      return target.onTouchEvent(event);
  }
  
  public int getTopLayer(){
      return layers.size() -1;
  }
  
  public void removeLayer(int topLayer){
      layers.remove(topLayer);
      invalidate();
  }
  
  public void addLayer(Bitmap drawable){   
      Layer l = new Layer(context, this, drawable);
      layers.add(l);
      invalidate();
  }
  
  public void flip(int type){
      target.flip(type);
  }    
  </code></pre>
  
  <p>}</p>
  
  <p>This is my Layer.java</p>
  
  <pre><code>public class Layer {
   Matrix matrix = new Matrix();
   Matrix inverse = new Matrix();
   RectF bounds;
   View parent;
   Bitmap bitmap;   
   Context context;   
   MoveGestureDetector mgd;
   ScaleGestureDetector sgd;
   RotateGestureDetector rgd;
   Matrix moveMatrix ;
  
   public Layer(){}
  
  public Layer(Context ctx, View p, Bitmap b) {
      this.context = ctx;
      parent = p;
      bitmap = b;
      bounds = new RectF(0, 0, b.getWidth(), b.getHeight());
  
      mgd = new MoveGestureDetector(context, mgl);
      sgd = new ScaleGestureDetector(context, sgl);
      rgd = new RotateGestureDetector(context, rgl);
      matrix.postTranslate(50 + (float) Math.random() * 50, 50 + (float) Math.random() * 50);
  }
  
    /**
   * @return the bitmap
   */
  public Bitmap getBitmap() {
      return bitmap;
  }
  
  /**
   * @return the moveMatrix
   */
  public Matrix getMoveMatrix() {
      return moveMatrix;
  }
  
  /**
   * @param moveMatrix the moveMatrix to set
   */
  public void setMoveMatrix(Matrix moveMatrix) {
      this.moveMatrix = moveMatrix;
  }
  
  
  public boolean contains(MotionEvent event) {
      matrix.invert(inverse);
      float[] pts = {event.getX(), event.getY()};      
      Log.e("points",String.valueOf(pts[0])+" " + String.valueOf(pts[1]));
      Log.e("bounds",String.valueOf(bounds.right)+" " + String.valueOf(bounds.bottom));
      inverse.mapPoints(pts);       
          if (!bounds.contains(pts[0], pts[1])) {
              return false;
          }
  
      return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
  
  }
  
  public boolean onTouchEvent(MotionEvent event) {
      mgd.onTouchEvent(event);
      sgd.onTouchEvent(event);
      rgd.onTouchEvent(event);
      return true;
  }
    public void draw(Canvas canvas, Matrix modifiedMatrix) {
      if(modifiedMatrix !=null)
      {
          canvas.drawBitmap(bitmap, modifiedMatrix, null);    
      }
      else
      {
          canvas.drawBitmap(bitmap, matrix, null);
      }
  }
     SimpleOnMoveGestureListener mgl = new SimpleOnMoveGestureListener() {
      @Override
      public boolean onMove(MoveGestureDetector detector) {
          PointF delta = detector.getFocusDelta();            
          matrix.postTranslate(delta.x, delta.y);  
          setMoveMatrix(matrix);                     
          parent.invalidate();
          return true;
      }
  };
  
  SimpleOnScaleGestureListener sgl = new SimpleOnScaleGestureListener() {
      @Override
      public boolean onScale(ScaleGestureDetector detector) {
          float scale = detector.getScaleFactor();
          matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
          parent.invalidate();
          return true;
      }
  };
  
  SimpleOnRotateGestureListener rgl = new SimpleOnRotateGestureListener() {
      @Override
      public boolean onRotate(RotateGestureDetector detector) {
          matrix.postRotate(-detector.getRotationDegreesDelta(), detector.getFocusX(), detector.getFocusY());
          parent.invalidate();
          return true;
      };
  };
  }
  </code></pre>
  
  <p>This is my MoveGestureDetector.java</p>
  
  <pre><code> public class MoveGestureDetector extends BaseGestureDetector {
  
  /**
   * Listener which must be implemented which is used by MoveGestureDetector
   * to perform callbacks to any implementing class which is registered to a
   * MoveGestureDetector via the constructor.
   * 
   * @see MoveGestureDetector.SimpleOnMoveGestureListener
   */
  public interface OnMoveGestureListener {
      public boolean onMove(MoveGestureDetector detector);
      public boolean onMoveBegin(MoveGestureDetector detector);
      public void onMoveEnd(MoveGestureDetector detector);
  }
  
  /**
   * Helper class which may be extended and where the methods may be
   * implemented. This way it is not necessary to implement all methods
   * of OnMoveGestureListener.
   */
  public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
      public boolean onMove(MoveGestureDetector detector) {
          return false;
      }
  
      public boolean onMoveBegin(MoveGestureDetector detector) {
          return true;
      }
  
      public void onMoveEnd(MoveGestureDetector detector) {
          // Do nothing, overridden implementation may be used
      }
  }
  
  private static final PointF FOCUS_DELTA_ZERO = new PointF();
  
  private final OnMoveGestureListener mListener;
  
  private PointF mCurrFocusInternal;
  private PointF mPrevFocusInternal;  
  private PointF mFocusExternal = new PointF();
  private PointF mFocusDeltaExternal = new PointF();
  
  
  public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
      super(context);
      mListener = listener;
  }
  
  @Override
  protected void handleStartProgressEvent(int actionCode, MotionEvent event){
      switch (actionCode) { 
          case MotionEvent.ACTION_DOWN: 
              resetState(); // In case we missed an UP/CANCEL event
  
              mPrevEvent = MotionEvent.obtain(event);
              mTimeDelta = 0;
  
              updateStateByEvent(event);
              break;
  
          case MotionEvent.ACTION_MOVE:
              mGestureInProgress = mListener.onMoveBegin(this);
              break;
      }
  }
  
  @Override
  protected void handleInProgressEvent(int actionCode, MotionEvent event){    
      switch (actionCode) {
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_CANCEL:
              mListener.onMoveEnd(this);
              resetState();
              break;
  
          case MotionEvent.ACTION_MOVE:
              updateStateByEvent(event);
  
              // Only accept the event if our relative pressure is within
              // a certain limit. This can help filter shaky data as a
              // finger is lifted.
              if (mCurrPressure / mPrevPressure &gt; PRESSURE_THRESHOLD) {
                  final boolean updatePrevious = mListener.onMove(this);
                  if (updatePrevious) {
                      mPrevEvent.recycle();
                      mPrevEvent = MotionEvent.obtain(event);
                  }
              }
              break;
      }
  }
  
  protected void updateStateByEvent(MotionEvent curr) {
      super.updateStateByEvent(curr);
  
      final MotionEvent prev = mPrevEvent;
  
      // Focus intenal
      mCurrFocusInternal = determineFocalPoint(curr);
      mPrevFocusInternal = determineFocalPoint(prev);
  
      // Focus external
      // - Prevent skipping of focus delta when a finger is added or removed
      boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount();
      mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x,  mCurrFocusInternal.y - mPrevFocusInternal.y);
  
      // - Don't directly use mFocusInternal (or skipping will occur). Add 
      //   unskipped delta values to mFocusExternal instead.
      mFocusExternal.x += mFocusDeltaExternal.x;
      mFocusExternal.y += mFocusDeltaExternal.y;        
  }
  
  /**
   * Determine (multi)finger focal point (a.k.a. center point between all
   * fingers)
   * 
   * @param MotionEvent e
   * @return PointF focal point
   */
  private PointF determineFocalPoint(MotionEvent e){
      // Number of fingers on screen
      final int pCount = e.getPointerCount(); 
      float x = 0f;
      float y = 0f;
  
      for(int i = 0; i &lt; pCount; i++){
          x += e.getX(i);
          y += e.getY(i);
      }
  
      return new PointF(x/pCount, y/pCount);
  }
  
  public float getFocusX() {
      return mFocusExternal.x;
  }
  
  public float getFocusY() {
      return mFocusExternal.y;
  }
  
  public PointF getFocusDelta() {
      return mFocusDeltaExternal;
  }
  </code></pre>
  
  <p>}</p>
  
  <p>This is RotateGestureDetector.java</p>
  
  <pre><code>public class RotateGestureDetector extends TwoFingerGestureDetector {
  
  /**
   * Listener which must be implemented which is used by RotateGestureDetector
   * to perform callbacks to any implementing class which is registered to a
   * RotateGestureDetector via the constructor.
   * 
   * @see RotateGestureDetector.SimpleOnRotateGestureListener
   */
  public interface OnRotateGestureListener {
      public boolean onRotate(RotateGestureDetector detector);
      public boolean onRotateBegin(RotateGestureDetector detector);
      public void onRotateEnd(RotateGestureDetector detector);
  }
  
  /**
   * Helper class which may be extended and where the methods may be
   * implemented. This way it is not necessary to implement all methods
   * of OnRotateGestureListener.
   */
  public static class SimpleOnRotateGestureListener implements OnRotateGestureListener {
      public boolean onRotate(RotateGestureDetector detector) {
          return false;
      }
  
      public boolean onRotateBegin(RotateGestureDetector detector) {
          return true;
      }
  
      public void onRotateEnd(RotateGestureDetector detector) {
          // Do nothing, overridden implementation may be used
      }
  }
  
  
  private final OnRotateGestureListener mListener;
  private boolean mSloppyGesture;
  
  public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
      super(context);
      mListener = listener;
  }
  
  @Override
  protected void handleStartProgressEvent(int actionCode, MotionEvent event){
      switch (actionCode) {
          case MotionEvent.ACTION_POINTER_DOWN:
              // At least the second finger is on screen now
  
              resetState(); // In case we missed an UP/CANCEL event
              mPrevEvent = MotionEvent.obtain(event);
              mTimeDelta = 0;
  
              updateStateByEvent(event);
  
              // See if we have a sloppy gesture
              mSloppyGesture = isSloppyGesture(event);
              if(!mSloppyGesture){
                  // No, start gesture now
                  mGestureInProgress = mListener.onRotateBegin(this);
              } 
              break;
  
          case MotionEvent.ACTION_MOVE:
              if (!mSloppyGesture) {
                  break;
              }
  
              // See if we still have a sloppy gesture
              mSloppyGesture = isSloppyGesture(event);
              if(!mSloppyGesture){
                  // No, start normal gesture now
                  mGestureInProgress = mListener.onRotateBegin(this);
              }
  
              break;
  
          case MotionEvent.ACTION_POINTER_UP:
              if (!mSloppyGesture) {
                  break;
              }
  
              break; 
      }
  }
  
  
  @Override
  protected void handleInProgressEvent(int actionCode, MotionEvent event){    
      switch (actionCode) {
          case MotionEvent.ACTION_POINTER_UP:
              // Gesture ended but 
              updateStateByEvent(event);
  
              if (!mSloppyGesture) {
                  mListener.onRotateEnd(this);
              }
  
              resetState();
              break;
  
          case MotionEvent.ACTION_CANCEL:
              if (!mSloppyGesture) {
                  mListener.onRotateEnd(this);
              }
  
              resetState();
              break;
  
          case MotionEvent.ACTION_MOVE:
              updateStateByEvent(event);
  
              // Only accept the event if our relative pressure is within
              // a certain limit. This can help filter shaky data as a
              // finger is lifted.
              if (mCurrPressure / mPrevPressure &gt; PRESSURE_THRESHOLD) {
                  final boolean updatePrevious = mListener.onRotate(this);
                  if (updatePrevious) {
                      mPrevEvent.recycle();
                      mPrevEvent = MotionEvent.obtain(event);
                  }
              }
              break;
      }
  }
  
  @Override
  protected void resetState() {
      super.resetState();
      mSloppyGesture = false;
  }
  
  
  /**
   * Return the rotation difference from the previous rotate event to the current
   * event. 
   * 
   * @return The current rotation //difference in degrees.
   */
  public float getRotationDegreesDelta() {
      double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
      return (float) (diffRadians * 180 / Math.PI);
  }
  
  public float getFocusX() {
      return mCurrEvent.getX() + mCurrFingerDiffX * 0.5f;
  }
  
  public float getFocusY() {
      return mCurrEvent.getY() + mCurrFingerDiffY * 0.5f;
  }
  </code></pre>
  
  <p>}</p>