10

Hi I am trying to do a drag and drop on a ListView. It works great. But I lost the ability to click and scroll. How to I retain those two? I was thinkng the drag and drop should happen on long press of the item instead of a normal press. Here is my onTouch that is located in the MyListView that extends ListView

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        final int x = (int) ev.getX();
        final int y = (int) ev.getY();  

        if (action == MotionEvent.ACTION_DOWN && x < this.getWidth()/4) {
            this._dragMode = true;
        }

        if (!this._isDragNDrop) 
            return super.onTouchEvent(ev);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                this._startPosition = pointToPosition(x,y);
                if (this._startPosition != INVALID_POSITION) {
                    int mItemPosition = this._startPosition - getFirstVisiblePosition();
                    this._dragPointOffset = y - getChildAt(mItemPosition).getTop();
                    this._dragPointOffset -= ((int)ev.getRawY()) - y;
                    startDrag(mItemPosition,y);
                    drag(x,y);
                }   
                break;
            case MotionEvent.ACTION_MOVE:
                drag(x,y);
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
            default:
                this._dragMode = false;
                //_isDragNDrop = false;
                this._endPosition = pointToPosition(x,y);
                stopDrag(this._startPosition - getFirstVisiblePosition());
                if (this._dropListener != null && this._startPosition != INVALID_POSITION && this._endPosition != INVALID_POSITION) 
                    this._dropListener.onDrop(this._startPosition, this._endPosition);
                break;
        }
        return true;
    }
dropsOfJupiter
  • 6,763
  • 12
  • 47
  • 59

6 Answers6

5

I just implemented drag and drop list view in my application, the dragging starts by long press on an item.

Source code is on GitHub: DraggableListView, I have a blog post to describe it. Hope it helps you.

aleung
  • 9,848
  • 3
  • 55
  • 69
  • wow, thank you so much! We decided to postpone this feature because of the fact that we were not able to get this working. I'll look at this and see if this solves my problem. I saw your blog, its true it would take over a week as when I rolled into two days with I had to drop it :) – dropsOfJupiter Apr 18 '11 at 01:51
  • great component! Works fine for me... THX – dudeldidadum Nov 16 '11 at 15:20
  • The blog post link above doesn't work but you can find the rest of what's needed here: http://codegarage.no-ip.co.uk/snippets/94 – mpemburn Feb 17 '14 at 18:58
  • I've updated the link in the answer. Thanks @mpemburn – aleung Feb 19 '14 at 08:31
3

I'm implementing something similiar. Hopefully this will help someone else as I'm still ironing out the kinks.

...
thumb.setOnDragListener(new Drag());
...

