Jumping ImageView while dragging. getX() and getY() values are jumping
Asked Answered
A

3

6

I've created an onTouchListener for dragging Views. Images drag smoothly if I use getRawX() and getRawY(). The problem with that is the image will jump to the second pointer when you place a second pointer down then lift the first pointer.

This onTouchListener attempts to fix that issue by keeping track of the pointerId. The problem with this onTouchListener is while dragging an ImageView, the ImageView jumps around pretty crazily. The getX() and getY() values jump around.

I feel like I'm doing this correctly. I don't want to have to write a custom view for this because I've already implemented a scaleGestureDetector and written a custom rotateGestureDetector that work. Everything works fine but I need to fix the issue I get when using getRawX() and getRawY().

Does anybody know what I'm doing wrong here?

Here's my 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);
Alejandrinaalejandro answered 8/7, 2013 at 15:36 Comment(1)
Anybody have a solution to this? Seems like others have run into this issue, but I haven't found a solution yet.Alejandrinaalejandro
A
12

The issue was simple, but unexpected. getRawX/Y() returns absolute coordinates, while getX/Y() returns coordinates relative to the view. I would move the view, reset lastX/Y, and the image wouldn't be in the same spot anymore so when I get new values they'd be off. In this case I only needed where I originally pressed the image (not the case when using `getRawX/Y').

So, the solution was to simply remove the following:

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

I hope this will help somebody in the future, because I've seen others with this problem, and they had similar code to me (resetting lastX/Y)

Alejandrinaalejandro answered 9/7, 2013 at 13:28 Comment(5)
Glad I found this! I suspect many people have code for saving the lastX/LastY because of reading android-developers.blogspot.com/2010/06/… . Apparently the situation of moving a view differs from the example in "multitouch" where a canvas is being translated. The last comment here, #16676597, notes that when he commented out the setting of view.setX() and view.setY(), the interleaving of absolute and relative coordinates during onTouch() stopped.Modulate
@T.Folsom Glad I could help! Yes, a lot of people model their code after your first link, myself included. As far as I know, getX() and getY() always return relative coordinates. His coordinates were jumping for the same reason as me. He moves the view then gets different relative coordinates because the view has shifted. Does that make sense?Alejandrinaalejandro
Yes, this did help more people :)Totaquine
2019, still helpful. I've got the sample code from the official docs (developer.android.com/training/gestures/scale) and inherited this issue. Thanks!Rey
@Rey Wonderful :) I’m glad the answer has held upAlejandrinaalejandro
C
1

After a lot of research, I found this to be the issue, getRawX is absolute and getX is relative to view. hence use this to transform one to another

//RawX = getX + View.getX

event.getRawX == event.getX(event.findPointerIndex(ptrID1))+view.getX()
Can answered 18/7, 2016 at 13:24 Comment(0)
H
0

I have One tip & think it will help.

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

What you should use is requestLayout(): does the measuring and layout process. & i think requestLayout() will be called when call setLayoutParams();

So Try by removing v.invalidate()

or try using view.layout(left,top,right,bottom) method instead of setting layoutParams.

Haggar answered 8/7, 2013 at 17:1 Comment(2)
I've tried removing v.invalidate() in the past (tried it again just now since you suggested it) since I figured it's useless in this scenario, doesn't help though. I tried replacing it with v.requestLayout() and that didn't change the behavior either :/ Any other suggestions?Alejandrinaalejandro
or try using view.layout(left,top,right,bottom) method instead of setting layoutParams.Haggar

© 2022 - 2024 — McMap. All rights reserved.