18

ListView我正在尝试在 android(Ice Cream Sandwich) 中实现拖放。因此,当拖动的对象到达 边缘时ListView,我正在ListView向相关方向滚动。问题是当我们滚动时,有时适配器会View根据需要创建新的,而这些新View的没有ACTION_DRAG_STARTED更早地接收到事件,因此没有接收到DragEvent更新。有什么方法可以将事件发送到这些视图吗?

4

4 回答 4

1

在列表视图中实现拖放的最简单方法是使用这个很棒的库。 https://github.com/commonsguy/cwac-touchlist 值得一试。

于 2012-12-14T08:57:09.407 回答
0

查看 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) {
}
于 2012-12-11T08:43:24.280 回答
0

我有同样的问题。我没有解决这个回收问题,但我发现了一个可能的解决方法,仍然使用拖放框架。这个想法是改变视角:而不是在列表中的OnDragListener每个上使用a View,它可以直接用于ListView

然后的想法是在进行拖放时找到手指在哪个项目之上,并ListAdapterListView. 然后诀窍是找到我们在哪个项目视图之上,以及在哪里完成放置。

为了做到这一点,我将id适配器创建的每个视图的ListView位置设置为 - with ,这样我以后可以使用和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;
         }
    }
}

现在,如果您在进行拖放时需要视觉反馈,有几种策略。例如,您可以在活动中拥有 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(当然在事件线程中,使用处理程序)。

于 2013-01-02T18:09:58.077 回答
0

问题是因为 listView.getPositionForView(view) 如果视图在调用时不可见,则返回 -1。因此,当您滚动列表时,依赖它会失败。因此,您可以在调用 startDrag() 的列表项上设置 listView.setOnItemLongClickListener() 而不是设置 view.setOnLongClickListener()。onItemLongClick() 为您提供可以在 startDrag() 的 myLocalState 参数中传递的位置。然后在 onDrag() 中使用 event.getLocalState() 恢复它并将其转换为 Integer。像这样...

    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();
    ...
}
于 2016-11-04T22:13:38.893 回答