Подтвердить что ты не робот

Перетаскивание в "ListView"

Я пытаюсь реализовать перетаскивание в ListView в android (Ice Cream Sandwich). Поэтому, когда перетаскиваемый объект достигает края ListView, я прокручиваю ListView в соответствующем направлении. Проблема в том, что при прокрутке иногда адаптер создает новый View по мере необходимости, и эти "новые" View не получили событие ACTION_DRAG_STARTED раньше и, следовательно, не получают обновления DragEvent. Есть ли способ отправить события на эти виды?

4b9b3361

Ответ 1

Самый простой способ реализовать перетаскивание в listview - использовать эту большую библиотеку. 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) {
}

Ответ 3

У меня есть та же проблема. Я не решил эту проблему утилизации, но я нашел возможное временное решение, использующее фреймворк Drag and Drop. Идея заключается в изменении перспективы: вместо использования OnDragListener для каждого View в списке его можно использовать непосредственно в ListView.

Тогда идея состоит в том, чтобы найти поверх этого элемента палец при выполнении перетаскивания и записать соответствующий код отображения в ListAdapter ListView. Трюк заключается в том, чтобы найти поверх того, что мы видим, и где это делается.

Для этого я устанавливаю как 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;
         }
    }
}

Теперь, если вам нужна визуальная обратная связь при выполнении перетаскивания, существует несколько стратегий. Вы можете, например, иметь две переменные экземпляра в вашей деятельности с именем:

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().

Ответ 4

Проблема заключается в том, что listView.getPositionForView(view) возвращает -1, если вид не отображается, когда он вызывается. Поэтому, когда вы прокручиваете список, это будет терпеть неудачу. Таким образом, вместо установки view.setOnLongClickListener() вы можете установить listView.setOnItemLongClickListener() в элементе списка, который вызывает startDrag() для элемента. onItemLongClick() предоставляет вам позицию, которую вы можете передать в параметре myLocalState для startDrag(). Затем вы восстанавливаете это в 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();
    ...
}