【问题标题】:How do I modify TouchImageView with double tap to zoom in and out?如何通过双击来修改 TouchImageView 以放大和缩小?
【发布时间】:2011-12-03 23:34:06
【问题描述】:

我正在修改 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
}

}

【问题讨论】:

  • 使用点击缩放实现检查 TouchImageViewthis fork

标签: android imageview zooming


【解决方案1】:

我的回答可能并不完全针对您的问题,但它涵盖了 ImageView 放大/缩小、双击、缩放时 Imageview 中的边界图像,总之就像 Android 的默认图库应用程序

http://blog.sephiroth.it/2011/04/04/imageview-zoom-and-scroll/

正如博客所说:

只要 Android 没有具有缩放和滚动功能的内置 ImageView 小部件,我就会尝试从 google 存储库开始自己创建一个。

结果非常好,所以我在这里发布源代码,如果 任何人都有兴趣,或者只是不想浪费时间 创建一个新的。

【讨论】:

    【解决方案2】:

    将当前缩放加倍放大,然后返回原始缩放级别:

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

    您可能希望将矩阵转换为用户双击点的中心。

    【讨论】:

    • 这在我的 SGS2 上似乎对我不起作用。它放大一个点,然后不缩小。
    • 抱歉,没有阅读您的所有代码。在放大/缩小时设置一个布尔值,并在 onDoubleTap 函数中使用它而不是 isZoomed
    【解决方案3】:

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

    如果有帮助,请告诉我。

    【讨论】:

      【解决方案4】:

      双击和其他手势可以检测如下: Fling gesture detection on grid layout

      我希望这会有所帮助... 伊曼纽尔

      【讨论】:

        【解决方案5】:

        通过上面的TouchImageView类,如果你想应用双击,你可以做一些步骤:

        1. 声明变量:

          GestureDetector mDoubleTap;
          
        2. 在构造函数中初始化:

          mDoubleTap = new GestureDetector(new DoubleTapGestureListener());
          
        3. onTouch()中注册:

          @Override
          public boolean onTouch(View v, MotionEvent event) {
              mDoubleTap.onTouchEvent(event);
              ...
          }
          
        4. 编写方法 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);
                  }
              }
          }
          
        5. 把它放在 zoomIn() 方法中为:

          public void zoomIn() {
              handleScale(2.0f);// 2.0f is the scale ratio
          }
          
        6. 在覆盖方法onScale()中,可以修改为:

           @Override
              public boolean onScale(ScaleGestureDetector detector) {
                  float mScaleFactor = detector.getScaleFactor();
                  handleScale(mScaleFactor);
                  return true;
              }
          

        遵循 DRY 原则。

        注意:双击仅对小触摸区域有效。

        让我知道它是否适合你。

        【讨论】:

        • 它正在工作,但是当我缩小图像的中心时,图像的中心会向触摸方向移动。例如如果您在中心右侧双击,那么当您缩小图像时,图像会移动到右侧。我添加了 public void zoomOut() { handleScale(0.5f); isZoomed = false;} ,有什么想法吗?
        • 我有一个答案给你,请在下面查看。很抱歉,这里发帖太长了
        猜你喜欢
        • 2021-01-24
        • 2017-10-27
        • 2012-02-19
        • 2018-05-18
        • 1970-01-01
        • 1970-01-01
        • 2013-03-28
        • 2018-07-04
        • 1970-01-01
        相关资源
        最近更新 更多