2012-08-29 198 views
11

我已经为ImageView创建了缩放和平移类。缩放和平移ImageView Android

功能我想创建。 - 单手指触摸和移动平移 - 它放大和平移两个手指触摸和移动

大多数情况下,它的工作原理非常好。 它有一个轻微的错误,当我做以下事情: - 我用一根手指泛(状态:没问题) - 我放下了第二个手指,缩放和平移(状态:没问题) - 我释放我的第二个手指(状态:图像跳动一点)

希望有人能帮我解决这个问题。

我认为它必须有任何帮助重置mLastTouchX和mLastTouchY在“案例MotionEvent.ACTION_POINTER_UP”

将大大理解的事!

import android.content.Context; 
import android.graphics.Canvas; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.ScaleGestureDetector; 
import android.widget.ImageView; 

public class MyImageView extends ImageView { 

    private static final int INVALID_POINTER_ID = -1; 

    private float mPosX; 
    private float mPosY; 

    private float mLastTouchX; 
    private float mLastTouchY; 
    private float mLastGestureX; 
    private float mLastGestureY; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f; 

    public MyImageView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
     mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); 
    } 

    public MyImageView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     // Let the ScaleGestureDetector inspect all events. 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: { 
       if (!mScaleDetector.isInProgress()) { 
        final float x = ev.getX(); 
        final float y = ev.getY(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
        mActivePointerId = ev.getPointerId(0); 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_1_DOWN: { 
       if (mScaleDetector.isInProgress()) { 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 
        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 
       break; 
      } 
      case MotionEvent.ACTION_MOVE: { 

       // Only move if the ScaleGestureDetector isn't processing a gesture. 
       if (!mScaleDetector.isInProgress()) { 
        final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
        final float x = ev.getX(pointerIndex); 
        final float y = ev.getY(pointerIndex); 

        final float dx = x - mLastTouchX; 
        final float dy = y - mLastTouchY; 

        mPosX += dx; 
        mPosY += dy; 

        invalidate(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
       } 
       else{ 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 

        final float gdx = gx - mLastGestureX; 
        final float gdy = gy - mLastGestureY; 

        mPosX += gdx; 
        mPosY += gdy; 

        invalidate(); 

        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 

       break; 
      } 
      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_UP: { 
       final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
         >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = ev.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        Log.d("DEBUG", "mActivePointerId"); 
        // This was our active pointer going up. Choose a new 
        // active pointer and adjust accordingly. 
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
        mLastTouchX = ev.getX(newPointerIndex); 
        mLastTouchY = ev.getY(newPointerIndex); 
        mActivePointerId = ev.getPointerId(newPointerIndex); 
       } 

       break; 
      } 
     } 

     return true; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 

     canvas.save(); 

     canvas.translate(mPosX, mPosY); 

     if (mScaleDetector.isInProgress()) { 
      canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY()); 
     } 
     else{ 
      canvas.scale(mScaleFactor, mScaleFactor); 
     } 
     super.onDraw(canvas); 
     canvas.restore(); 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); 

      invalidate(); 
      return true; 
     } 
    } 
} 
+0

hm。尝试你的代码,并观察小图像,然后我把第二根手指的图像'跳'到它。这是你正在悔改的错误吗? – sandrstar

+0

是的,跳跃是我想要解决的。 – Hank

+0

看起来像是使用焦点坐标造成的。稍后将尝试调试代码。 – sandrstar

回答

16

看来canvas.scale()在“的onDraw”方法的“其他”语句所需的mLastGestureX和mLastGestureY停止跳动。 我也刷新mLastTouchX和mLastTouchY要回单指当平移“的情况下MotionEvent.ACTION_POINTER_UP”

这里的决赛中,可能不适合所有人都导致它不限制平移过去的图像边界但应该是很容易实现在那个主题上有很多讨论。

import android.content.Context; 
import android.graphics.Canvas; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.ScaleGestureDetector; 
import android.widget.ImageView; 

