Versione corta:Come rendere gli elementi della lista ottengono DragEvents dopo lo scorrimento
- Esiste un modo per fare una nuova visualizzazione creata ricevere
DragEvent
s di un'operazione di drag-and-drop già in esecuzione?
C'è How to register a DragEvent while already inside one and have it listen in the current DragEvent?, ma mi piacerebbe davvero una soluzione più pulita.
La soluzione GONE-> VISIBLE suggerita è abbastanza complessa da ottenere "correttamente", perché è necessario assicurarsi di usarla solo quando un elemento della lista diventa visibile e non incondizionatamente su tutte le voci della vista elenco corrente. In questo l'hack è leggermente perdente senza nemmeno più codice risolvibile per farlo bene.
Versione lunga:
Ho un ListView
. Gli elementi di ListView
sono View personalizzati che contengono simboli trascinabili (piccole caselle), ad es. simile a questo:
È possibile trascinare le piccole scatole tra le voci del ListView
, come l'ordinamento elementi in scatole. Il gestore di trascinamento sulle voci di elenco è più o meno banale:
@Override
public boolean onDragEvent(DragEvent event)
{
if ((event.getLocalState() instanceof DragableSymbolView)) {
final DragableSymbolView draggedView = (DragableSymbolView) event.getLocalState();
if (draggedView.getTag() instanceof SymbolData) {
final SymbolData symbol = (SymbolData) draggedView.getTag();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_ENTERED:
setSelected(true);
return true;
case DragEvent.ACTION_DRAG_ENDED:
case DragEvent.ACTION_DRAG_EXITED:
setSelected(false);
return true;
case DragEvent.ACTION_DROP:
setSelected(false);
// [...] remove symbol from soruce box and add to current box
requestFocus();
break;
}
}
}
return super.onDragEvent(event);
}
Il trascinamento inizia quando si tiene il puntatore su un simbolo e iniziando a trascinare (vale a dire lo spostamento al di là di una piccola soglia).
Ora, tuttavia, le dimensioni dello schermo potrebbero non essere sufficienti per contenere tutte le caselle e quindi lo ListView
deve scorrere. Ho scoperto il modo in cui devo implementare lo scorrimento da solo, dal momento che lo ListView
non scorre automaticamente durante il trascinamento.
In arriva ListViewScrollingDragListener
:
public class ListViewScrollingDragListener
implements View.OnDragListener {
private final ListView _listView;
public static final int DEFAULT_SCROLL_BUFFER_DIP = 96;
public static final int DEFAULT_SCROLL_DELTA_UP_DIP = 48;
public static final int DEFAULT_SCROLL_DELTA_DOWN_DIP = 48;
private int _scrollDeltaUp;
private int _scrollDeltaDown;
private boolean _doScroll = false;
private boolean _scrollActive = false;
private int _scrollDelta = 0;
private int _scrollDelay = 250;
private int _scrollInterval = 100;
private int _scrollBuffer;
private final Rect _visibleRect = new Rect();
private final Runnable _scrollHandler = new Runnable() {
@Override
public void run()
{
if (_doScroll && (_scrollDelta != 0) && _listView.canScrollVertically(_scrollDelta)) {
_scrollActive = true;
_listView.smoothScrollBy(_scrollDelta, _scrollInterval);
_listView.postDelayed(this, _scrollInterval);
} else {
_scrollActive = false;
}
}
};
public ListViewScrollingDragListener(final ListView listView, final boolean attach)
{
_scrollBuffer = UnitUtil.dipToPixels(listView, DEFAULT_SCROLL_BUFFER_DIP);
_scrollDeltaUp = -UnitUtil.dipToPixels(listView, DEFAULT_SCROLL_DELTA_UP_DIP);
_scrollDeltaDown = UnitUtil.dipToPixels(listView, DEFAULT_SCROLL_DELTA_DOWN_DIP);
_listView = listView;
if (attach) {
_listView.setOnDragListener(this);
}
}
public ListViewScrollingDragListener(final ListView listView)
{
this(listView, true);
}
protected void handleDragLocation(final float x, final float y)
{
_listView.getGlobalVisibleRect(_visibleRect);
if (_visibleRect.contains((int) x, (int) y)) {
if (y < _visibleRect.top + _scrollBuffer) {
_scrollDelta = _scrollDeltaUp;
_doScroll = true;
} else if (y > _visibleRect.bottom - _scrollBuffer) {
_scrollDelta = _scrollDeltaDown;
_doScroll = true;
} else {
_doScroll = false;
_scrollDelta = 0;
}
if ((_doScroll) && (!_scrollActive)) {
_scrollActive = true;
_listView.postDelayed(_scrollHandler, _scrollDelay);
}
}
}
public ListView getListView()
{
return _listView;
}
@Override
public boolean onDrag(View v, DragEvent event)
{
/* hide sequence controls during drag */
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_ENTERED:
_doScroll = true;
break;
case DragEvent.ACTION_DRAG_EXITED:
case DragEvent.ACTION_DRAG_ENDED:
case DragEvent.ACTION_DROP:
_doScroll = false;
break;
case DragEvent.ACTION_DRAG_LOCATION:
handleDragLocation(event.getX(), event.getY());
break;
}
return true;
}
}
Questo Scorre fondamentalmente la ListView
quando si trascina vicino al confine superiore o inferiore della sua area visibile. Non è perfetto, ma è abbastanza buono.
Tuttavia, c'è un problema:
Quando l'elenco scorre ad un elemento di prima invisibili, quell'elemento non riceve DragEvent
s. Non viene selezionato (evidenziato) quando si trascina un simbolo su di esso e non accetta gocce.
Qualsiasi idea su come rendere le viste "a scorrimento" riceve DragEvent
s dall'operazione di trascinamento della selezione già attiva?
Mi ha salvato un sacco di tempo, grazie! –