2011-10-09 74 views
5

我正在修改TouchImageView(https://github.com/MikeOrtiz/TouchImageView/issues)以便在双击时放大和缩小。我已经开始按照这篇文章 - How does TouchImageView works?并添加了手势检测。如何修改TouchImageView双击放大和缩小?

现在我必须实现放大和缩小,我不知道该怎么做。这是我迄今为止的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

检查[这个叉](https://github.com/Dreddik/AndroidTouchGallery)的** TouchImageView **与点击缩放实施 – Sash0k

回答

8

我的答案可能是不完全具体到你的问题,但它涵盖的ImageView Zooom在输入/输出,双击,在ImageView的包围盒的图像时,其放大,总之是完全一样的Android

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

的默认图库应用

正如博客说:

只要Android不具备内置有我试图通过自己从谷歌库开始创建一个缩放和滚动功能部件ImageView的。

结果是相当不错的,所以我在这里张贴的源代码,如果 有人有兴趣,或者根本不想浪费时间 创建一个新的。

+0

很好 - 谢谢。 – timothyjc

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

您可能要到矩阵中心对用户双击了点翻译。

+0

这似乎不适用于我在我的SGS2 ..它放大到一个点,然后不缩小。 – timothyjc

+0

对不起,没有读取您的所有代码。在放大/缩小时设置一个布尔值,并在onDoubleTap函数中使用它,而不是缩放 – FunkTheMonk

0

通过上面的类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()方法coppied:

    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原则。

注:双击仅具有小的触摸区域的影响。

让我知道它是否适合你。

+0

它正在工作,但是当我缩小图像的中心向touch.e.g的方向移动时。如果您在中央右侧重叠录制,则当缩放图像移到右侧时。我添加了public void zoomOut(){handle35(0f); isZoomed = false;},有什么想法? – sherlock

+0

我有一个答案给你,请检查下面。非常抱歉,因为在这里发帖太久了 –

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

让我知道它是否有帮助。