AndreaF
AndreaF

Reputation: 12375

Canvas view crash the application on touch events

I'm trying a simple CustomView with canvas. This custom view simply change the position of an icon picture on the screen randomly when is invoked updatePosition(), and in my intention should move the image on drag, scale on pinch... and when I touch a position in the screen should appear in the clicked position.

My try is the follow

public class CustomView extends View {

    Bitmap mBmp;
    Random mRnd;
    Paint mPaint;

    private static final int INVALID_POINTER_ID = -1;

    private float mPosX;
    private float mPosY;

    private float mLastTouchX;
    private float mLastTouchY;
    private int mActivePointerId = INVALID_POINTER_ID;

    private ScaleGestureDetector mScaleDetector;
    private float mScaleFactor = 1.f;

    int w, h, bw, bh;
    int px = -1, py = -1;

    public CustomView(Context context) {
        this(context, null, 0);
        mBmp = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.icon);

    }

    public CustomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs, 0);

        mBmp = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.icon); 
        bw = mBmp.getWidth(); 
        bh = mBmp.getHeight();
        mPaint = new Paint(); 
        mPaint.setColor(Color.CYAN);
        mPaint.setAntiAlias(true);
        mRnd = new Random();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();
        Log.d("DEBUG", "X: " + mPosX + " Y: " + mPosY);
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);

        if (px == -1 && py == -1) { 
            px = w / 2 - bw / 2; 
            py = h / 2 - bh / 2; 
        }

        canvas.drawCircle(px + (bw / 2), py + (bh / 2), 70, mPaint); 
        canvas.drawBitmap(mBmp, px, py, null); 

        canvas.restore();

    }

    public void updatePosition() {
        px = mRnd.nextInt(w - bw);
        py = mRnd.nextInt(h - bh);
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        w = MeasureSpec.getSize(widthMeasureSpec);
        h = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(w, h);
    }

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

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
        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);

            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: {
            final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                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, 10.0f));

            invalidate();
            return true;
        }
    }

}

updatePosition() listener works while onTouchListener cause crash of the application with a NullPointerException if I try to drag the image or touch any position of the screen

Could someone help me? Thanks

Upvotes: 0

Views: 291

Answers (1)

JRomero
JRomero

Reputation: 4868

It looks like mScaleDetector is only initialized in one of the constructor. It should be initialized in all constructors. A common pattern is to create an init() method and place all you initializing in there then call that method from all constructors.

Note: It's always helpful to post a StackTrace... You should be able to see which constructor got called.

Upvotes: 1

Related Questions