2015-03-31 79 views
2

我想实现在应该重点放在一个枢轴点的画布上的缩放。缩放工作正常,但之后用户应该能够选择画布上的元素。问题是,我的翻译值似乎是不正确的,因为他们有一个不同的偏移量,比我没有放大到枢轴点(没有枢轴点和拖动工作正常的缩放)。 我使用this example的一些代码。画布缩放与枢轴点后,x和y坐标是错误的

相关的代码是:

class DragView extends View { 

private static float MIN_ZOOM = 0.2f; 
private static float MAX_ZOOM = 2f; 

// These constants specify the mode that we're in 
private static int NONE = 0; 
private int mode = NONE; 
private static int DRAG = 1; 
private static int ZOOM = 2; 
public ArrayList<ProcessElement> elements; 

// Visualization 
private boolean checkDisplay = false; 
private float displayWidth; 
private float displayHeight; 
// These two variables keep track of the X and Y coordinate of the finger when it first 
// touches the screen 
private float startX = 0f; 
private float startY = 0f; 
// These two variables keep track of the amount we need to translate the canvas along the X 
//and the Y coordinate 
// Also the offset from initial 0,0 
private float translateX = 0f; 
private float translateY = 0f; 

private float lastGestureX = 0; 
private float lastGestureY = 0; 

private float scaleFactor = 1.f; 
private ScaleGestureDetector detector; 
... 

private void sharedConstructor() { 
    elements = new ArrayList<ProcessElement>(); 
    flowElements = new ArrayList<ProcessFlow>(); 
    detector = new ScaleGestureDetector(getContext(), new ScaleListener()); 
} 

/** 
* checked once to get the measured screen height/width 
* @param hasWindowFocus 
*/ 
@Override 
public void onWindowFocusChanged(boolean hasWindowFocus) { 
    super.onWindowFocusChanged(hasWindowFocus); 
    if (!checkDisplay) { 
     displayHeight = getMeasuredHeight(); 
     displayWidth = getMeasuredWidth(); 
     checkDisplay = true; 
    } 
} 

@Override 
public boolean onTouchEvent(MotionEvent event) { 
    ProcessBaseElement lastElement = null; 

    switch (event.getAction() & MotionEvent.ACTION_MASK) { 
     case MotionEvent.ACTION_DOWN: 
      mode = DRAG; 

      // Check if an Element has been touched. 
      // Need to use the absolute Position that's why we take the offset into consideration 
      touchedElement = isElementTouched(((translateX * -1) + event.getX())/scaleFactor, (translateY * -1 + event.getY())/scaleFactor); 


       //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated 
       //amount for each coordinates This works even when we are translating the first time because the initial 
       //values for these two variables is zero. 
       startX = event.getX() - translateX; 
       startY = event.getY() - translateY; 
      } 
      // if an element has been touched -> no need to take offset into consideration, because there's no dragging possible 
      else { 
       startX = event.getX(); 
       startY = event.getY(); 
      } 

      break; 

     case MotionEvent.ACTION_MOVE: 
      if (mode != ZOOM) { 
       if (touchedElement == null) { 
        translateX = event.getX() - startX; 
        translateY = event.getY() - startY; 
       } else { 
        startX = event.getX(); 
        startY = event.getY(); 
       } 
      } 

      if(detector.isInProgress()) { 
       lastGestureX = detector.getFocusX(); 
       lastGestureY = detector.getFocusY(); 
      } 

      break; 

     case MotionEvent.ACTION_UP: 
      mode = NONE; 

      break; 
     case MotionEvent.ACTION_POINTER_DOWN: 
      mode = ZOOM; 

      break; 
     case MotionEvent.ACTION_POINTER_UP: 
      break; 
    } 

    detector.onTouchEvent(event); 
    invalidate(); 

    return true; 
} 

private ProcessBaseElement isElementTouched(float x, float y) { 
    for (int i = elements.size() - 1; i >= 0; i--) { 
     if (elements.get(i).isTouched(x, y)) 
      return elements.get(i); 
    } 
    return null; 
} 

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

    canvas.save(); 

    if(detector.isInProgress()) { 
     canvas.scale(scaleFactor,scaleFactor,detector.getFocusX(),detector.getFocusY()); 
    } else 
     canvas.scale(scaleFactor, scaleFactor,lastGestureX,lastGestureY);  // zoom 

//  canvas.scale(scaleFactor,scaleFactor); 

    //We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level 
    //because the translation amount also gets scaled according to how much we've zoomed into the canvas. 
    canvas.translate(translateX/scaleFactor, translateY/scaleFactor); 

    drawContent(canvas); 

    canvas.restore(); 
} 

/** 
* scales the canvas 
*/ 
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; 
    } 
} 
} 

元素被保存在画布上的绝对位置(在考虑拖动)。我怀疑我不考虑从translateXtranslateY这个支点的新偏移量,但我无法弄清楚我应该在哪里以及如何执行此操作。 任何帮助,将不胜感激。

回答

7

好的,所以你基本上试图找出某个屏幕X/Y坐标对应的位置,在视图缩放到某个轴心点{Px,Py}之后。

所以,我们试着分解它。

为了说明起见,我们假设Px & Py = 0,那s = 2。这意味着视图在视图的左上角放大了2倍。

在这种情况下,屏幕坐标{0,0}对应于视图中的{0,0},因为该点是唯一没有改变的点。一般来说,如果屏幕坐标等于支点,那么没有改变。

如果用户点击其他点,比如{2,3},会发生什么?在这种情况下,曾经{2,3}现在已经从枢轴点({0,0})移动了2倍,所以相应的位置是{4,6}。

当枢轴点是{0,0}时,所有这些都很简单,但是当它不是时会发生什么?那么,让我们来看另一种情况 - 轴心点现在是视图的右下角(宽度= w,高度= h - {w,h})。同样,如果用户点击相同的位置,那么相应的位置也是{w,h},但可以说用户点击了其他位置,例如{w-2,h-3}?同样的逻辑发生在这里:翻译的位置是{w-4,h-6}。

总而言之,我们要做的就是将屏幕坐标转换为翻译后的坐标。我们需要在收到的X/Y坐标上执行相同的操作,即对缩放视图中的每个像素执行的操作。

步骤1 - 我们想根据枢转点翻译的X/Y位置:

X = X - Px 
Y = Y - Py 

步骤2 - 然后,我们扩展X & Y:

X = X * s 
Y = Y * s 

第3步 - 然后我们翻译回来:

X = X + Px 
Y = Y + Py 

如果我们应用此我把最后一个例子(我只展示了X):

Original value: X = w - 2, Px = w 
Step 1: X <-- X - Px = w - 2 - w = -2 
Step 2: X <-- X * s = -2 * 2 = -4 
Step 3: X <-- X + Px = -4 + w = w - 4 

一旦将此任何X/Y收到该变焦之前是相关的,该点将被翻译以便它相对于缩放状态。

希望这会有所帮助。

+0

这个答案列出了真正的原则。你太棒了!非常感谢你! – drakeet 2016-10-03 05:42:13