Canvas中位图的触摸检测问题

时间:2015-04-15 10:45:19

标签: android canvas bitmap

我正在开发一个应用程序,用户可以在画布中添加图像并可以使用它们。

以下是我的要求:

  1. 要一个接一个地在画布中添加位图,并且如果在画布中添加了位图并在画布上添加后移动,那么下次用户添加另一个位图时,已添加的位图的位置需要保留----- Canvas默认保存它

  2. 我有三个用于检测手势的课程

    1. MoveGestureDetector
    2. ScaleGestureDetector
    3. RotateGestureDetector
  3. 问题:我能够成功检测到我在画布上添加的位图上的触摸事件,但不能检测先前在画布上添加的先前位图。因此,如果我在画布上添加一个位图,我可以成功检测到它上面的触摸事件,但是如果我添加另一个位图,那么我将无法检测到旧位图上的触摸事件,并且能够检测到新的触摸事件。仅

    我几乎尝试过所有事情,但我无法获得任何见解。

    这就是我添加画布视图的方式

    FrameLayout gcfl = (FrameLayout) findViewById(R.id.gestureControlledFrameLayout);       
        final GestureViewPort gvp = new GestureViewPort(this);
        gcfl.addView(gvp);
    

    这是我的GestureViewPort.java

    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<Layer> layers = new ArrayList<Layer>();  
    private List<Bitmap> bitmapList;
    
    public GestureViewPort(Context context) {
        super(context);  
        this.context = context;
    
        this.bitmapList = ProductDetailActivity.bitmapList;      
    
        if(bitmapList!=null)
        {  
            for (int bitmapIndex = 0; bitmapIndex < bitmapList.size();bitmapIndex++)
            {                           
                if(bitmapList.get(bitmapIndex)!=null )
                {
                    Layer l2 = new Layer(context, this, bitmapList.get(bitmapIndex));
                    for(int layerIndex = 0 ; layerIndex < 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 >= 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);
    }    
    

    }

    这是我的Layer.java

    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;
        };
    };
    }
    

    这是我的MoveGestureDetector.java

     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 > 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 < 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;
    }
    

    }

    这是RotateGestureDetector.java

    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 > 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;
    }
    

    }

0 个答案:

没有答案