Image in Canvas with touch events
You can use this:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
public class MyImageView extends View {
private static final int INVALID_POINTER_ID = -1;
private Drawable mImage;
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;
public MyImageView(Context context) {
this(context, null, 0);
mImage = getResources().getDrawable(R.drawable.imagename);
mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
}
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
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);
// 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: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_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;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
Log.d("DEBUG", "X: "+mPosX+" Y: "+mPosY);
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mImage.draw(canvas);
canvas.restore();
}
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;
}
}
}
How to move images over canvas by touch event?
you also need to take care of MSPointer events, that are events from Microsoft to manage touch (it was introduced with Win8 and WinPhone 8).
The steps needed, each frame:
- detect mouse, touch and MSPointer events
- check the cursor position colliding the images
- move the image
For the first point:
function getCursorPositions (event, canvas) {
var element = canvas, offsetX = 0, offsetY = 0, positions = [];
if (element.offsetParent) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// Add padding and border style widths to offset
/*offsetX += stylePaddingLeft;
offsetY += stylePaddingTop;
offsetX += styleBorderLeft;
offsetY += styleBorderTop;*/
var touch = event;
//if multi-touch, get all the positions
if (event.targetTouches) { // or changedTouches
var touchPoints = (typeof event.targetTouches !== 'undefined') ? event.targetTouches : [event];
for (var i = 0; i < touchPoints.length; i++) {
touch = touchPoints[i];
positions.push({touch.pageX - offsetX, touch.pageY - offsetY});
}
}
else {
positions.push({touch.pageX - offsetX}, {touch.pageY - offsetY});
}
//return positions for mouse or fingers
return positions;
}
For the second point, you have at least 2 ways to detect the collision:
you can check whether the mouse position is inside the bounding box of your item :
function pointIsInRegion (point, targetRegion, threshold) {
return point.x >= (targetRegion.position.x - threshold) &&
point.y >= (targetRegion.position.y - threshold) &&
point.x <= (targetRegion.position.x + targetRegion.dimension.width + threshold) &&
point.y <= (targetRegion.position.y + targetRegion.dimension.height + threshold);
}
Or you can be more accurate by checking the pixels collision.
To achieve this second method, you have to render your items in a temporary canvas and check if there is at least pixels from your 2 items that collide (it can be accelerated by using masks).
For the third point (move the image), all you have to do is to move your image from "currentCursorPosition - previousCursorPosition". That's ths easiest part.
Anyway, I suggest you to use a framework. The code is already done and It will help you to go faster.
cgSceneGraph, (I'm the designer of this framework) will do the Job for you in just few lines.
Have a look at the "planner 2D" and "collision" examples (http://gwennaelbuchet.github.com/cgSceneGraph/examples.html)
Hope this can help you.
Assigning touch events to elements within canvas - iPad
You can't assign touch events to an image
object. The only valid attributes areonerror
, onload
, src
, validate
.
A related thread: Bind event to shape on canvas
Draw a Square on the HTML5 Canvas With Touch Events
Tested in Chrome with emulated touch events works with this jsFiddle The problem seems to be the way you bind your click event. I just used jQuery.
Or without jQuery: just a plain addEventListener
Related Topics
How to Kill an Application with All Its Activities
Converting a Canvas into Bitmap Image in Android
Bad Class File Magic or Version
Passing Arraylist of Objects Between Activities
Autocompletetextview Backed by Cursorloader
Notificationcompat.Builder Deprecated in Android O
Android HTML Imagegetter as Asynctask
What Is Different Between Mainactivity.This VS Getapplicationcontext()
Is It a Bad Practice to Use Negative Margins in Android
Listview Setonitemclicklistener Not Working by Adding Button
Retrofit 2: Get JSON from Response Body
Alertdialog.Builder with Custom Layout and Edittext; Cannot Access View
How to Use Library That Used Android Support with Androidx Projects
Change Text Color of One Word in a Textview
How to Zip and Unzip a String Using Gzipoutputstream That Is Compatible with .Net
Deleting Row in SQLite in Android
How Set Spannable Object Font with Custom Font
Decimal Separator Comma (',') with Numberdecimal Inputtype in Edittext