Reputation: 12375
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
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