In my application i am able to overlap images one by another, when i drag one image from the group of images the left and right images are also moving on the screen.
How restrict this, below is my code
int cards[] = {R.drawable.c1,R.drawable.c2,R.drawable.c3,R.drawable.c4,R.drawable.c5,R.drawable.c6,
R.drawable.c7,R.drawable.c8,R.drawable.c9,R.drawable.c10,R.drawable.c11,R.drawable.c12,R.drawable.c13};
ImageView[] Images = new ImageView[cards.length];
for (int i = 0; i < cards.length; i++) {
Images[i] = new ImageView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i != 0) {
params.addRule(RelativeLayout.ALIGN_LEFT,i-1);
params.leftMargin= 40;
Images[i].setImageBitmap(BitmapFactory.decodeResource(getResources(), cards[i]));
Images[i].setId(i);
Images[i].setOnTouchListener(this);
layout.addView(Images[i], params);
} else {
Images[i].setImageBitmap(BitmapFactory.decodeResource(getResources(), cards[i]));
Images[i].setId(i);
Images[i].setOnTouchListener(this);
layout.addView(Images[i], params);
}
}
//Ontouch
#Override
public boolean onTouch(View p_v, MotionEvent p_event){
params = (RelativeLayout.LayoutParams)p_v.getLayoutParams();
switch (p_event.getAction()){
case MotionEvent.ACTION_DOWN:
{
status = START_DRAGGING;
imageView1 = new ImageView(this);
imageView1.setImageBitmap(p_v.getDrawingCache());
m_lastTouchX = p_event.getX();
m_lastTouchY = p_event.getY();
break;
}
case MotionEvent.ACTION_UP:
{
status=STOP_DRAGGING;
break;
}
case MotionEvent.ACTION_MOVE:
{
if (status == START_DRAGGING) {
m_dx = p_event.getX() - m_lastTouchX;
m_dy = p_event.getY() - m_lastTouchY;
m_posX = m_prevX + m_dx;
m_posY = m_prevY + m_dy;
System.out.println("Dragging");
params.leftMargin = (int) m_posX;
params.topMargin=(int) m_posY;
p_v.bringToFront();
p_v.setLayoutParams(params);
imageView1.invalidate();
m_prevX = m_posX;
m_prevY = m_posY;
}
break;
}
}
return true;
}
Here is a small snippet to get you going. I added some images into RelativeLayout. The drawing is done in canvas for better performance of dragging. You just need to handle the layout params update MotionEvent.ACTION_UP event:
private ImageView[] Images;
private boolean mDragging = false;
private Rect mImageRect = new Rect();
int mX = 0;
int mY = 0;
private Bitmap mBitmap;
private ImageView mImage;
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mX = (int) ev.getX();
mY = (int) ev.getY();
for (ImageView image : Images) {
image.getHitRect(mImageRect);
if (mImageRect.contains(mX, mY)) {
mDragging = true;
mImage = image;
mBitmap = ((BitmapDrawable) mImage.getDrawable())
.getBitmap();
mImage.setVisibility(View.GONE);
postInvalidate();
break;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (mDragging) {
mX = (int) ev.getX();
mY = (int) ev.getY();
postInvalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mDragging) {
mDragging = false;
mBitmap = null;
mImage.setVisibility(View.VISIBLE);
mImage = null;
}
break;
}
return super.onTouchEvent(ev);
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, mX, mY, null);
}
}
My guess would be that because your images overlap, all three images get the onTouch event and start dragging. You should add some logic that would determine which image is the one that should drag (for example the one that processes the event first) and then START_DRAGGING only that image. You already have a "global" value status. Beside it just add int imageNo and set it in the ACTION_DOWN case statement.
While on the subject: you should only process ACTION_DOWN if status != START_DRAGGING.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am making photo editing application, in my activity I have bitmap that is going to erase on touch when it's not zoomed or rotated its drawing perfectly but after I zoom or rotate bitmap, eraser is not erased at touched points.its erased on some other position according bitmap scaled.
how can I resolve this, please help me.
Drawingview.java
public class DrawingView extends ImageView implements View.OnTouchListener,MatrixGestureDetector.OnMatrixChangeListener {
private int ERASE = 1;
private int ZOOMTOUCH = 5;
private Matrix matrix = new Matrix();
Matrix inverse = new Matrix();
MatrixGestureDetector mgd = new MatrixGestureDetector(matrix, this);
#Override
public void onChange(Matrix matrix) {
invalidate();
}
public interface ActionListener {
void onActionCompleted(int i);
}
public void setImageBitmap(Bitmap bm) {
if (bm != null) {
bmp = bm;
orgBit = bm.copy(Bitmap.Config.ARGB_8888, true);
Bitmap2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
c2 = new Canvas();
c2.setBitmap(Bitmap2);
c2.drawBitmap(bmp, 0, 0, null);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.concat(matrix);
canvas.drawBitmap(Bitmap2,0,0, null);
if (c2 != null) {
Paint p;
if (!updateOnly) {
if (isTouched) {
paint = getPaintByMode(MODE, brushSize);
c2.drawPath(tPath, paint);
isTouched = false;
} else if (curIndx >= 0 && drawOnLasso) {
redrawCanvas();
}
}
if (MODE == ERASE) {
p = new Paint();
p.setColor(SupportMenu.CATEGORY_MASK);
erPaint.setStrokeWidth(updatebrushsize(erps, scale));
canvas.drawCircle(f887X, f888Y, (float) (brushSize / 2), erPaint);
canvas.drawCircle(f887X, f888Y + ((float) offset), updatebrushsize(ImageUtils.dpToPx(getContext(), 7), scale), p);
}
if (MODE == ZOOMTOUCH) {
}
updateOnly = false;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (MODE == ERASE) {
f887X = event.getX();
f888Y = event.getY() - ((float) offset);
isTouched = true;
switch (event.getAction()) {
case 0:
paint.setStrokeWidth((float) brushSize);
tPath = new Path();
tPath.moveTo(f887X, f888Y);
drawPath.moveTo(f887X, f888Y);
invalidate();
break;
case 1:
drawPath.lineTo(f887X, f888Y);
tPath.lineTo(f887X, f888Y);
invalidate();
changesIndx.add(curIndx + 1, new Path(tPath));
brushIndx.add(curIndx + 1, Integer.valueOf(brushSize));
modeIndx.add(curIndx + 1, Integer.valueOf(MODE));
vectorPoints.add(curIndx + 1, null);
lassoIndx.add(curIndx + 1, Boolean.valueOf(insidCutEnable));
tPath.reset();
curIndx++;
clearNextChanges();
break;
case 2:
drawPath.lineTo(f887X, f888Y);
tPath.lineTo(f887X, f888Y);
invalidate();
break;
default:
return false;
}
}
if (MODE == ZOOMTOUCH) {
matrix.invert(inverse);
float[] pts = {event.getX(), event.getY()};
inverse.mapPoints(pts);
mgd.onTouchEvent(event);
}
return true;
}
}
class MatrixGestureDetector {
private static final String TAG = "MatrixGestureDetector";
private int ptpIdx = 0;
private Matrix mTempMatrix = new Matrix();
private Matrix mMatrix;
private OnMatrixChangeListener mListener;
private float[] mSrc = new float[4];
private float[] mDst = new float[4];
private int mCount;
interface OnMatrixChangeListener {
void onChange(Matrix matrix);
}
public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
this.mMatrix = matrix;
this.mListener = listener;
}
public void onTouchEvent(MotionEvent event) {
if (event.getPointerCount() > 2) {
return;
}
int action = event.getActionMasked();
int index = event.getActionIndex();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
int idx = index * 2;
mSrc[idx] = event.getX(index);
mSrc[idx + 1] = event.getY(index);
mCount++;
ptpIdx = 0;
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < mCount; i++) {
idx = ptpIdx + i * 2;
mDst[idx] = event.getX(i);
mDst[idx + 1] = event.getY(i);
}
mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
mMatrix.postConcat(mTempMatrix);
if (mListener != null) {
mListener.onChange(mMatrix);
}
System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerId(index) == 0) ptpIdx = 2;
mCount--;
break;
}
}
}
found the solution thanx to pskink, on this link Android ImageView Scaling and translating issue
by using invert matrix in layer#contain method.
I want to put marker on image with below functionality
-Dynamically adding multiple markers on image with zoom and scroll functionality on image.
-Blink animation on marker after added on image.
-Marker can be click-able and drag & drop on image.
-Marker's x & y position on image to sync same with other devices
I have used https://github.com/davemorrissey/subsampling-scale-image-view library
Java Code :
public class PinMarkerView extends SubsamplingScaleImageView implements View.OnTouchListener {
private PointF sPin;
private Bitmap pin;
private int resId;
public float vX;
public float vY;
private PointF vPrevious;
private PointF vStart;
private boolean drawing = false;
private boolean markerTouch = false;
private int strokeWidth;
private ArrayList<MapPins> allPins = new ArrayList<MapPins>();
private ArrayList<DrawPins> drawnPins = new ArrayList<DrawPins>();
public PinView(Context context) {
this(context, null);
}
public PinView(Context context, AttributeSet attr) {
super(context, attr);
// initialise();
}
public void setPin(PointF sPin, #DrawableRes int resid, String extras) {
MapPins mapPins = new MapPins();
mapPins.setPointF(sPin);
mapPins.setResId(resid);
mapPins.setExtrasData(extras);
mapPins.setX(sPin.x);
mapPins.setY(sPin.y);
allPins.add(mapPins);
//this.sPin = sPin;
// this.resId = resid;
initialise();
invalidate();
}
public PointF getPin() {
return sPin;
}
public Bitmap getMarker() {
return pin;
}
private void initialise() {
setOnTouchListener(this);
float density = getResources().getDisplayMetrics().densityDpi;
strokeWidth = (int) (density / 60f);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
Bitmap localpin = BitmapFactory.decodeResource(this.getResources(), mapPins.getResId());
float w = (density / 100f) * localpin.getWidth();
float h = (density / 100f) * localpin.getHeight();
//pin = Bitmap.createScaledBitmap(pin, (int) w, (int) h, true);
pin = Bitmap.createScaledBitmap(localpin, localpin.getWidth(), localpin.getHeight(), true);
mapPins.setCreatedBitmap(pin);
allPins.set(i, mapPins);
//-------
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw pin before image is ready so it doesn't move around during setup.
if (!isReady()) {
return;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
if (mapPins.getPointF() != null && mapPins.getCreatedBitmap() != null) {
PointF vPin = sourceToViewCoord(mapPins.getPointF());
vX = vPin.x - (mapPins.getCreatedBitmap().getWidth() / 2);
vY = vPin.y - mapPins.getCreatedBitmap().getHeight();
canvas.drawBitmap(mapPins.getCreatedBitmap(), vX, vY, paint);
}
}
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
setMaxScale(5f);
return false;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
/* if (isZoomEnabled()) {
return super.onTouchEvent(event);
}*/
boolean consumed = false;
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
vStart = new PointF(event.getX(), event.getY());
vPrevious = new PointF(event.getX(), event.getY());
getPinIdByPoint(vStart, viewToSourceCoord(event.getX(), event.getY()));
handleActionDown((int) event.getX(), (int) event.getY());
break;
case MotionEvent.ACTION_POINTER_2_DOWN:
// Abort any current drawing, user is zooming
vStart = null;
vPrevious = null;
break;
case MotionEvent.ACTION_MOVE:
if (markerTouch) {
setPanEnabled(false);
PointF sCurrentF = viewToSourceCoord(event.getX(), event.getY());
PointF sCurrent = new PointF(sCurrentF.x, sCurrentF.y);
PointF sStart = vStart == null ? null : new PointF(viewToSourceCoord(vStart).x, viewToSourceCoord(vStart).y);
if (touchCount == 1 && vStart != null) {
float vDX = Math.abs(event.getX() - vPrevious.x);
float vDY = Math.abs(event.getY() - vPrevious.y);
if (vDX >= strokeWidth * 5 || vDY >= strokeWidth * 5) {
if (sPin == null) {
sPin = sStart;
}
sPin = sCurrent;
vPrevious.x = event.getX();
vPrevious.y = event.getY();
drawing = true;
}
consumed = true;
invalidate();
} else if (touchCount == 1) {
// Consume all one touch drags to prevent odd panning effects handled by the superclass.
consumed = true;
}
} else {
return super.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
invalidate();
setPanEnabled(true);
drawing = false;
markerTouch = false;
vPrevious = null;
vStart = null;
}
// Use parent to handle pinch and two-finger pan.
return consumed || super.onTouchEvent(event);
}
public void handleActionDown(int eventX, int eventY) {
if (eventX >= (sPin.x - pin.getWidth()) && (eventX <= (sPin.x + pin.getWidth()))) {
if (eventY >= (sPin.y - pin.getHeight()) && (sPin.y <= (sPin.y + pin.getHeight()))) {
markerTouch = true;
} else {
markerTouch = false;
}
} else {
markerTouch = false;
}
}
public int getPinIdByPoint(PointF tappedCoordinate, PointF deeplinkCoordinate) {
for (int i = allPins.size() - 1; i >= 0; i--) {
MapPins dPin = allPins.get(i);
int blockWidth = dPin.getCreatedBitmap().getWidth();
int blockHeight = dPin.getCreatedBitmap().getHeight();
int deeplinkX = (int) (deeplinkCoordinate.x - (dPin.getCreatedBitmap().getWidth() / 2));
int deeplinkY = (int) (deeplinkCoordinate.y - dPin.getCreatedBitmap().getHeight());
// center coordinate -/+ blockWidth actually sets touchable area to 2x icon size
if (tappedCoordinate.x >= deeplinkX - blockWidth && tappedCoordinate.x <= deeplinkX + blockWidth &&
tappedCoordinate.y >= deeplinkY - blockHeight && tappedCoordinate.y <= deeplinkY + blockHeight) {
sPin = dPin.getPointF();
pin = dPin.getCreatedBitmap();
return dPin.getId();
}
}
return -1; //negative no means no pin selected
}
}
Currently, I am able to add multiple marker, but I am facing issue of marker click and dragging on image.
I have an app where I draw text on bitmap using the position specified by the user. When I ask canvas to draw the text using the x,y coordinates , the text draws in wrong place.
Where I position text Where it draws after I save
My code to save the image
private void drawText(Bitmap bitmap, TextView mText) {
try {
Resources resources = getResources();
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// resource bitmaps are imutable,
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setTextSize(text.getTextSize());
Rect bounds = new Rect();
paint.getTextBounds(mText.getText().toString(), 0, mText.length(), bounds);
canvas.drawText(mText.getText().toString(), text.getX() , text.getY(), paint);
ImageView im = (ImageView) findViewById(R.id.imageView);
im.setImageBitmap(bitmap);
saveBitmap(bitmap);
} catch (Exception e) {
// TODO: handle exception
}
}
Code to drag textview
textView.setOnTouchListener(new View.OnTouchListener() {
int initialX = 0;
int initialY = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
initialX = (int) event.getX();
initialY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int currentX = (int) event.getX();
int currentY = (int) event.getY();
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) textView.getLayoutParams();
int left = lp.leftMargin + (currentX - initialX);
int top = lp.topMargin + (currentY - initialY);
int right = lp.rightMargin - (currentX - initialX);
int bottom = lp.bottomMargin - (currentY - initialY);
lp.rightMargin = right;
lp.leftMargin = left;
lp.bottomMargin = bottom;
lp.topMargin = top;
textView.setLayoutParams(lp);
break;
default:
break;
}
return true;
}
});
findViewById(R.id.save).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
drawText(getBitmap(), textView);
}
});
I’m developing an android application (API 19 4.4) and I encounter some issue with ImageViews.
I have a SurfaceView, in which I dynamically add ImageViews which I want to react to touch events.
On so far, I have managed to make the ImageView move and scale smoothly but I have an annoying behavior.
When I scale down the image to a certain limit (I would say half the original size) and I try to move it, the image flicker.
After a short analysis, it seems that it’s switching its position symmetrically around the finger point on the screen, cumulating distance, and finally gets out of sight (all that happens very fast ( < 1s).
I think I am missing something with the relative value of the touch event to the ImageView/SurfaceView, but I’m a quite a noob and I’m stucked…
Here is my code
public class MyImageView extends ImageView {
private ScaleGestureDetector mScaleDetector ;
private static final int MAX_SIZE = 1024;
private static final String TAG = "MyImageView";
PointF DownPT = new PointF(); // Record Mouse Position When Pressed Down
PointF StartPT = new PointF(); // Record Start Position of 'img'
public MyImageView(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context,new MySimpleOnScaleGestureListener());
setBackgroundColor(Color.RED);
setScaleType(ScaleType.MATRIX);
setAdjustViewBounds(true);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(-MAX_SIZE, -MAX_SIZE, -MAX_SIZE, -MAX_SIZE);
this.setLayoutParams(lp);
this.setX(MAX_SIZE);
this.setY(MAX_SIZE);
}
int firstPointerID;
boolean inScaling=false;
#Override
public boolean onTouchEvent(MotionEvent event) {
// get pointer index from the event object
int pointerIndex = event.getActionIndex();
// get pointer ID
int pointerId = event.getPointerId(pointerIndex);
//First send event to scale detector to find out, if it's a scale
boolean res = mScaleDetector.onTouchEvent(event);
if (!mScaleDetector.isInProgress()) {
int eid = event.getAction();
switch (eid & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_MOVE :
if(pointerId == firstPointerID) {
PointF mv = new PointF( (int)(event.getX() - DownPT.x), (int)( event.getY() - DownPT.y));
this.setX((int)(StartPT.x+mv.x));
this.setY((int)(StartPT.y+mv.y));
StartPT = new PointF( this.getX(), this.getY() );
}
break;
case MotionEvent.ACTION_DOWN : {
firstPointerID = pointerId;
DownPT.x = (int) event.getX();
DownPT.y = (int) event.getY();
StartPT = new PointF( this.getX(), this.getY() );
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL: {
firstPointerID = -1;
break;
}
default :
break;
}
return true;
}
return true;
}
public boolean onScaling(ScaleGestureDetector detector) {
this.setScaleX(this.getScaleX()*detector.getScaleFactor());
this.setScaleY(this.getScaleY()*detector.getScaleFactor());
invalidate();
return true;
}
private class MySimpleOnScaleGestureListener extends SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
return onScaling(detector);
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d(TAG, "onScaleBegin");
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector arg0) {
Log.d(TAG, "onScaleEnd");
}
}
}
I have also another questions about rotations. How should I implement this?
Could I use the ScalegestureDetector in some way or have I to make this works in the view touch event? I would like to be able to scale and rotate in the same gesture (and move in another).
Thank for helping me, I would really appreciate!
Sorry for my english
this is a working example of two fingers move/scale/rotate (note: the code is quite short due to smart detector used - see MatrixGestureDetector):
class ViewPort extends View {
List<Layer> layers = new LinkedList<Layer>();
int[] ids = {R.drawable.layer0, R.drawable.layer1, R.drawable.layer2};
public ViewPort(Context context) {
super(context);
Resources res = getResources();
for (int i = 0; i < ids.length; i++) {
Layer l = new Layer(context, this, BitmapFactory.decodeResource(res, ids[i]));
layers.add(l);
}
}
#Override
protected void onDraw(Canvas canvas) {
for (Layer l : layers) {
l.draw(canvas);
}
}
private Layer target;
#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);
}
}
class Layer implements MatrixGestureDetector.OnMatrixChangeListener {
Matrix matrix = new Matrix();
Matrix inverse = new Matrix();
RectF bounds;
View parent;
Bitmap bitmap;
MatrixGestureDetector mgd = new MatrixGestureDetector(matrix, this);
public Layer(Context ctx, View p, Bitmap b) {
parent = p;
bitmap = b;
bounds = new RectF(0, 0, b.getWidth(), b.getHeight());
matrix.postTranslate(50 + (float) Math.random() * 50, 50 + (float) Math.random() * 50);
}
public boolean contains(MotionEvent event) {
matrix.invert(inverse);
float[] pts = {event.getX(), event.getY()};
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);
return true;
}
#Override
public void onChange(Matrix matrix) {
parent.invalidate();
}
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, matrix, null);
}
}
class MatrixGestureDetector {
private static final String TAG = "MatrixGestureDetector";
private int ptpIdx = 0;
private Matrix mTempMatrix = new Matrix();
private Matrix mMatrix;
private OnMatrixChangeListener mListener;
private float[] mSrc = new float[4];
private float[] mDst = new float[4];
private int mCount;
interface OnMatrixChangeListener {
void onChange(Matrix matrix);
}
public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
this.mMatrix = matrix;
this.mListener = listener;
}
public void onTouchEvent(MotionEvent event) {
if (event.getPointerCount() > 2) {
return;
}
int action = event.getActionMasked();
int index = event.getActionIndex();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
int idx = index * 2;
mSrc[idx] = event.getX(index);
mSrc[idx + 1] = event.getY(index);
mCount++;
ptpIdx = 0;
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < mCount; i++) {
idx = ptpIdx + i * 2;
mDst[idx] = event.getX(i);
mDst[idx + 1] = event.getY(i);
}
mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
mMatrix.postConcat(mTempMatrix);
if(mListener != null) {
mListener.onChange(mMatrix);
}
System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (event.getPointerId(index) == 0) ptpIdx = 2;
mCount--;
break;
}
}
}
I tried to implementation of multiple touch on view not on bitmap using matrix, now i success. Now i think it will helpful to you for individual gesture for multiple image. Try it, it work best for me.
public class MultiTouchImageView extends ImageView implements OnTouchListener{
float[] lastEvent = null;
float d = 0f;
float newRot = 0f;
public static String fileNAME;
public static int framePos = 0;
//private ImageView view;
private boolean isZoomAndRotate;
private boolean isOutSide;
// We can be in one of these 3 states
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
private PointF start = new PointF();
private PointF mid = new PointF();
float oldDist = 1f;
public MultiTouchImageView(Context context) {
super(context);
}
public MultiTouchImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MultiTouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#SuppressWarnings("deprecation")
#Override
public boolean onTouch(View v, MotionEvent event) {
//view = (ImageView) v;
bringToFront();
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
lastEvent = null;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
midPoint(mid, event);
mode = ZOOM;
}
lastEvent = new float[4];
lastEvent[0] = event.getX(0);
lastEvent[1] = event.getX(1);
lastEvent[2] = event.getY(0);
lastEvent[3] = event.getY(1);
d = rotation(event);
break;
case MotionEvent.ACTION_UP:
isZoomAndRotate = false;
case MotionEvent.ACTION_OUTSIDE:
isOutSide = true;
mode = NONE;
lastEvent = null;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
if(!isOutSide){
if (mode == DRAG && !isZoomAndRotate) {
isZoomAndRotate = false;
setTranslationX((event.getX() - start.x) + getTranslationX());
setTranslationY((event.getY() - start.y) + getTranslationY());
} else if (mode == ZOOM && event.getPointerCount() == 2) {
isZoomAndRotate = true;
boolean isZoom = false;
if(!isRotate(event)){
float newDist = spacing(event);
if (newDist > 10f) {
float scale = newDist / oldDist * getScaleX();
setScaleX(scale);
setScaleY(scale);
isZoom = true;
}
}
else if(!isZoom){
newRot = rotation(event);
setRotation((float)(getRotation() + (newRot - d)));
}
}
}
break;
}
new GestureDetector(new MyGestureDectore());
Constants.currentSticker = this;
return true;
}
private class MyGestureDectore extends GestureDetector.SimpleOnGestureListener{
#Override
public boolean onDoubleTap(MotionEvent e) {
bringToFront();
return false;
}
#Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
}
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
private boolean isRotate(MotionEvent event){
int dx1 = (int) (event.getX(0) - lastEvent[0]);
int dy1 = (int) (event.getY(0) - lastEvent[2]);
int dx2 = (int) (event.getX(1) - lastEvent[1]);
int dy2 = (int) (event.getY(1) - lastEvent[3]);
Log.d("dx1 ", ""+ dx1);
Log.d("dx2 ", "" + dx2);
Log.d("dy1 ", "" + dy1);
Log.d("dy2 ", "" + dy2);
//pointer 1
if(Math.abs(dx1) > Math.abs(dy1) && Math.abs(dx2) > Math.abs(dy2)) {
if(dx1 >= 2.0 && dx2 <= -2.0){
Log.d("first pointer ", "right");
return true;
}
else if(dx1 <= -2.0 && dx2 >= 2.0){
Log.d("first pointer ", "left");
return true;
}
}
else {
if(dy1 >= 2.0 && dy2 <= -2.0){
Log.d("seccond pointer ", "top");
return true;
}
else if(dy1 <= -2.0 && dy2 >= 2.0){
Log.d("second pointer ", "bottom");
return true;
}
}
return false;
}
}
I finally use this (spacing is used to calculated the distance between two fingers), I offset the imageview after scaling to keep it centered, works fine for now :
float newDist = spacing(event);
float scale = newDist / oldDist;
int oldH =getLayoutParams().height;
int oldW =getLayoutParams().width;
int newH =(int) (getLayoutParams().height*scale);
int newW =(int) (getLayoutParams().width*scale);
if(newH<MAX_SIZE && newW<MAX_SIZE){
//scale the height and width of the view
getLayoutParams().height = newH;
getLayoutParams().width = newW;
//calculate the X and Y offset to apply after scaling to keep the image centered
int xOffset = (int)(getLayoutParams().height - oldH)/2;
int yOffset = (int)(getLayoutParams().width - oldW)/2;
setX(getX()-xOffset);
setY(getY()-yOffset);
requestLayout();
setAdjustViewBounds(true);
oldDist=newDist;
All these examples had a glitchy gesture support because of scaleType was set to matrix. When I tried to zoom, I was not able to keep the image in center and control the amount of zoom. So I did some study and wrote a small, easy but very pleasing code for this: https://stackoverflow.com/a/65697376/13339685
I have a few polygons on Canvas.
How I can listening click on any from polygons?
I have a method
public void drawPolygon(ChartPolygon polygon) {
List<Point> points = polygon.getPoints();
int size = points.size();
if (size < 2) {
return;
}
Paint polyPaint = new Paint();
polyPaint.setColor(polygon.getColor());
polyPaint.setStyle(Style.FILL);
Path polyPath = new Path();
polyPath.moveTo(points.get(0).getX(), points.get(0).getY());
for (Point point : points) {
polyPath.lineTo(point.getX(), point.getY());
}
canvas.drawPath(polyPath, polyPaint);
}
I would keep a list of Regions. Whenever you draw a new Polygon, use the same Path which you use for drawing also to define a new Region:
Region r = new Region();
r.setPath(path, clip);
regionList.add(r);
clip could be defined e.g. by
Region clip = new Region(0, 0, canvas.getWidth(), canvas.getHeight());
Finally when you detect a touch in your onTouchListener loop over the regionList and check, whether the touched point (given by x and y) is inside the region:
for (int i = 0; i < regionList.size(); i++) {
Region r = regionList.get(i);
if (r.contains(x,y) {
selectedRegionIndex = i;
break;
}
}
Read this post, this post can help you
First one, you can get the position in X and Y of the click event in your screen.
You can implement the onTouchEvent function in the Activity extends
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (topTouchArea.contains(event.getX(), event.getY())) {
currentTouch = TOUCH_TOP;
} else if (RightTouchArea.contains(event.getX(),event.getY())) {
currentTouch = TOUCH_RIGHT;
} else if (LeftTouchArea.contains(event.getX(),event.getY())) {
currentTouch = TOUCH_LEFT;
} else {
return false; //Return false if user touches none of the corners
}
return true;
}
}
Now (always in your onTouchEvent) function, you can evaluate the position and regions of your polygon.
case MotionEvent.ACTION_MOVE:
switch (currentTouch) {
case TOUCH_TOP:
top.x = event.getX();
top.y = event.getY();
invalidate();
return true;
case TOUCH_RIGHT:
Right.x = event.getX();
Right.y = event.getY();
invalidate();
return true;
case TOUCH_LEFT:
Left.x = event.getX();
Left.y = event.getY();
invalidate();
return true;
}
case MotionEvent.ACTION_UP:
switch (currentTouch) {
case TOUCH_TOP:
top.x = event.getX();
top.y = event.getY();
invalidate();
currentTouch = NONE;
return true;
case TOUCH_RIGHT:
Right.x = event.getX();
Right.y = event.getY();
invalidate();
currentTouch = NONE;
return true;
case TOUCH_LEFT:
Left.x = event.getX();
Left.y = event.getY();
invalidate();
currentTouch = NONE;
return true;
}
return false;
All the post is in the article link