Перетаскивание, ListView и представления элементов, которые пропускают событие перетаскивания действия
на Android, я использую ListView
и я хочу иметь возможность изменить порядок своих элементов с помощью перетаскивания. Я знаю, что есть другая реализация "перетаскивания listview", однако я хочу использовать перетащите рамки С уровень API 11.
это началось очень хорошо, пока я не захотел прокрутить мой ListView
во время перетаскивания. Как написано в приведенном ниже примере, на данный момент я проверяю, поверх какого элемента списка я нахожусь, поэтому, если его позиция не между ListView.getLastVisiblePosition()
и ListView.getFirstVisiblePosition()
Я использую ListView.smoothScrollToPosition()
для просмотра других элементов списка.
это первая реализация, но она работает достаточно хорошо.
проблема возникает при прокрутке: некоторые элементы не отвечают на события перетаскивания -DragEvent.ACTION_DRAG_ENTERED
а остальные-когда я на них сверху. Это связано с тем, как ListView управляет своими представлениями элементов: он пытается перезапустить представления элементов, которые больше не видны.
все в порядке, и это работает, но иногда getView()
на ListAdapter
возвращает новый объект. Поскольку он новый, этот объект пропустил DragEvent.ACTION_DRAG_STARTED
поэтому он не отвечает на другой DragEvent
событий!
вот пример. В этом случае, если я начну перетаскивание с длинным щелчком по элементу списка, и если я перетащу его, большинство элементов будут иметь зеленый фон, если я нахожусь поверх них ; но некоторые нет.
любая идея о том, чтобы сделать их подписаться на механизм перетаскивания событий, даже если они промахнулись!--9-->?
// Somewhere I have a ListView that use the MyViewAdapter
// MyListView _myListView = ...
// _myListView.setAdapter(new MyViewAdapter(getActivity(), ...));
_myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0);
return true;
}
});
class MyViewAdapter extends ArrayAdapter<MyElement> {
public MyViewAdapter(Context context, List<TimedElement> objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View myElementView = convertView;
if (myElementView == null) {
/* If the code is executed here while scrolling with a drag and drop,
* the new view is not associated to the current drag and drop events */
Log.d("app", "Object created!");
// Create view
// myElementView = ...
// Prepare drag and drop
myElementView.setOnDragListener(new MyElementDragListener());
}
// Associates view and position in ListAdapter, needed for drag and drop
myElementView.setTag(R.id.item_position, position);
// Continue to prepare view
// ...
return timedElementView;
}
private class MyElementDragListener implements View.OnDragListener {
@Override
public boolean onDrag(View v, DragEvent event) {
final int action = event.getAction();
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
v.setBackgroundColor(Color.GREEN);
v.invalidate();
return true;
case DragEvent.ACTION_DRAG_LOCATION:
int targetPosition = (Integer)v.getTag(R.id.item_position);
if (event.getY() < v.getHeight()/2 ) {
Log.i("app", "top "+targetPosition);
}
else {
Log.i("app", "bottom "+targetPosition);
}
// To scroll in ListView while doing drag and drop
if (targetPosition > _myListView.getLastVisiblePosition()-2) {
_myListView.smoothScrollToPosition(targetPosition+2);
}
else if (targetPosition < _myListView.getFirstVisiblePosition()+2) {
_myListView.smoothScrollToPosition(targetPosition-2);
}
return true;
case DragEvent.ACTION_DRAG_EXITED:
v.setBackgroundColor(Color.BLUE);
v.invalidate();
return true;
case DragEvent.ACTION_DROP:
case DragEvent.ACTION_DRAG_ENDED:
default:
break;
}
return false;
}
}
}
1 ответов
я не решил эту проблему утилизации, но я нашел возможное решение, все еще Используя фреймворк Drag & Drop. Идея состоит в том, чтобы изменить перспективу: вместо использования OnDragListener
в каждом View
в списке он может использоваться на ListView
напрямую.
тогда идея состоит в том, чтобы найти поверх какого элемента палец во время перетаскивания и написать соответствующий код дисплея в ListAdapter
на ListView
. Трюк заключается в том, чтобы найти поверх которого элемента view мы и где это сделать.
для этого я установил как id
к каждому виду, созданному адаптером его ListView
установки - с View.setId()
, так что я могу найти его позже, используя комбинацию ListView.pointToPosition()
и ListView.findViewById()
.
в качестве примера прослушивателя перетаскивания (который, напоминаю вам, применяется на ListView
), это может быть что-то вроде этого:
// Initalize your ListView
private ListView _myListView = new ListView(getContext());
// Start drag when long click on a ListView item
_myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0);
return true;
}
});
// Set the adapter and drag listener
_myListView.setOnDragListener(new MyListViewDragListener());
_myListView.setAdapter(new MyViewAdapter(getActivity()));
// Classes used above
private class MyViewAdapter extends ArrayAdapter<Object> {
public MyViewAdapter (Context context, List<TimedElement> objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View myView = convertView;
if (myView == null) {
// Instanciate your view
}
// Associates view and position in ListAdapter, needed for drag and drop
myView.setId(position);
return myView;
}
}
private class MyListViewDragListener implements View.OnDragListener {
@Override
public boolean onDrag(View v, DragEvent event) {
final int action = event.getAction();
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_DROP:
// We drag the item on top of the one which is at itemPosition
int itemPosition = _myListView.pointToPosition((int)event.getX(), (int)event.getY());
// We can even get the view at itemPosition thanks to get/setid
View itemView = _myListView.findViewById(itemPosition );
/* If you try the same thing in ACTION_DRAG_LOCATION, itemView
* is sometimes null; if you need this view, just return if null.
* As the same event is then fired later, only process the event
* when itemView is not null.
* It can be more problematic in ACTION_DRAG_DROP but for now
* I never had itemView null in this event. */
// Handle the drop as you like
return true;
}
}
}