拖动时ImageView跳跃。getX()和getY()值跳跃。

6
我为拖动视图创建了一个 onTouchListener。如果我使用 getRawX() 和 getRawY(),图片会平滑地拖动。但是问题是,当你放下第二个指针并抬起第一个指针时,图片会跳到第二个指针处。
这个 onTouchListener 试图通过跟踪 pointerId 来解决这个问题。但是在拖动 ImageView 时,ImageView 会非常不稳定地跳动,getX() 和 getY() 的值也会跳动。
我觉得我做得很正确。我不想为此编写自定义视图,因为我已经实现了 scaleGestureDetector 并编写了可工作的自定义 rotateGestureDetector。一切都正常运行,但我需要解决使用 getRawX() 和 getRawY() 时遇到的问题。
有人知道我做错了什么吗?
下面是我的 onTouchListener:
final View.OnTouchListener onTouchListener = new View.OnTouchListener()
{
    @Override
    public boolean onTouch(View v, MotionEvent event)
    {
        relativeLayoutParams = (RelativeLayout.LayoutParams) v.getLayoutParams();

        final int action = event.getAction();
        switch (action & MotionEvent.ACTION_MASK)
        {
            case MotionEvent.ACTION_DOWN:
            {
                final float x = event.getX();
                final float y = event.getY();

                // Where the user started the drag
                lastX = x;
                lastY = y;
                activePointerId = event.getPointerId(0);
                break;
            }
            case MotionEvent.ACTION_MOVE:
            {
                // Where the user's finger is during the drag
                final int pointerIndex = event.findPointerIndex(activePointerId);
                final float x = event.getX(pointerIndex);
                final float y = event.getY(pointerIndex);

                // Calculate change in x and change in y
                final float dx = x - lastX;
                final float dy = y - lastY;

                // Update the margins to move the view
                relativeLayoutParams.leftMargin += dx;
                relativeLayoutParams.topMargin += dy;
                v.setLayoutParams(relativeLayoutParams);

                // Save where the user's finger was for the next ACTION_MOVE
                lastX = x;
                lastY = y;

                v.invalidate();
                break;
            }
            case MotionEvent.ACTION_UP:
            {
                activePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            {
                activePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_POINTER_UP:
            {
                // Extract the index of the pointer that left the touch sensor
                final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = event.getPointerId(pointerIndex);

                if(pointerId == activePointerId)
                {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    lastX = (int) event.getX(newPointerIndex);
                    lastY = (int) event.getY(newPointerIndex);
                    activePointerId = event.getPointerId(newPointerIndex);
                }
                break;
            }
        }
        return true;
    }
};
image1.setOnTouchListener(onTouchListener);

有人有解决这个问题的方法吗?似乎其他人也遇到了这个问题,但我还没有找到解决方案。 - Ben Kane
3个回答

12
问题很简单,但出乎意料。getRawX/Y()返回绝对坐标,而getX/Y()返回相对于视图的坐标。如果我移动视图,重置lastX/Y,那么图像将不再在同一位置,因此当我获取新值时它们会偏离。在这种情况下,我只需要知道我最初按下图像的位置(而不是使用`getRawX/Y'时的情况)。
所以,解决方案就是简单地删除以下内容:
// Save where the user's finger was for the next ACTION_MOVE
lastX = x;
lastY = y;
我希望这可以帮助将来遇到类似问题的人,因为我曾经看到其他人也遇到了这个问题,而且他们的代码与我相似(重置lastX/Y)。

太好了,我找到了这个!我怀疑很多人都有保存lastX/LastY代码的经历,因为他们读了http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html。显然,在移动视图的情况下,与在“multitouch”示例中翻译画布时不同。这里的最后一条评论https://dev59.com/aHLYa4cB1Zd3GeqPW2D2?rq=1指出,当他注释掉view.setX()和view.setY()的设置时,onTouch()期间绝对和相对坐标的交错停止了。 - T. Folsom
@T.Folsom 很高兴能帮到你!是的,很多人都会模仿你第一个链接中的代码,我也是其中之一。据我所知,getX()getY()始终返回相对坐标。他的坐标跳动的原因和我一样。他移动了视图,然后获取不同的相对坐标,因为视图已经移动了。这有意义吗? - Ben Kane
是的,这确实帮助了更多的人 :) - nsg
1
2019年,仍然有用。我从官方文档(https://developer.android.com/training/gestures/scale)中获取了示例代码,并继承了这个问题。谢谢! - Vasiliy
@Vasiliy 很好 :) 我很高兴这个答案仍然有效。 - Ben Kane

1
经过大量的研究,我发现这是一个问题:getRawX返回绝对值而getX返回相对于视图的值。因此,使用此方法将一个值转换为另一个值。
//RawX = getX + View.getX

event.getRawX == event.getX(event.findPointerIndex(ptrID1))+view.getX()

0

我有一个小建议,希望能对你有所帮助。

invalidate() : does only redrawing a view,doesn't change view size/position.

你应该使用requestLayout():执行测量和布局过程。我认为当调用setLayoutParams()时,requestLayout()将被调用;
所以尝试通过删除v.invalidate()来解决问题,
或者尝试使用view.layout(left,top,right,bottom)方法而不是设置layoutParams。

我之前尝试过删除v.invalidate()(因为我认为在这种情况下它是无用的),但并没有起到帮助作用。你建议我再试一次,我刚刚又试了一遍,仍然没有改变情况。我还尝试了使用v.requestLayout()替换它,但也没有改变行为:/ 你还有其他建议吗? - Ben Kane
尝试使用view.layout(left,top,right,bottom)方法而不是设置layoutParams。 - Sreejith B Naick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接