2013-01-02 17 views
6

Android'de, bir ListView kullanıyorum ve sürükle bırak kullanarak öğelerini yeniden sıralamak istiyorum. Bir "sürükle ve bırak listview" farklı uygulama olduğunu biliyorum, ancak API düzey 11 beri gelen Drag and Drop framework kullanmak istiyorum.Sürükle ve bırak, ListView ve öğe ACTION_DRAG_STARTED etkinliğini kaçırmayan görünümler

Sürükleyip bırakırken ListView sayfamı kaydırmak istedim kadar çok iyi başladı. Aşağıdaki örnekte yazıldığı gibi, şimdilik, hangi liste elemanının üstündeyim diye kontrol ediyorum, bu yüzden pozisyonu ListView.getLastVisiblePosition() ve ListView.getFirstVisiblePosition() arasında değilse, diğer liste maddelerini görüntülemek için ListView.smoothScrollToPosition() kullanın.

Bu ilk uygulama ancak oldukça iyi çalışıyor.

Kaydırma sırasında sorun ortaya çıkıyor: bazı öğeler sürükle ve bırak olaylarına - DragEvent.ACTION_DRAG_ENTERED ve diğerleri - yanıt vermem. ListView'in öğe görünümlerini yönetme biçiminden kaynaklanıyor: Artık görünmeyen öğe görünümlerini geri dönüştürmeye çalışıyor.

Sorun yok ve çalışıyor, ancak bazen ListAdapter'un getView() yeni bir nesneyi döndürüyor. Yeni olduğu için, bu nesne diğer DragEvent olaylarına cevap vermemesi için DragEvent.ACTION_DRAG_STARTED cevapsız!

İşte bir örnek. Bu durumda, bir liste öğesine uzun bir tıklama ile bir sürükle ve bırak işlemi başlatırsam ve sürüklediğimde, bunların çoğunda olduğumda öğelerin çoğunluğu yeşil bir arka plana sahip olur; ama bazıları değil.

Onları yapma hakkında herhangi bir fikir, DragEvent.ACTION_DRAG_STARTED yanıtsız olsalar bile sürükle ve bırak etkinlik mekanizmasına abone olur mu?

// Somewhere I have a ListView that use the MyViewAdapter 
// MyListView _myListView = ... 
// _myListView.setAdapter(new MyViewAdapter(getActivity(), ...)); 
_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; 
    } 
}); 

class MyViewAdapter extends ArrayAdapter<MyElement> { 

    public MyViewAdapter(Context context, List<TimedElement> objects) { 
     super(context, 0, objects); 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View myElementView = convertView; 
     if (myElementView == null) { 
      /* If the code is executed here while scrolling with a drag and drop, 
      * the new view is not associated to the current drag and drop events */ 
      Log.d("app", "Object created!"); 
      // Create view 
      // myElementView = ... 
      // Prepare drag and drop 
      myElementView.setOnDragListener(new MyElementDragListener()); 
     } 
     // Associates view and position in ListAdapter, needed for drag and drop 
     myElementView.setTag(R.id.item_position, position); 
     // Continue to prepare view 
     // ... 
     return timedElementView; 
    } 

    private class MyElementDragListener 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_ENTERED: 
       v.setBackgroundColor(Color.GREEN); 
       v.invalidate(); 
       return true; 
      case DragEvent.ACTION_DRAG_LOCATION: 
       int targetPosition = (Integer)v.getTag(R.id.item_position); 
       if (event.getY() < v.getHeight()/2) { 
        Log.i("app", "top "+targetPosition);   
       } 
       else { 
        Log.i("app", "bottom "+targetPosition); 
       } 
       // To scroll in ListView while doing drag and drop 
       if (targetPosition > _myListView.getLastVisiblePosition()-2) { 
        _myListView.smoothScrollToPosition(targetPosition+2); 
       } 
       else if (targetPosition < _myListView.getFirstVisiblePosition()+2) { 
        _myListView.smoothScrollToPosition(targetPosition-2); 
       } 
       return true; 
      case DragEvent.ACTION_DRAG_EXITED: 
       v.setBackgroundColor(Color.BLUE); 
       v.invalidate(); 
       return true; 
      case DragEvent.ACTION_DROP: 
      case DragEvent.ACTION_DRAG_ENDED: 
      default: 
       break; 
      } 
      return false; 
     }  
    } 
} 

cevap

7

Bu geri dönüşüm sorunu çözülmüş değildi, ama yine de Sürükle & Bırak çerçevesini kullanarak olası bir geçici çözüm bulundu. Fikir, perspektif değişimidir: Listede her bir View üzerinde bir OnDragListener kullanmak yerine, doğrudan ListView üzerinde kullanılabilir.

Ardından fikir Sürükle & Bırak yaparken parmak olan madde üstünde bulmak için ve ListView arasında ListAdapter ilgili ekran kod yazmaktır. Hile daha sonra hangi öğe görünümünün üstünde olduğumuzu ve düşmenin yapıldığı yeri bulmaktır.

Bunu yapabilmek için, ben adaptörü onun ListView pozisyonu yarattığı her görüşe bir id olarak ayarlanmış - View.setId() ile, bu yüzden daha sonra ListView.pointToPosition() ve ListView.findViewById() bir arada kullanarak bulabilirsiniz. Ben bu deneyin vermedi ama kim olduğunu belki bir gün yapacağım

// 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; 
     } 
    } 
} 
+0

: sürükle dinleyici örnek olarak

(ki, ben ListView uygulanan, hatırlatılır), onun gibi bir şey olabilir. Bu gerçekten iyi bir iş gibi görünüyor ve oldukça teknik! Benden al. –