ListView
Android(Ice Cream Sandwich)でドラッグアンドドロップを実装しようとしています。したがって、ドラッグされたオブジェクトが の端に到達すると、関連する方向にListView
をスクロールします。ListView
問題は、スクロールすると、アダプターがView
必要に応じて新しい を作成することがあり、これらの「新しい」View
はACTION_DRAG_STARTED
以前にイベントを受信しなかったため、DragEvent
更新を受信しないことです。これらのビューにもイベントを送信する方法はありますか?
4 に答える
リストビューでドラッグ アンド ドロップを実装する最も簡単な方法は、この優れたライブラリを使用することです。 https://github.com/commonsguy/cwac-touchlist 試してみる価値があります。
問題は、呼び出されたときにビューが表示されていない場合、 listView.getPositionForView(view) が -1 を返すためです。そのため、リストをスクロールすると失敗します。そのため、view.setOnLongClickListener() を設定する代わりに、項目で startDrag() を呼び出す listView.setOnItemLongClickListener() をリスト項目に設定できます。onItemLongClick() は、startDrag() の myLocalState パラメータに渡すことができる位置を示します。次に、event.getLocalState() を使用して onDrag() でそれを回復し、整数にキャストします。このような...
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();
...
}
私は同じ問題を抱えています。このリサイクルの問題は解決しませんでしたが、ドラッグ アンド ドロップ フレームワークを使用することで解決できる可能性があることを発見しました。アイデアは視点を変えることです:リスト内のOnDragListener
各に a を使用する代わりに、直接に使用できます。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;
}
}
}
ドラッグ アンド ドロップを行うときに視覚的なフィードバックが必要な場合は、いくつかの方法があります。たとえば、次の名前のアクティビティに 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
。もちろん、UI を更新することを忘れないでください (もちろん、イベント スレッドでは、Handler を使用します) _myListView.getAdapter()).notifyDataSetChanged()
または多分_myListView.invalidate()
メソッドのようなものを使用します。
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) {
}