The destination application allows the user to drag and drop views to move them to FrameLayout or save them to another LinearLayout.
The general mechanism works, but I canβt get the necessary accuracy in the position when dropping the view, it "jumps" a little further. How could I get the position of the shadow shadow to place the fallen view exactly where the shadow was in order to avoid the βjumpβ artifact?
For example, the layout is quite simple, here is an overview with two TextView objects that can be moved (I deleted all the details):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:id="@+id/repository"/> <FrameLayout android:id="@+id/frame"> <TextView android:id="@+id/view1"/> <TextView android:id="@+id/view2"/> </FrameLayout> </LinearLayout>
First, to improve the starting position of the shadow itself, I redefine onProvideShadowMetrics . Due to this, the view does not jump when the user starts to drag it:
private class MyDragShadowBuilder extends View.DragShadowBuilder { private float x, y; public MyDragShadowBuilder(View view, float x, float y) { super(view); this.x = x; this.y = y; } @Override public void onProvideShadowMetrics(Point size, Point touchPoint) { super.onProvideShadowMetrics(size, touchPoint); touchPoint.set((int) x, (int) y); } }
Then, the OnTouchListener implementation is used to start the drag operation:
private class LongClickListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { TextView tv = (TextView) v; if (tv != null) { ClipData data = ClipData.newPlainText("", ""); MyDragShadowBuilder shadowBuilder = new MyDragShadowBuilder(v, event.getX(), event.getY()); v.startDrag(data, shadowBuilder, v, 0); v.setVisibility(View.INVISIBLE); return true; } } return false; } }
The drop operation is where I would like to get the starting position x, y of the βfingerβ in the view in order to position the view in the same way as a shadow.
private class DragListener implements View.OnDragListener { private final Class<?> FRAMELAYOUT_CLASS = FrameLayout.class; private final Class<?> LINEARLAYOUT_CLASS = LinearLayout.class; private Drawable enterShape = getResources().getDrawable(R.drawable.shape_droptarget); private Drawable normalShape = getResources().getDrawable(R.drawable.shape); @Override public boolean onDrag(View v, DragEvent event) { int action = event.getAction(); switch (action) { case DragEvent.ACTION_DRAG_STARTED: break; case DragEvent.ACTION_DRAG_ENTERED: v.setBackground(enterShape); break; case DragEvent.ACTION_DRAG_EXITED: v.setBackground(normalShape); break; case DragEvent.ACTION_DROP: View view = (View) event.getLocalState(); ViewGroup owner = (ViewGroup) view.getParent(); ViewGroup container = (ViewGroup) v; if (container.getClass() == FRAMELAYOUT_CLASS) {
Everything that is set in the OnCreate method of activity:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); View v = findViewById(R.id.view1); v.setOnTouchListener(new LongClickListener()); v = findViewById(R.id.view2); v.setOnTouchListener(new LongClickListener()); v = findViewById(R.id.frame); v.setOnDragListener(new DragListener()); v = findViewById(R.id.repository); v.setOnDragListener(new DragListener()); }
Thanks in advance for any feedback on how I should get this information!
EDIT . Obviously, I could transfer data to ClipData, but that would be inconvenient (especially if I need to transfer large arrays of data in the future, serialization here is redundant):
ClipData data = ClipData.newPlainText( "pos", String.format("%d %d", (int)event.getX(), (int)event.getY())); v.startDrag(data, shadowBuilder, v, 0);
...
if (container.getClass() == FRAMELAYOUT_CLASS) { ClipData data = event.getClipData(); String[] pos = ((String)data.getItemAt(0).getText()).split(" "); int x = Integer.valueOf(pos[0]); int y = Integer.valueOf(pos[1]); view.setX(event.getX() - x); view.setY(event.getY() - y);