2013-01-02 34 views
6

एंड्रॉइड पर याद करते हैं, मैं ListView का उपयोग करता हूं और मैं ड्रैग और ड्रॉप का उपयोग करके अपने आइटम को पुन: व्यवस्थित करने में सक्षम होना चाहता हूं। मुझे पता है कि "ड्रैग और ड्रॉप सूचीदृश्य" के विभिन्न कार्यान्वयन हैं, हालांकि मैं एपीआई स्तर 11 के बाद से आना चाहता हूं।खींचें और छोड़ें, ListView और आइटम दृश्य जो ACTION_DRAG_STARTED ईवेंट

यह बहुत अच्छा शुरू हुआ जब तक कि मैं ड्रैग और ड्रॉप करते समय अपना ListView स्क्रॉल करना चाहता था। जैसा कि यह नीचे दिए गए उदाहरण में लिखा गया है, अभी के लिए, मैं किस सूची तत्व के शीर्ष पर हूं, इसलिए यदि इसकी स्थिति ListView.getLastVisiblePosition() और ListView.getFirstVisiblePosition() के बीच नहीं है, तो मैं अन्य सूची आइटम देखने के लिए ListView.smoothScrollToPosition() का उपयोग करता हूं।

यह पहला कार्यान्वयन है लेकिन यह काफी अच्छा काम करता है।

स्क्रॉल करते समय समस्या उत्पन्न होती है: कुछ तत्व ड्रैग और ड्रॉप ईवेंट का जवाब नहीं देते - DragEvent.ACTION_DRAG_ENTERED और अन्य - जब मैं उनके शीर्ष पर हूं। यह सूची दृश्य अपने आइटम दृश्यों के प्रबंधन के तरीके के कारण है: यह उन आइटम दृश्यों को रीसायकल करने का प्रयास करता है जो अब दिखाई नहीं दे रहे हैं।

यह ठीक है और यह काम करता है, लेकिन कभी-कभी 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 पर किया जा सकता है।

फिर विचार शीर्ष जो आइटम की उंगली जबकि खींचें & ड्रॉप कर रही है पर लगता है, और ListView की ListAdapter में संबंधित प्रदर्शन कोड लिखने के लिए है। यह चाल तब है जब हम किस आइटम दृश्य के शीर्ष पर हैं, और जहां ड्रॉप किया जाता है।

ऐसा करने में, मैं अनुकूलक अपने ListView स्थिति के द्वारा बनाई गई प्रत्येक दृश्य के लिए एक id के रूप में सेट - View.setId() साथ है, तो मैं इसे बाद में ListView.pointToPosition() और ListView.findViewById() के संयोजन का उपयोग पा सकते हैं।

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

मैं यह कोशिश नहीं की, बल्कि जो जानते हैं कि शायद किसी दिन मैं लूंगा:

एक खींचें श्रोता उदाहरण के रूप में (जो, मैं आपको याद दिलाना, ListView पर लागू), यह ऐसा ही कुछ हो सकता है। यह एक असली अच्छा काम लगता है, और काफी तकनीकी! मुझसे ऊपर हटो –