2013-01-02 27 views
6

在Android上,我使用了ListView,我希望能夠使用拖放操作重新排序項目。我知道有一個「拖放列表視圖」的不同實現,但是我想使用自API級別11以來的Drag and Drop framework拖放,ListView和缺失ACTION_DRAG_STARTED事件的項目視圖

它開始很好,直到我想滾動我的ListView而做拖放。正如它在下面的例子中寫的,現在,我檢查我是哪個列表元素的頂部,所以如果它的位置不在ListView.getLastVisiblePosition()ListView.getFirstVisiblePosition()之間,我使用ListView.smoothScrollToPosition()來查看其他列表項目。

這是第一次實施,但它工作得很好。

滾動時出現問題:某些元素無法應答拖放事件 - DragEvent.ACTION_DRAG_ENTERED和其他元素 - 當我處於頂部時。這是由於ListView管理其項目視圖的方式:它試圖回收不再可見的項目視圖。

它是好的,它的工作原理,但有時ListAdaptergetView()返回一個新的對象。由於它是新的,此對象錯過了DragEvent.ACTION_DRAG_STARTED,所以它不會回答其他DragEvent事件!

這裏是一個例子。在這種情況下,如果我通過長按一個列表項目來拖動和拖放,並且如果拖動它,大多數項目都將具有綠色背景,前提是我處於頂部;但有些不。

任何關於使他們訂閱拖放事件機制的想法,即使他們錯過了DragEvent.ACTION_DRAG_STARTED

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

回答

7

我並沒有解決了這個問題,回收,但我發現了一個可能的解決方法仍然使用拖放&降框架。我們的想法是改變觀點:不是在列表中的每個View上使用OnDragListener,而是直接在ListView上使用它。

然後這個想法是在拖動&拖放的同時在手指所在的頂部找到哪個項目,並在ListViewListAdapter中寫入相關的顯示代碼。然後訣竅就是在哪個項目視圖的頂部找到我們,以及放置位置。

爲了做到這一點,我設置爲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; 
     } 
    } 
} 
+0

我沒有嘗試這一點,但誰知道也許有一天我會的。這似乎是一個很好的工作,而且技術性很強! Upvote從我。 –