public class Drag implements OnDragListener{
public static int PositionOfDraggedItem = -1;
      public int PositionOfItemDraggedOver = -1;

public Drag() {}

@Override
public boolean onDrag(View v, DragEvent event)
{
    final int action = event.getAction();
    boolean state = true;

    View root = v.getRootView();
    ListView List = (ListView) root.findViewById(android.R.id.list);
    Object holder = v.getTag() instanceof OrderHolder? (OrderHolder)v.getTag() : v.getTag();

    String BookId = holder instanceof OrderHolder? String.valueOf(((OrderHolder)holder).id) : (String)holder;

    switch(action)
    {
        case DragEvent.ACTION_DRAG_STARTED:

            break;
        case DragEvent.ACTION_DRAG_ENTERED:
            if( PositionOfDraggedItem != -1 )
            {
                if( this.PositionOfItemDraggedOver != PositionOfDraggedItem )
                {
                    if( v.getBackground() != null )
                        v.setBackgroundDrawable(v.getContext().getResources().getDrawable(drawable.row_ovr));
                }
            }
            break;
        case DragEvent.ACTION_DRAG_LOCATION:
            /**
             * Set the item being
             * here, as it will not
             * provide location data
             * anywhere else
             */
            if( PositionOfDraggedItem == -1 )
                PositionOfDraggedItem = List.getPositionForView(v);

            /**
             * Set the view thats
             * being dragged over
             */
            this.PositionOfItemDraggedOver = List.getPositionForView(v);

            int PositionOfLastVisibleView = List.getLastVisiblePosition();
            int PositionOfFirstVisibleView = List.getFirstVisiblePosition();

            if( this.PositionOfItemDraggedOver != -1 && ( this.PositionOfItemDraggedOver != PositionOfDraggedItem ) ){
                if( this.PositionOfItemDraggedOver == PositionOfLastVisibleView-1 || this.PositionOfItemDraggedOver == PositionOfLastVisibleView || this.PositionOfItemDraggedOver == PositionOfLastVisibleView+1 ){
                    List.smoothScrollToPosition(PositionOfLastVisibleView+1);
                }else if( this.PositionOfItemDraggedOver == PositionOfFirstVisibleView-1 || this.PositionOfItemDraggedOver == PositionOfFirstVisibleView+1 || this.PositionOfItemDraggedOver == PositionOfFirstVisibleView ){
                    List.smoothScrollToPosition(PositionOfFirstVisibleView-1);
                }
            }
            break;
        case DragEvent.ACTION_DRAG_EXITED:
            if( v.getBackground() != null )
                v.setBackgroundDrawable(v.getContext().getResources().getDrawable(drawable.row));
            break;
        case DragEvent.ACTION_DROP:
            if(event != null)
            {
                ClipData data = event.getClipData();
                if( data != null && data.getItemCount() > 0 )
                {
                    if( !data.getItemAt(0).getText().toString().equalsIgnoreCase(BookId) )
                    {
                        if( v.getBackground() != null )
                            v.setBackgroundDrawable(v.getContext().getResources().getDrawable(drawable.row));
                        /**
                         * Ordering info for
                         * book being dragged
                         */
                        String oShelfOrder = BookManager.item(v.getContext().getContentResolver(), dbhelper.BOOKS_SHELFORDER, 
                                booksprovider.GetUri(dbhelper.BOOKS), Long.parseLong(data.getItemAt(0).getText().toString()));
                        String oBookOrder = BookManager.item(v.getContext().getContentResolver(), dbhelper.BOOKS_BOOKSORDER, 
                                booksprovider.GetUri(dbhelper.BOOKS), Long.parseLong(data.getItemAt(0).getText().toString()));

                        /**
                         * Ordering info
                         * for book being dragged
                         * to
                         */
                        String dShelfOrder = BookManager.item(v.getContext().getContentResolver(), dbhelper.BOOKS_SHELFORDER, 
                                booksprovider.GetUri(dbhelper.BOOKS), Long.parseLong(BookId));
                        String dBookOrder = BookManager.item(v.getContext().getContentResolver(), dbhelper.BOOKS_BOOKSORDER, 
                                booksprovider.GetUri(dbhelper.BOOKS), Long.parseLong(BookId));

                        /**
                         * Update book being dragged
                         */
                        ContentValues args = new ContentValues();
                        args.put(dbhelper.BOOKS_SHELFORDER, dShelfOrder);
                        args.put(dbhelper.BOOKS_BOOKSORDER, dBookOrder);
                        BookManager.updateBook(v.getContext().getContentResolver(), data.getItemAt(0).getText().toString(), args);

                        /**
                         * Update book being dragged to
                         */
                        ContentValues values = new ContentValues();
                        values.put(dbhelper.BOOKS_SHELFORDER, oShelfOrder);
                        values.put(dbhelper.BOOKS_BOOKSORDER, oBookOrder);
                        BookManager.updateBook(v.getContext().getContentResolver(), BookId, values);

                        if( v.getContext() instanceof OrderBooks ){
                            ((OrderBooks)v.getContext()).adapter.refresh();
                            ((OrderBooks)v.getContext()).adapter.notifyDataSetChanged();

                            PositionOfDraggedItem = -1;
                        }
                    }
                }
            }
            break;
        case DragEvent.ACTION_DRAG_ENDED:
            if( event != null && event.getResult())
            {
                v.invalidate();
            }
            break;
    }
    return state;
}

}

