Android: using MotionEvent to drag items in custom ViewGroup

I am having problems positioning view elements in the custom ViewGroup I created, especially in drag and drop situations. I am targeting Android 2.2 and higher, so I cannot use the drag and drop API introduced in Android 3.

My custom ViewGroup is called "NodeGrid" and it extends "RelativeLayout". Its onLayout method is overridden so that it looks like this:

@Override protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) { //pxConversion is a multiplier to convert 1 dip to x pixels. //We will use it to convert dip units into px units. Resources r = getResources(); float pxConversion = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, r.getDisplayMetrics()); //Layout the children of this viewgroup int childCount = this.getChildCount(); for (int i = 0; i < childCount; i++) { NodeView view = (NodeView) this.getChildAt(i); if (view.DataContext != null) { int left = (int) (view.DataContext.get_left() * pxConversion); int top = (int) (view.DataContext.get_top() * pxConversion); int right = left + (int) (view.DataContext.get_width() * pxConversion); int bottom = top + (int) (view.DataContext.get_height() * pxConversion); view.layout(left, top, right, bottom); } } } 

Children from NodeGrid are of type NodeView (as you can see in the code above). NodeView is a simple custom View class that I created. It contains an element called "DataContext", which is a view model class that contains some getters / setters with location information for the NodeView instance in the NodeGrid.

My "NodeView" class captures touch events by the user, so the user can simply drag the node anywhere on the grid. The event handler is as follows:

 @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { isBeingDragged = true; } else if (event.getAction() == MotionEvent.ACTION_UP) { isBeingDragged = false; } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (DataContext != null && isBeingDragged) { float xPosition = event.getRawX(); float yPosition = event.getRawY(); DataContext.set_left(xPosition); DataContext.set_top(yPosition); this.requestLayout(); } } return true; } 

My problem is that the view being viewed is not positioned as I expect on my "NodeGrid". It seems that the position of the x axis is correct when I drag it, but the position of the y axis is shifted by a number of pixels at any time. What could be the reason for this? I tried using the getX () and getY () methods, not getRawX () and getRawY (), but that only worsens it.

I was not sure if getRawX () and getRawY () would return units of units or drop units to me, so expecting it to return units of px to me, I tried converting them to dip units before assigning new x and y to node but it only reduced the displacement, it did not eliminate it.

What can cause a difference in where I touch and where the node is?

+4
source share
1 answer

I was very surprised that no one else seemed to have encountered such a situation ...

After some research, I was partially able to solve the problem.

The reason that getRawX () and getRawY () does not work is because they work with the absolute position of the screen without taking into account anything on the screen that is not part of your application (for example, the Android menu, which is constantly at the top of the screen if you are not in full screen). Using these coordinates seemed hacky, especially without a visible way of justifying them and converting them into the coordinates of "my application."

getX () and getY () seem to be relative to the affected object, as far as I can tell. With that in mind, I changed the code in onTouchEvent to read:

 float xPosition = event.getX(); float yPosition = event.getY(); DataContext.set_left(DataContext.get_left() + xPosition); DataContext.set_top(DataContext.get_top() + yPosition); this.requestLayout(); 

It definitely helps ... but it still doesn't seem like the perfect solution. In general, this makes the view object drag along with my mouse / finger cursor, but it is very nervous, and there are still moments when it can become awkward and exit the screen.

Then I thought that maybe this is because I do not include “historical coordinates” in the method. I tried to include the historical coordinates in the method by placing this code immediately before the code I just displayed above:

 int historySize = event.getHistorySize(); for (int i = 0; i < historySize; i++) { float xPosition = event.getHistoricalX(i); float yPosition = event.getHistoricalY(i); DataContext.set_left(DataContext.get_left() + xPosition); DataContext.set_top(DataContext.get_top() + yPosition); this.requestLayout(); } 

Unfortunately ... this actually makes it worse, so either I am not using the historical coordinates correctly, or they should not be used at all. I will post one more question on this question, but this is enough to partially answer my question above.

+1
source

Source: https://habr.com/ru/post/1383142/


All Articles