Как ограничить перетаскивание вдоль оси y только в android?

Я пытаюсь ограничить движение перетаскиванием только по оси Y, чтобы пользователь мог только просмотреть и перетащить его вверх или вниз – не влево или вправо.

У меня есть два вида (идентификаторы textView и dropZone) прямо сейчас. У одного (textView) установлен сенсорный прослушиватель, а у другого (dropZone) установлен трекер-прослушиватель.

Вот макет xml (activity_main.xml):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <TextView android:id="@+id/textView" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:background="#FFFFFF00" android:text="Text Drag" /> <TextView android:id="@+id/dropZone" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="167dp" android:background="#FFFF0000" android:text="Drop Zone" /> </RelativeLayout> 

Ниже приведен код активности:

 package com.example.dragexperiment; import android.app.Activity; import android.content.ClipData; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.DragEvent; import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.View.DragShadowBuilder; import android.view.View.OnDragListener; import android.view.View.OnGenericMotionListener; import android.view.View.OnTouchListener; import android.widget.RelativeLayout; import android.widget.TextView; public class MainActivity extends Activity { TextView tv, dz, sv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.textView); dz = (TextView) findViewById(R.id.dropZone); tv.setOnTouchListener(new MyTouchListener()); dz.setOnDragListener(new MyDragListener()); } private final class MyTouchListener implements OnTouchListener { int viewX0, viewY0, cY0, cY1, deltaCursorY; @Override public boolean onTouch(final View v,final MotionEvent event) { switch(event.getAction()) { case MotionEvent.ACTION_DOWN: viewX0 = (int) v.getX(); viewY0 = (int) v.getY(); cY0 = (int) event.getRawY(); ClipData data = ClipData.newPlainText("", ""); DragShadowBuilder shadow = new View.DragShadowBuilder(v); v.startDrag(data, shadow, v, 0); return true; case MotionEvent.ACTION_MOVE: // cY1 = (int) event.getRawY(); // deltaCursorY = cY1 - cY0; // v.setX(viewX0); // v.setY(viewY0 + deltaCursorY); return true; } return false; } } class MyDragListener implements OnDragListener { public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: break; case DragEvent.ACTION_DRAG_ENTERED: v.setBackgroundColor(Color.BLUE); break; case DragEvent.ACTION_DRAG_EXITED: break; case DragEvent.ACTION_DROP: v.setBackgroundColor(Color.BLUE); break; case DragEvent.ACTION_DRAG_ENDED: break; default: break; } return true; } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } } 

Как вы можете видеть из приведенного выше кода, когда пользователь перетаскивает textView над dropZone, я пытаюсь превратить цвет фона вида DropZone в синий. Это отлично работает, если я использую создание DragShadowBuilder в событии движения ACTION_DOWN:

  ClipData data = ClipData.newPlainText("", ""); DragShadowBuilder shadow = new View.DragShadowBuilder(v); v.startDrag(data, shadow, v, 0); 

Проблема в том, что я не могу контролировать тень так, чтобы она двигалась только по оси Y (по вертикали).

Если я вытащу код DragShadowBuilder (три строки выше) и добавлю код, чтобы переместить перетаскиваемый вид в событии движения ACTION_MOVE (он прокомментирован выше):

  cY1 = (int) event.getRawY(); deltaCursorY = cY1 - cY0; v.setX(viewX0); v.setY(viewY0 + deltaCursorY); 

Затем я могу контролировать textView для перемещения только по оси Y. К сожалению, без DragShadowBuilder я не могу заставить ACTION_DRAG_ENTERED DragEvent запускать, чтобы отобразить синюю позицию dropZone.

Если я оставлю код DragShadowBuilder и четыре строки кода выше, событие движения ACTION_MOVE запускается только один раз для перетаскивания; Он не продолжает следовать за перетаскиванием.

У кого-нибудь есть идеи относительно того, что я могу сделать? Я некоторое время общался с этим, но не повезло. Я даже попытался в какой-то момент создать пользовательский вид, который расширяет класс Android View, но я не могу переопределить метод startDrag, потому что он объявлен как final. Хотел бы я, чтобы мне было лучше. 🙁

Ах, ха! Я не смог найти этот ответ нигде (и много людей спрашивали), вот что я сделал.

Основная идея заключалась в том, чтобы сделать DragShadow невидимым и передать движение оси Y виду, которое вы хотите «перетащить». Таким образом, на самом деле вы перетаскиваете DragShadow, но пользователю будет показано, что перемещаемое представление перемещается.

 draggableItem.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(final View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { ClipData data = ClipData.newPlainText("", ""); View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(new View(getApplicationContext())); view.startDrag(data, shadowBuilder, view, 0); return true; } else { return false; } } }); mDropZone.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: ViewGroup.MarginLayoutParams lParams = (ViewGroup.MarginLayoutParams) arcMenu.getLayoutParams(); _yDelta = (int) event.getY() - lParams.topMargin; break; case DragEvent.ACTION_DRAG_LOCATION: ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) arcMenu.getLayoutParams(); layoutParams.topMargin = (int) event.getY() - _yDelta; arcMenu.setLayoutParams(layoutParams); break; case DragEvent.ACTION_DROP: // Dropped, reassign View to ViewGroup View view = (View) event.getLocalState(); // Do your drop actions here view.setVisibility(View.VISIBLE); break; default: break; } return true; } }); 

Вам также необходимо объявить переменную _yDelta в вашем классе.

Надеюсь, это сработает для всех твоих крутых кошек!

Следующий код делает невидимым тень перетаскивания, но все же сохраняет вид видимым:

 View transparentView = new FrameLayout(getContext()); DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(transparentView); view.startDrag(data, shadowBuilder, view, 0); 

Вы можете изменить значение y вида из события перетаскивания следующим образом:

 view.setY(event.getY());