在“ListView”中拖放
Posted
技术标签:
【中文标题】在“ListView”中拖放【英文标题】:Drag and Drop in a `ListView` 【发布时间】:2012-11-24 04:08:20 【问题描述】:我正在尝试在 android(Ice Cream Sandwich) 中实现 ListView
的拖放操作。因此,当拖动的对象到达ListView
的边缘时,我正在向相关方向滚动ListView
。问题是,当我们滚动时,有时适配器会根据需要创建新的View
s,而这些“新”View
s 之前没有收到ACTION_DRAG_STARTED
事件,因此不会收到DragEvent
更新。有什么方法可以将事件也发送到这些视图?
【问题讨论】:
我相信这两个项目都能做到你想要的,所以你可以看看他们的代码:github.com/commonsguy/cwac-touchlist和github.com/bauerca/drag-sort-listview 我已经看过这些了,我想拥有自己的拖放实现,这些方法实现起来非常繁琐。我想知道如何使用较新的 API 来做到这一点。 developer.android.com/guide/topics/ui/drag-drop.html 照说的试试吧 谢谢@lochana。我已经读过了,它没有帮助。如果您可以使用列表视图和自定义适配器,您可以发布一些代码 再次看到您的问题后,我只想问一下,一旦您拖动列表视图,您是否再次重新加载它? 【参考方案1】:在列表视图中实现拖放的最简单方法是使用这个很棒的库。 https://github.com/commonsguy/cwac-touchlist 值得一试。
【讨论】:
我已经看到了这个实现,这不是要求【参考方案2】:查看 View 的源代码,我明白了:
static final int DRAG_CAN_ACCEPT = 0x00000001;
int mPrivateFlags2;
boolean canAcceptDrag()
return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0;
mPrivateFlags2 是包私有的,SDK 不公开。但是,您应该可以通过以下方式在子类中更改它:
try
Field mPrivateFlags2 = this.getClass().getField("mPrivateFlags2");
int currentValue = mPrivateFlags2.getInt(this);
mPrivateFlags2.setInt(this, currentValue | 0x00000001);
catch (Exception e)
【讨论】:
就像我说的,我看过了,但我想知道我们如何使用自 HoneyComb 以来提供的新 DragEvent 框架来做到这一点。【参考方案3】:我有same problem。我没有解决这个回收问题,但我发现了一个可能的解决方法,仍然使用拖放框架。这个想法是改变视角:而不是在列表中的每个View
上使用OnDragListener
,它可以直接在ListView
上使用。
然后想法是在进行拖放时找到手指在哪个项目上,并在ListView
的ListAdapter
中编写相关的显示代码。然后,诀窍是找到我们在哪个项目视图之上,以及放置完成的位置。
为了做到这一点,我将适配器创建的每个视图的ListView
位置设置为id
- 使用View.setId()
,这样我以后可以使用ListView.pointToPosition()
和@987654331 的组合找到它@。
作为一个拖动监听器示例(我提醒你,它应用于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;
现在,如果您在进行拖放操作时需要视觉反馈,有几种策略。例如,您的活动中可以有 2 个实例变量,名为:
private boolean ongoingDrag = false; // To know if we are in a drag&drop state
private int dragPosition = 0; // You put the itemPosition variable here
在MyListViewDragListener
中进行拖放时,您会修改这些变量,并在MyViewAdapter
中使用它们的状态。当然不要忘记使用 _myListView.getAdapter()).notifyDataSetChanged()
或 _myListView.invalidate()
之类的方法更新 UI(当然,在事件线程中使用 Handler)。
【讨论】:
能否请您详细说明代码不清楚。targetPosition
是什么,targetView
是什么。你是如何处理掉落的?
我写了更多的代码,希望更清楚。 targetPosition 和 targetView 是 itemPosition 和 itemView,我在为 *** 转换代码时犯了一些错误。我处理下降的方式与处理 ACTION_DRAG_LOCATION 的方式完全相同,为了更清楚起见,我将代码放在 ACTION_DROP 事件中。我希望它会更好,如果您还有问题,请告诉我。【参考方案4】:
问题是因为 listView.getPositionForView(view) 如果视图在调用时不可见则返回 -1。因此,当您滚动列表时,依赖它会失败。因此,您可以在调用 startDrag() 的列表项上设置 listView.setOnItemLongClickListener() 而不是设置 view.setOnLongClickListener()。 onItemLongClick() 为您提供可以在 startDrag() 的 myLocalState 参数中传递的位置。然后在 onDrag() 中使用 event.getLocalState() 恢复它并将其转换为整数。像这样……
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
position -= listView.getHeaderViewsCount();
DragShadowBuilder dragShadow = new View.DragShadowBuilder(view);
view.startDrag(null, dragShadow, position, 0);
return true;
);
然后在你的 OnDragListener...
@Override
public boolean onDrag(View eventView, DragEvent event)
Integer dragViewPos = ((Integer) event.getLocalState());
int eventViewPos = listView.getPositionForView(eventView) - listView.getHeaderViewsCount();
...
【讨论】:
以上是关于在“ListView”中拖放的主要内容,如果未能解决你的问题,请参考以下文章