public class MyImageView extends ImageView { 

    private static final int INVALID_POINTER_ID = -1; 

    private float mPosX; 
    private float mPosY; 

    private float mLastTouchX; 
    private float mLastTouchY; 
    private float mLastGestureX; 
    private float mLastGestureY; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f; 

    public MyImageView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
     mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); 
    } 

    public MyImageView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     // Let the ScaleGestureDetector inspect all events. 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: { 
       if (!mScaleDetector.isInProgress()) { 
        final float x = ev.getX(); 
        final float y = ev.getY(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
        mActivePointerId = ev.getPointerId(0); 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_1_DOWN: { 
       if (mScaleDetector.isInProgress()) { 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 
        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 
       break; 
      } 
      case MotionEvent.ACTION_MOVE: { 

       // Only move if the ScaleGestureDetector isn't processing a gesture. 
       if (!mScaleDetector.isInProgress()) { 
        final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
        final float x = ev.getX(pointerIndex); 
        final float y = ev.getY(pointerIndex); 

        final float dx = x - mLastTouchX; 
        final float dy = y - mLastTouchY; 

        mPosX += dx; 
        mPosY += dy; 

        invalidate(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
       } 
       else{ 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 

        final float gdx = gx - mLastGestureX; 
        final float gdy = gy - mLastGestureY; 

        mPosX += gdx; 
        mPosY += gdy; 

        invalidate(); 

        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 

       break; 
      } 
      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_UP: { 

       final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
         >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = ev.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        // This was our active pointer going up. Choose a new 
        // active pointer and adjust accordingly. 
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
        mLastTouchX = ev.getX(newPointerIndex); 
        mLastTouchY = ev.getY(newPointerIndex); 
        mActivePointerId = ev.getPointerId(newPointerIndex); 
       } 
       else{ 
        final int tempPointerIndex = ev.findPointerIndex(mActivePointerId); 
        mLastTouchX = ev.getX(tempPointerIndex); 
        mLastTouchY = ev.getY(tempPointerIndex); 
       } 

       break; 
      } 
     } 

     return true; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 

     canvas.save(); 

     canvas.translate(mPosX, mPosY); 

     if (mScaleDetector.isInProgress()) { 
      canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY()); 
     } 
     else{ 
      canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY); 
     } 
     super.onDraw(canvas); 
     canvas.restore(); 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); 

      invalidate(); 
      return true; 
     } 
    } 
} 
+0

如何限制它移出ImageView? –

+0

因为它是一个更强大的图形映射器,所有东西都被移动到了OpenGL中,但是我认为如果你想知道你的图像的边界是什么,你可以检查并防止平移 – Hank

+0

当第二个手指是放在屏幕上。 –

1

我一直在玩弄一个解决这个问题的一个多星期,和它给了我一吨的问题。不过,我已经明显缩小了这个问题的范围。上述解决方案对我无效,但我的解决方案非常接近。问题是无论何时按下或抬起第二根手指,它都会跳动。我发现发生这种情况是因为mPosX和mPosY并不总是真正代表假设所代表的变量。这就是我的意思是:

当ACTION_MOVE被调用,代码进入了“其他”的声明(处理缩放事件),mPosX和mPosY只更改根据焦点的变化,变焦变化。这意味着使用两根手指进行平移可以工作,并且可以用两根手指进行缩放,但mPosX和mPosY不会因变化的放大而适当地改变。

我一直在试图找出方法来解决这个问题使用变焦差变化(mScaleDetector.getScaleFactor()),并在对焦差的变化,但我似乎无法通过逻辑不够好工作找到有效的东西。

另一个解决方案是将所有缩放操作移动到OnTouchListener中,并完全摆脱ScaleListener。这意味着更多的数学,但它肯定是一个解决方案。

这里onDraw有:

