2012-07-05 72 views
7

我一直在这个问题上停留了八个小时,所以我觉得是时候得到一些帮助。画布捏缩放到点范围内

在开始我的问题之前,我只会让我们知道我已经浏览过此网站和Google,而且我找到的答案都没有帮助。 (This是一个,anotheranother

这里的交易:我有一个扩展SurfaceView类(姑且称之为MySurface),并覆盖在它的许多方法。通常,它会绘制几个正方形和文本框,这很好。只要用户开始触摸,它就会转换为Bitmap,然后绘制直到用户释放的每个帧。

这里是蹭:我想实现这样的功能,用户可以将两个手指放在屏幕上,捏缩放,也可以平移(但只有两个手指向下)。

我发现捏到变焦的几个实现,并通过以下他们适应我Canvas对象MySurface

public void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save(); 

    canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below 
    canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine 

    // Start draw code 

    // ... 

    // End draw code 

    canvas.restore(); 
} 
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     float factor = detector.getScaleFactor(); 
     if (Math.abs(factor - 1.0f) >= 0.0075f) { 
      mScaleVector.z *= factor; 
      mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM)); 
     } 

     // ... 

     invalidate(); 

     return true; 
    } 
} 
public boolean onTouchEvent(MotionEvent event) { 
    int action = event.getAction() & MotionEvent.ACTION_MASK; 
    int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
    if (event.getPointerCount() == 2) { 
     if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) { 
      // The various pivot coordinate codes would belong here 
     } 
    } 

    detector.onTouchEvent(event); // Calls the Scale Gesture Detector 
    return true; 
} 

虽然这两个元素做工精细 - 来回捏滚动 - 缩放 - 有一个大问题。缩放到缩放时,放大到点0,0,而不是放大到手指点。

我已经尝试了很多办法来解决这个问题:

  • 使用canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);;显然,这会产生不想要的结果,因为mScaleVector x和y值是0偏移量。
  • 管理使用与translate()方法相同的偏移量的“数据透视”坐标,但这会产生相同的0,0问题,或者在触摸视图时跳转。
  • 许多其他的事情......我已经做了很多与上述枢轴坐标,试图将其位置基于用户的第一次触摸,并将其相对于触摸每个连续的手势。

此外,此画布必须是有界的,所以用户不能永久滚动。但是,当我使用.scale(sx, sy, px, py)方法时,它会将事物推到我在.translate()中设置的任何边界之外。

我很...在这一点上很开放。我知道可以添加此功能,因为它可以在Android 4.0图库中查看(查看单个图像时)。我试图追踪处理这个问题的源代码,但无济于事。

+0

我个人直接使用'onTouchEvent'处理两个手指位置。有了这个,你可以很容易地找到中点,以及需要多大的缩放比例。 – Doomsknight

+0

如果你已经解决了我面临同样问题的问题,你可以请发布解决方案,我的客户是...... :(或者你能告诉我你是如何解决问题的吗?谢谢 – AkashG

回答

13

这里是我用来实现使用ScaleGestureDetectorImageView缩放缩放的代码。只需很少修改或者不需要修改,您也应该可以使用它,因为您也可以使用转换关系来绘制Canvas

@Override 
public boolean onScale(ScaleGestureDetector detector) { 
    float mScaleFactor = (float) Math.min(
     Math.max(.8f, detector.getScaleFactor()), 1.2); 
    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; 
} 

就我而言,我计算在视图的onMeasure()方法neccesary值,你可能想要做你的SurfaceView

width = MeasureSpec.getSize(widthMeasureSpec); // Change this according to your screen size 
height = MeasureSpec.getSize(heightMeasureSpec); // Change this according to your screen size 

// 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; 
scaleMappingRatio = saveScale/scale; 

// 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); 

一点解释这个其他地方:

saveScale是当前的比例Bitmap

mScaleFactor is th电子因素,你必须乘以你的比例。

maxScaleminScale可以是恒定值。

widthheight是屏幕的尺寸。

redundantXSpaceredundantYSpace是图像边界和屏幕边缘之间空,因为当在它更小,则屏幕

origHeightorigWidth是位图的大小

matrix是对图像的中心用于绘制位图的当前转换矩阵

诀窍是,当我初始化缩放并居中初始化图像时,我将该缩放比例选为1,并将scaleMappingRatio我映射了相对于该图像的实际比例值。

+2

哦,并且彻底地测试你的代码的各个方面,我发现那些矩阵变换是关键!如果可以的话,我会给你买一杯啤酒!A +,好的先生。 – Eric

+1

不客气,我花了很多时间找出正确的方法,当我开始编写上面的代码时,我认为它会值得分享。 –

+0

@AdamL.Mónos伟大的工作亚当,欢呼! –

-1
protected override void OnDraw(Canvas canvas) 
    { 
     canvas.Save(); 

     int x = (int)(canvas.Width * mScaleFactor - canvas.Width)/2; 
     int y = (int)(canvas.Height * mScaleFactor - canvas.Height)/2; 

     if (mPosX > (canvas.Width + x - 50)) 
     { 
      mPosX = canvas.Width + x - 50; 
     } 
     if (mPosX < (50 - canvas.Width - x)) 
     { 
      mPosX = 50 - canvas.Width - x; 
     } 
     if (mPosY > (canvas.Height + y - 50)) 
     { 
      mPosY = canvas.Height + y - 50; 
     } 
     if (mPosY < (50 - canvas.Height - y)) 
     { 
      mPosY = 50 - canvas.Height - y; 
     } 

     canvas.Translate(mPosX, mPosY); 
     canvas.Scale(mScaleFactor, mScaleFactor, canvas.Width/2, canvas.Height/2); 
     base.OnDraw(canvas); 
     canvas.Restore(); 
    } 

这是monodroid代码,但可以很容易地转换为JAVA。 你可以为你的边界选择任何值(这里是50像素边距)

+0

这里是一个库,将这样做 https://stackoverflow.com/questions/6578320 /如何对申请变焦拖和旋转到的图像功能于机器人/ 47861501#47861501 – user2288580