我正在修改TouchImageView(https://github.com/MikeOrtiz/TouchImageView/issues),以便在双击时放大和缩小。我已根据此帖发布了How does TouchImageView works?并添加了手势检测功能。
现在我必须实现放大和缩小,我不知道该怎么做。这是我到目前为止的代码,未实现zoomIn和zoomOut方法。有人知道怎么做吗?另外我注意到捏缩放并没有真正缩放到你捏的位置,所以我希望这可以使其表现得更像gallery3D捏缩放。感谢。
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View;
import android.widget.ImageView;
public class TouchImageView extends ImageView {
private static final String TAG = TouchImageView.class.getSimpleName();
Matrix matrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] m;
float redundantXSpace, redundantYSpace;
float width, height;
static final int CLICK = 3;
float saveScale = 1f;
float right, bottom, origWidth, origHeight, bmWidth, bmHeight;
ScaleGestureDetector mScaleDetector;
private GestureDetector gestureDetector;
Context context;
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TouchImageView(Context context) {
super(context);
init(context);
}
public void init(Context context) {
gestureDetector = new GestureDetector(new DoubleTapGestureListener());
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
matrix.setTranslate(1f, 1f);
m = new float[9];
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
mScaleDetector.onTouchEvent(event);
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float scaleWidth = Math.round(origWidth * saveScale);
float scaleHeight = Math.round(origHeight * saveScale);
if (scaleWidth < width) {
deltaX = 0;
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
} else if (scaleHeight < height) {
deltaY = 0;
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
} else {
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
}
matrix.postTranslate(deltaX, deltaY);
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
int xDiff = (int) Math.abs(curr.x - start.x);
int yDiff = (int) Math.abs(curr.y - start.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true; // indicate event was handled
}
});
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
bmWidth = bm.getWidth();
bmHeight = bm.getHeight();
}
public void setMaxZoom(float x) {
maxScale = x;
}
private class ScaleListener extends SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
LogUtil.i(TAG, detector.getScaleFactor() + " " + detector.getFocusX() + " " + detector.getFocusY());
float mScaleFactor = (float) Math.min(Math.max(.95f, detector.getScaleFactor()), 1.05);
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
if (origWidth * saveScale <= width || origHeight * saveScale <= height) {
matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
if (mScaleFactor < 1) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * saveScale) < width) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
return true;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
// Fit to screen.
float scale;
float scaleX = (float) width / (float) bmWidth;
float scaleY = (float) height / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
saveScale = 1f;
// Center the image
redundantYSpace = (float) height - (scale * (float) bmHeight);
redundantXSpace = (float) width - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = width - 2 * redundantXSpace;
origHeight = height - 2 * redundantYSpace;
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
setImageMatrix(matrix);
}
class DoubleTapGestureListener extends SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
// event when double tap occurs
@Override
public boolean onDoubleTap(MotionEvent e) {
float x = e.getX();
float y = e.getY();
LogUtil.i(TAG, "Tapped at: (" + x + "," + y + ")");
if (isZoomed()) {
zoomOut();
} else {
zoomIn();
}
return true;
}
}
public boolean isZoomed() {
return saveScale > minScale; // this seems to work
}
public void zoomIn() {
LogUtil.i(TAG, "Zooming in");
// TODO: no idea how to do this
}
public void zoomOut() {
LogUtil.i(TAG, "Zooming out");
// TODO: no idea how to do this
}
}
答案 0 :(得分:8)
我的答案可能并不完全针对您的问题,但它涵盖了ImageView Zooom输入/输出,双击,Imageview中缩放时的图像缩放,简而言之就像Android的默认Gallery应用程序
http://blog.sephiroth.it/2011/04/04/imageview-zoom-and-scroll/
正如博客所说:
只要Android没有带缩放和滚动功能的内置ImageView小部件,我就会尝试从谷歌存储库开始自己创建一个。
结果非常好,所以我在这里发布源代码,如果 任何人都有兴趣,或者根本不想浪费时间 创造一个新的。
答案 1 :(得分:3)
将当前缩放倍增放大,然后返回原始缩放级别:
float oldScale = 1.0f;
public void zoomIn() {
LogUtil.i(TAG, "Zooming in");
oldScale = saveScale;
saveScale *= 2;
matrix.setScale(saveScale, saveScale);
setImageMatrix(matrix);
invalidate();
}
public void zoomOut() {
LogUtil.i(TAG, "Zooming out");
saveScale = oldScale;
matrix.setScale(saveScale, saveScale);
setImageMatrix(matrix);
invalidate();
}
您可能希望将矩阵转换为用户双击的点的中心。
答案 2 :(得分:1)
@sherlock,
如果要在再次双击时缩小,可以编写如下方法:
private void handleScaleWhenDoubleTap(float scaleFactor) {
//set original scale
float origScale = curScale;
//update current scale
curScale *= scaleFactor;
//fix current scale if it greater than max scale, recalculate scale factor
if (curScale > maxScale) {
curScale = maxScale;
scaleFactor = maxScale / origScale;
}
//fix current scale if it less than min scale, recalculate scale factor
else if (curScale < minScale) {
curScale = minScale;
scaleFactor = minScale / origScale;
}
//calculate right point, bottom point
right = (curScale - 1) * width - 2 * (redundantXSpace * curScale);
bottom = (curScale - 1) * height - 2 * (redundantYSpace * curScale);
//scale
matrix.postScale(scaleFactor, scaleFactor, width / 2, height / 2);
//translate
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (y + bottom < 0) {
if (x >= 0) {
matrix.postTranslate(-x + redundantXSpace, -(y + bottom + redundantYSpace));
} else if (x + right < 0) {
matrix.postTranslate(-(x + right + redundantXSpace), -(y + bottom + redundantYSpace));
} else {
matrix.postTranslate(0, -(y + bottom));
}
} else if (y >= 0) {
if (x >= 0) {
matrix.postTranslate(-x + redundantXSpace, -y + redundantYSpace - navBarHeight);
} else if (x + right < 0) {
matrix.postTranslate(-(x + right + redundantXSpace), -y + redundantYSpace - navBarHeight);
} else {
matrix.postTranslate(0, -y);
}
} else {
if (x >= 0) {
matrix.postTranslate(-x, redundantYSpace - navBarHeight);
} else if (x + right < 0) {
matrix.postTranslate(-(x + right + redundantXSpace), redundantYSpace - navBarHeight);
} else {
matrix.postTranslate(0, -navBarHeight);
}
}
}
注意:如果手机有软导航栏(如Nexus 5),则必须重新计算要翻译的距离。在我的示例中,navBarHeight是由函数设置的变量:
public int getNavBarHeight(Context ctx) {
int id = ctx.getResources().getIdentifier("config_showNavigationBar", "bool", "android");
boolean hasNavBar = id > 0 && ctx.getResources().getBoolean(id);
if (hasNavBar) {
int resourceId = ctx.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return ctx.getResources().getDimensionPixelSize(resourceId);
}
}
return 0;
}
如果有帮助,请告诉我。
答案 3 :(得分:0)
可以按如下方式检测双击和其他手势: Fling gesture detection on grid layout
我希望这会有所帮助...... 灵光
答案 4 :(得分:0)
使用上面的 TouchImageView 类,如果您想应用双击,您可以执行以下步骤:
声明变量:
GestureDetector mDoubleTap;
构造函数初始化:
mDoubleTap = new GestureDetector(new DoubleTapGestureListener());
在 onTouch():
中注册@Override
public boolean onTouch(View v, MotionEvent event) {
mDoubleTap.onTouchEvent(event);
...
}
编写方法 handleScale() - 通过 onScale()方法进行编写 - 如下所示:
private void handleScale(float mScaleFactor) {
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
if (origWidth * saveScale <= width || origHeight * saveScale <= height) {
matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
if (mScaleFactor < 1) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * saveScale) < width) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
}
将其放在 zoomIn()方法中:
public void zoomIn() {
handleScale(2.0f);// 2.0f is the scale ratio
}
在覆盖方法 onScale()中,您可以修改为:
@Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
handleScale(mScaleFactor);
return true;
}
遵循DRY原则。
注意: 双击仅对触摸区域有效。
让我知道它是否适合你。