Problema con Android ImageView Scaling y traducción

Estoy desarrollando una aplicación de Android (API 19 4.4) y encuentro algún problema con ImageViews. Tengo un SurfaceView, en el cual agrego ImageViews dinámicamente y quiero reactjsr ante los eventos táctiles. Hasta ahora, he logrado hacer que ImageView se mueva y escale sin problemas, pero tengo un comportamiento molesto.

Cuando reduzco la imagen a un cierto límite (diría que la mitad del tamaño original) y trato de moverlo, la imagen parpadea. Después de un breve análisis, parece que está cambiando su posición simétricamente alrededor del punto del dedo en la pantalla, acumulando distancia, y finalmente se pierde de vista (todo eso sucede muy rápido (<1s). Creo que me falta algo con el pariente valor del evento táctil para ImageView / SurfaceView, pero soy bastante novato y estoy sorprendido …

Aquí está mi código

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

}

También tengo otras preguntas sobre rotaciones. ¿Cómo debo implementar esto? ¿Puedo usar ScalegestureDetector de alguna manera o tengo que hacer que esto funcione en el evento touch view? Me gustaría poder escalar y girar en el mismo gesto (y moverme en otro).

Gracias por ayudarme, ¡realmente lo agradecería!

Lo siento por mi ingles

este es un ejemplo de trabajo de dos dedos mover / escalar / rotar (nota: el código es bastante corto debido al detector inteligente utilizado – ver MatrixGestureDetector ):

 class ViewPort extends View { List layers = new LinkedList(); 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; } } } 

Finalmente uso esto (el espaciado se usa para calcular la distancia entre dos dedos), compensé la vista de la imagen después de la escala para mantenerla centrada, funciona bien por ahora:

  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 

Traté de implementar el toque múltiple en la vista, no en el bitmap usando la matriz, ahora tengo éxito. Ahora creo que será útil para usted gesto individual para imagen múltiple. Pruébalo, funciona mejor para mí.

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