@Override 
    public void onDraw(Canvas c) { 
     c.save(); 

     if (mScaleDetector.isInProgress()) { 
      c.scale(mScaleFactor, mScaleFactor, mLastGestureX - mPosX, 
        mLastGestureY - mPosY); 
     } else { 
      c.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY); 
     } 

     c.translate(mPosX/mScaleFactor, mPosY/mScaleFactor); 

     // drawing instruction here 

     c.restore(); 
    } 

下面是代码如何反应手指按压:

@Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
     case MotionEvent.ACTION_DOWN: { 
      if (!mScaleDetector.isInProgress()) { 
       final float x = ev.getX(); 
       final float y = ev.getY(); 

       mLastTouchX = x; 
       mLastTouchY = y; 

       mActivePointerId = ev.getPointerId(0); 
      } 
      break; 
     } 

     case MotionEvent.ACTION_POINTER_DOWN: { 
      if (!mScaleDetector.isInProgress()) { 
       final float gx = mScaleDetector.getFocusX(); 
       final float gy = mScaleDetector.getFocusY(); 

       mLastGestureX = gx; 
       mLastGestureY = gy; 
      } 
      break; 
     } 

     case MotionEvent.ACTION_MOVE: { 
      if (!mScaleDetector.isInProgress()) { 
       Log.i("hi", "SD not in progress"); 
       final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
       final float x = ev.getX(pointerIndex); 
       final float y = ev.getY(pointerIndex); 

       final float dx = x - mLastTouchX; 
       final float dy = y - mLastTouchY; 

       mPosX += dx; 
       mPosY += dy; 

       invalidate(); 

       mLastTouchX = x; 
       mLastTouchY = y; 
      } else { 
       Log.i("hi", "SD in progress"); 
       final float gx = mScaleDetector.getFocusX(); 
       final float gy = mScaleDetector.getFocusY(); 

       final float gdx = gx - mLastGestureX; 
       final float gdy = gy - mLastGestureY; 

       mPosX += gdx; 
       mPosY += gdy; 

       // SOMETHING NEEDS TO HAPPEN RIGHT HERE. 

       invalidate(); 

       mLastGestureX = gx; 
       mLastGestureY = gy; 
      } 

      break; 
     } 

     case MotionEvent.ACTION_POINTER_UP: { 

      final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
      final int pointerId = ev.getPointerId(pointerIndex); 
      if (pointerId == mActivePointerId) { 
       // This was our active pointer going up. Choose a new 
       // active pointer and adjust accordingly. 
       final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 

       mLastTouchX = ev.getX(newPointerIndex); 
       mLastTouchY = ev.getY(newPointerIndex); 

       mActivePointerId = ev.getPointerId(newPointerIndex); 
      } else { 
       final int tempPointerIndex = ev.findPointerIndex(mActivePointerId); 

       mLastTouchX = ev.getX(tempPointerIndex); 
       mLastTouchY = ev.getY(tempPointerIndex); 
      } 

      break; 
     } 
     } 

     return true; 
    } 

虽然它主要是无关的,这里的ScaleListener:

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      invalidate(); 
      return true; 
     } 
    } 

再次,这段代码并不完美,但它非常接近。我已经解释了上述确切的问题,并且仍然无法正常工作。我不知道这是否会在您的通知中弹出,汉克,但希望有人会看到它并帮助我。

+0

谢谢,这是对以前代码的改进,但是当第二根手指在屏幕上被触摸时,它仍会跳跃。 –

0

汉克的解决方案适用于我。我添加了重置功能,因此后续图像可以正常显示。

public void ResetView() { 
     mScaleFactor = 1.f; 
     mPosX = 0.f; 
     mPosY = 0.f; 
    } 
0

W/O看代码我会假设你正在做基于2个手指的位置计算时有2个手指。在这种情况下,你总是会跳起来的。

+1

Lol为什么在问题解决后三年回答? – Hank

+2

哈哈,你为什么在乎? –