我怎样才能放大 ImageView 而不会在其他地方稍微跳动? (Android)
How can I zoom into an ImageView without it jumping slightly elsewhere? (Android)
我正在尝试创建一个用户可以在其中拖动和缩放 ImageView 的应用程序。但是我在使用以下代码时遇到了问题。
当 scaleFactor 不是 1 并且第二根手指向下时,它会稍微平移到其他地方。我不知道这个翻译是从哪里来的...
这是完整的 class:
package me.miutaltbati.ramaview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
import static android.view.MotionEvent.INVALID_POINTER_ID;
public class RamaView extends ImageView {
private Context context;
private Matrix matrix = new Matrix();
private Matrix translateMatrix = new Matrix();
private Matrix scaleMatrix = new Matrix();
// Properties coming from outside:
private int drawableLayoutId;
private int width;
private int height;
private static float MIN_ZOOM = 0.33333F;
private static float MAX_ZOOM = 5F;
private PointF mLastTouch = new PointF(0, 0);
private PointF mLastFocus = new PointF(0, 0);
private PointF mLastPivot = new PointF(0, 0);
private float mPosX = 0F;
private float mPosY = 0F;
public float scaleFactor = 1F;
private int mActivePointerId = INVALID_POINTER_ID;
private Paint paint;
private Bitmap bitmapLayout;
private OnFactorChangedListener mListener;
private ScaleGestureDetector mScaleDetector;
public RamaView(Context context) {
super(context);
initializeInConstructor(context);
}
public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs) {
super(context, attrs);
initializeInConstructor(context);
}
public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeInConstructor(context);
}
public void initializeInConstructor(Context context) {
this.context = context;
paint = new Paint();
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mScaleDetector.setQuickScaleEnabled(false);
setScaleType(ScaleType.MATRIX);
}
public Bitmap decodeSampledBitmap() {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
// Calculate inSampleSize
options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
}
public void setDrawable(int drawableId) {
drawableLayoutId = drawableId;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
bitmapLayout = decodeSampledBitmap();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
int pointerIndex = event.getActionIndex();
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Remember where we started (for dragging)
mLastTouch = new PointF(x, y);
// Save the ID of this pointer (for dragging)
mActivePointerId = event.getPointerId(0);
}
case MotionEvent.ACTION_POINTER_DOWN: {
if (event.getPointerCount() == 2) {
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Calculate the distance moved
float dx = 0;
float dy = 0;
if (event.getPointerCount() == 1) {
// Calculate the distance moved
dx = x - mLastTouch.x;
dy = y - mLastTouch.y;
matrix.setScale(scaleFactor, scaleFactor, mLastPivot.x, mLastPivot.y);
// Remember this touch position for the next move event
mLastTouch = new PointF(x, y);
} else if (event.getPointerCount() == 2) {
// Calculate the distance moved
dx = mScaleDetector.getFocusX() - mLastFocus.x;
dy = mScaleDetector.getFocusY() - mLastFocus.y;
matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
mLastPivot = new PointF(-mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
mPosX += dx;
mPosY += dy;
matrix.postTranslate(mPosX, mPosY);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.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;
mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
mActivePointerId = event.getPointerId(newPointerIndex);
} else {
final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
}
break;
}
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitmapLayout, 0, 0, paint);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
我认为问题出在这一行:
matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
我尝试了很多东西,但我无法让它正常工作。
更新:
以下是初始化 RamaView 实例的方法:
主要 activity 的 onCreate:
rvRamaView = findViewById(R.id.rvRamaView);
final int[] rvSize = new int[2];
ViewTreeObserver vto = rvRamaView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rvRamaView.getViewTreeObserver().removeOnPreDrawListener(this);
rvSize[0] = rvRamaView.getMeasuredWidth();
rvSize[1] = rvRamaView.getMeasuredHeight();
rvRamaView.setSize(rvSize[0], rvSize[1]);
return true;
}
});
rvRamaView.setDrawable(R.drawable.original_jpg);
似乎代码不完整(例如,我看不到矩阵是如何使用的以及 scaleFactor 的分配位置),但我认为翻译不一致的原因是因为如果有 2 个指针,你会得到 [x , y] 位置从 mScaleDetector.getFocus
。正如 ScaleGestureDetector.getFocusX()
的文档所述:
Get the X coordinate of the current gesture's focal point. If a
gesture is in progress, the focal point is between each of the
pointers forming the gesture.
您应该只使用 mScaleDetector
来获取当前比例,但平移应该始终计算为 mLastTouch
和 event.getXY(pointerIndex)
之间的差异,以便只考虑一个指针用于翻译。如果用户添加第二根手指并释放第一根手指,请确保重新分配 pointerIndex
并且不执行任何转换以避免跳转。
最好使用矩阵来累积 变化,而不是尝试自己重新计算转换。您可以使用矩阵 post... 和 pre... 方法执行此操作并远离 set... 重置矩阵的方法。
这里是 RamaView class 的返工,除了上面提到的对矩阵的特定处理外,它在很大程度上符合目标。模组是 onTouchEvent() 方法。该视频是在示例应用程序中运行的代码的输出。
RamaView.java
public class RamaView extends ImageView {
private final Matrix matrix = new Matrix();
// Properties coming from outside:
private int drawableLayoutId;
private int width;
private int height;
private static final float MIN_ZOOM = 0.33333F;
private static final float MAX_ZOOM = 5F;
private PointF mLastTouch = new PointF(0, 0);
private PointF mLastFocus = new PointF(0, 0);
public float scaleFactor = 1F;
private int mActivePointerId = INVALID_POINTER_ID;
private Paint paint;
private Bitmap bitmapLayout;
// private OnFactorChangedListener mListener;
private ScaleGestureDetector mScaleDetector;
public RamaView(Context context) {
super(context);
initializeInConstructor(context);
}
public RamaView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initializeInConstructor(context);
}
public RamaView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeInConstructor(context);
}
public void initializeInConstructor(Context context) {
paint = new Paint();
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mScaleDetector.setQuickScaleEnabled(false);
setScaleType(ScaleType.MATRIX);
}
public Bitmap decodeSampledBitmap() {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
}
public void setDrawable(int drawableId) {
drawableLayoutId = drawableId;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
bitmapLayout = decodeSampledBitmap();
}
private float mLastScaleFactor = 1.0f;
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
int pointerIndex = event.getActionIndex();
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Remember where we started (for dragging)
mLastTouch = new PointF(x, y);
// Save the ID of this pointer (for dragging)
mActivePointerId = event.getPointerId(0);
}
case MotionEvent.ACTION_POINTER_DOWN: {
if (event.getPointerCount() == 2) {
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Calculate the distance moved
float dx = 0;
float dy = 0;
if (event.getPointerCount() == 1) {
// Calculate the distance moved
dx = x - mLastTouch.x;
dy = y - mLastTouch.y;
// Remember this touch position for the next move event
mLastTouch = new PointF(x, y);
} else if (event.getPointerCount() == 2) {
// Calculate the distance moved
float focusX = mScaleDetector.getFocusX();
float focusY = mScaleDetector.getFocusY();
dx = focusX - mLastFocus.x;
dy = focusY - mLastFocus.y;
// Since we are accumating translation/scaling, we are just adding to
// the previous scale.
matrix.postScale(scaleFactor/mLastScaleFactor, scaleFactor/mLastScaleFactor, focusX, focusY);
mLastScaleFactor = scaleFactor;
mLastFocus = new PointF(focusX, focusY);
}
// Translation is cumulative.
matrix.postTranslate(dx, dy);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.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;
mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
mActivePointerId = event.getPointerId(newPointerIndex);
} else {
final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
}
break;
}
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitmapLayout, 0, 0, paint);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
我正在尝试创建一个用户可以在其中拖动和缩放 ImageView 的应用程序。但是我在使用以下代码时遇到了问题。
当 scaleFactor 不是 1 并且第二根手指向下时,它会稍微平移到其他地方。我不知道这个翻译是从哪里来的...
这是完整的 class:
package me.miutaltbati.ramaview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
import static android.view.MotionEvent.INVALID_POINTER_ID;
public class RamaView extends ImageView {
private Context context;
private Matrix matrix = new Matrix();
private Matrix translateMatrix = new Matrix();
private Matrix scaleMatrix = new Matrix();
// Properties coming from outside:
private int drawableLayoutId;
private int width;
private int height;
private static float MIN_ZOOM = 0.33333F;
private static float MAX_ZOOM = 5F;
private PointF mLastTouch = new PointF(0, 0);
private PointF mLastFocus = new PointF(0, 0);
private PointF mLastPivot = new PointF(0, 0);
private float mPosX = 0F;
private float mPosY = 0F;
public float scaleFactor = 1F;
private int mActivePointerId = INVALID_POINTER_ID;
private Paint paint;
private Bitmap bitmapLayout;
private OnFactorChangedListener mListener;
private ScaleGestureDetector mScaleDetector;
public RamaView(Context context) {
super(context);
initializeInConstructor(context);
}
public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs) {
super(context, attrs);
initializeInConstructor(context);
}
public RamaView(Context context, @android.support.annotation.Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeInConstructor(context);
}
public void initializeInConstructor(Context context) {
this.context = context;
paint = new Paint();
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mScaleDetector.setQuickScaleEnabled(false);
setScaleType(ScaleType.MATRIX);
}
public Bitmap decodeSampledBitmap() {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
// Calculate inSampleSize
options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
}
public void setDrawable(int drawableId) {
drawableLayoutId = drawableId;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
bitmapLayout = decodeSampledBitmap();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
int pointerIndex = event.getActionIndex();
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Remember where we started (for dragging)
mLastTouch = new PointF(x, y);
// Save the ID of this pointer (for dragging)
mActivePointerId = event.getPointerId(0);
}
case MotionEvent.ACTION_POINTER_DOWN: {
if (event.getPointerCount() == 2) {
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Calculate the distance moved
float dx = 0;
float dy = 0;
if (event.getPointerCount() == 1) {
// Calculate the distance moved
dx = x - mLastTouch.x;
dy = y - mLastTouch.y;
matrix.setScale(scaleFactor, scaleFactor, mLastPivot.x, mLastPivot.y);
// Remember this touch position for the next move event
mLastTouch = new PointF(x, y);
} else if (event.getPointerCount() == 2) {
// Calculate the distance moved
dx = mScaleDetector.getFocusX() - mLastFocus.x;
dy = mScaleDetector.getFocusY() - mLastFocus.y;
matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
mLastPivot = new PointF(-mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
mPosX += dx;
mPosY += dy;
matrix.postTranslate(mPosX, mPosY);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.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;
mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
mActivePointerId = event.getPointerId(newPointerIndex);
} else {
final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
}
break;
}
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitmapLayout, 0, 0, paint);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
我认为问题出在这一行:
matrix.setScale(scaleFactor, scaleFactor, -mPosX + mScaleDetector.getFocusX(), -mPosY + mScaleDetector.getFocusY());
我尝试了很多东西,但我无法让它正常工作。
更新:
以下是初始化 RamaView 实例的方法:
主要 activity 的 onCreate:
rvRamaView = findViewById(R.id.rvRamaView);
final int[] rvSize = new int[2];
ViewTreeObserver vto = rvRamaView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rvRamaView.getViewTreeObserver().removeOnPreDrawListener(this);
rvSize[0] = rvRamaView.getMeasuredWidth();
rvSize[1] = rvRamaView.getMeasuredHeight();
rvRamaView.setSize(rvSize[0], rvSize[1]);
return true;
}
});
rvRamaView.setDrawable(R.drawable.original_jpg);
似乎代码不完整(例如,我看不到矩阵是如何使用的以及 scaleFactor 的分配位置),但我认为翻译不一致的原因是因为如果有 2 个指针,你会得到 [x , y] 位置从 mScaleDetector.getFocus
。正如 ScaleGestureDetector.getFocusX()
的文档所述:
Get the X coordinate of the current gesture's focal point. If a gesture is in progress, the focal point is between each of the pointers forming the gesture.
您应该只使用 mScaleDetector
来获取当前比例,但平移应该始终计算为 mLastTouch
和 event.getXY(pointerIndex)
之间的差异,以便只考虑一个指针用于翻译。如果用户添加第二根手指并释放第一根手指,请确保重新分配 pointerIndex
并且不执行任何转换以避免跳转。
最好使用矩阵来累积 变化,而不是尝试自己重新计算转换。您可以使用矩阵 post... 和 pre... 方法执行此操作并远离 set... 重置矩阵的方法。
这里是 RamaView class 的返工,除了上面提到的对矩阵的特定处理外,它在很大程度上符合目标。模组是 onTouchEvent() 方法。该视频是在示例应用程序中运行的代码的输出。
RamaView.java
public class RamaView extends ImageView {
private final Matrix matrix = new Matrix();
// Properties coming from outside:
private int drawableLayoutId;
private int width;
private int height;
private static final float MIN_ZOOM = 0.33333F;
private static final float MAX_ZOOM = 5F;
private PointF mLastTouch = new PointF(0, 0);
private PointF mLastFocus = new PointF(0, 0);
public float scaleFactor = 1F;
private int mActivePointerId = INVALID_POINTER_ID;
private Paint paint;
private Bitmap bitmapLayout;
// private OnFactorChangedListener mListener;
private ScaleGestureDetector mScaleDetector;
public RamaView(Context context) {
super(context);
initializeInConstructor(context);
}
public RamaView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initializeInConstructor(context);
}
public RamaView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeInConstructor(context);
}
public void initializeInConstructor(Context context) {
paint = new Paint();
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mScaleDetector.setQuickScaleEnabled(false);
setScaleType(ScaleType.MATRIX);
}
public Bitmap decodeSampledBitmap() {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
options.inSampleSize = Util.calculateInSampleSize(options, width, height); // e.g.: 4, 8
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), drawableLayoutId, options);
}
public void setDrawable(int drawableId) {
drawableLayoutId = drawableId;
}
public void setSize(int width, int height) {
this.width = width;
this.height = height;
bitmapLayout = decodeSampledBitmap();
}
private float mLastScaleFactor = 1.0f;
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN: {
int pointerIndex = event.getActionIndex();
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Remember where we started (for dragging)
mLastTouch = new PointF(x, y);
// Save the ID of this pointer (for dragging)
mActivePointerId = event.getPointerId(0);
}
case MotionEvent.ACTION_POINTER_DOWN: {
if (event.getPointerCount() == 2) {
mLastFocus = new PointF(mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
}
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
// Calculate the distance moved
float dx = 0;
float dy = 0;
if (event.getPointerCount() == 1) {
// Calculate the distance moved
dx = x - mLastTouch.x;
dy = y - mLastTouch.y;
// Remember this touch position for the next move event
mLastTouch = new PointF(x, y);
} else if (event.getPointerCount() == 2) {
// Calculate the distance moved
float focusX = mScaleDetector.getFocusX();
float focusY = mScaleDetector.getFocusY();
dx = focusX - mLastFocus.x;
dy = focusY - mLastFocus.y;
// Since we are accumating translation/scaling, we are just adding to
// the previous scale.
matrix.postScale(scaleFactor/mLastScaleFactor, scaleFactor/mLastScaleFactor, focusX, focusY);
mLastScaleFactor = scaleFactor;
mLastFocus = new PointF(focusX, focusY);
}
// Translation is cumulative.
matrix.postTranslate(dx, dy);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.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;
mLastTouch = new PointF(event.getX(newPointerIndex), event.getY(newPointerIndex));
mActivePointerId = event.getPointerId(newPointerIndex);
} else {
final int tempPointerIndex = event.findPointerIndex(mActivePointerId);
mLastTouch = new PointF(event.getX(tempPointerIndex), event.getY(tempPointerIndex));
}
break;
}
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitmapLayout, 0, 0, paint);
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}