Haven't worked on it in awhile, updated to reflect latest changes.

Rickey
  • 7,830
  • 1
  • 19
  • 12
  • smoothScrollToPosition will start a scroller anim every time, and I think the best is to post a smoothScrollToPosition(amount, linear) every 1 second, but this method is not public or protected... – molikto Nov 11 '12 at 03:37
  • This solution scrolls perfectly, however, DragListeners of Views in the list that were not visible before scrolling don't react while dragging. When I drag an object to the lower area of the listview, it scrolls down, but the DragListener on views that get visible by scrolling don't get any events. They work as usual when I can reach them without scrolling. Did any of you come up with a solution for this? – FD_ Apr 13 '13 at 17:58
2

Check out this question for additional help. I also have a component called DragSortListView that I've spent a long time refining. You can find it here:

I could help you integrate long-press initiated dragging in a fork of my above repo.

Community
  • 1
  • 1
heycosmo
  • 1,388
  • 11
  • 11
1

I implemented this solution which works for me. The following method is in my Activity and it is called in my dragEventListener when the dragEvent is ACTION_DRAG_ENTERED. Some attributes I declared:

//first visible item (getFirstVisiblePosition() does not work for me)
int firstVisible;
//number of scrolls
int nbrScroll;
//my listview to scroll
ListView mListView;
//the adapter of my listview
SimpleAdapter mAdapter;

...

public void scrollDrag(int position) {
            //this is the position of the item in my listview
            //to scroll down
            //test if the item entered is the last visible
    if (position == mListView.getLastVisiblePosition()
            && id < mListView.getCount()) {
        mAdapter.notifyDataSetChanged();
        mRightDrawerList.post(new Runnable() {
            @Override
            public void run() {
                //scroll down
                mListView.smoothScrollToPosition(mListView
                        .getLastVisiblePosition() + 1);
                firstVisible++;
            }
        });
            //to scroll up
            //test if the item entered is the first visible
    } else if (position == firstVisible && position !=0) {
        mAdapter.notifyDataSetChanged();
        mListView.post(new Runnable() {
            @Override
            public void run() {
                //scroll up
                mListView.smoothScrollToPosition(firstVisible-1);
                firstVisible --;
            }
        });
    }
}

Hope it will help someone.

FaGuet
  • 11
  • 2
0

In Kotlin:

holder.mView.setOnDragListener { v, event ->
    when (event.action) {
        DragEvent.ACTION_DRAG_ENTERED -> {
            ...
        }
        DragEvent.ACTION_DRAG_EXITED, DragEvent.ACTION_DRAG_ENDED -> {
            ...
            val y = calculateY(holder.mView)
            val minY = calculateY(scroll_view) + 200

            val scrollBounds = Rect() 
            scroll_view.getHitRect(scrollBounds) //TODO: calculate only once

            val maxY = minY + scrollBounds.height() - 400
            if (y < minY) {
                scroll_view?.post {
                    scroll_view.smoothScrollBy(0, -200)
                }
            } else if (y > maxY) {
                scroll_view?.post {
                    scroll_view.smoothScrollBy(0, +200)
                }
            }
        }
    }
}
ElOjcar
  • 301
  • 2
  • 4
  • 12
-1

He guys I have made little bit change in rickey's code and it's work for me.

if (PositionOfItemDraggedOver == PositionOfLastVisibleView)
    order_item_list_lv.smoothScrollToPosition(PositionOfLastVisibleView + 1);

if (PositionOfItemDraggedOver == PositionOfFirstVisibleView)
   order_item_list_lv.smoothScrollToPosition(PositionOfFirstVisibleView - 1);
Phani
  • 5,319
  • 6
  • 35
  • 43