Android:Drag n Drop List View

In: Android|Mobile App Development

3 Apr 2011

While writing this post, a question was in my mind that how can I create a list where I can rearrange list items with dragging list rows to another row and so on?

I have a list of records in a listview that I want the user to be able to re-sort using a drag and drop method. I have seen this implemented in other apps, In iPhone apps too, It might be possible that others need this drag n drop feature as well.

From long time i have been trying  to make drag and drop  feature run on simple  list view. For this, i searched over the internet and  other resources and found that  “TouchInterceptor.java”  file from music  app in android is a very useful to perform the dragging and dropping items within list view. i have  used  most of the implementation from here  and  developed my own  DndListView  feature on list view.

Here are the important files those are used in sample Drag n Drop feature developed for Android.

DnDListView.java

public class DndListView extends ListView {
 private Context mContext;
 private ImageView mDragView;
 private WindowManager mWindowManager;
 private WindowManager.LayoutParams mWindowParams;
 private int mDragPos;
 private int mFirstDragPos;
 private int mDragPoint;
 private int mCoordOffset;
 private DragListener mDragListener;
 private DropListener mDropListener;
 private int mUpperBound;
 private int mLowerBound;
 private int mHeight;
 private Rect mTempRect = new Rect();
 private Bitmap mDragBitmap;
 private final int mTouchSlop;
 private int mItemHeightNormal;
 private int mItemHeightExpanded;
 private int dndViewId;
 private int dragImageX = 0;
 private int totalchilds = 0;
 
 public DndListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  mContext = context;
  mTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();// etScaledTouchSlop();
  totalchilds = getChildCount();
 }
 
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
//  Logger.debugE(“DndListView Intercept :” + ev.getAction());
 
  if (getChildCount() > 0)
   totalchilds = getChildCount();
 
  if (mDragListener != null || mDropListener != null) {
   switch (ev.getAction()) {
   case MotionEvent.ACTION_DOWN:
    int x = (int) ev.getX();
    int y = (int) ev.getY();
 
    if (x < this.getWidth()50) {
     return false;
    }
 
    int itemnum = pointToPosition(x, y);
    System.out.println(“Item num :+ itemnum);
    if (itemnum == AdapterView.INVALID_POSITION) {
     break;
    }
    View item = (View) getChildAt(itemnum
      - getFirstVisiblePosition());
    // item.setBackgroundColor(Color.RED);
    mItemHeightNormal = item.getHeight();
    mItemHeightExpanded = mItemHeightNormal * 2;
    mDragPoint = y – item.getTop();
    mCoordOffset = ((int) ev.getRawY()) – y;
    View dragger = item.findViewById(dndViewId);
    if (dragger == null)
     dragger = item;
    Rect r = mTempRect;
    dragger.getDrawingRect(r);
    if (x < r.right * 2) {
     item.setDrawingCacheEnabled(true);
 
     Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
     startDragging(bitmap, y);
     mDragPos = itemnum;
     mFirstDragPos = mDragPos;
     mHeight = getHeight();
 
     int touchSlop = mTouchSlop;
     mUpperBound = Math.min(y – touchSlop, mHeight / 3);
     mLowerBound = Math.max(y + touchSlop, mHeight * 2 / 3);
     return false;
    }
    mDragView = null;
    break;
   }
  }
  return super.onInterceptTouchEvent(ev);
 }
 
 public void redraw(int pos) {
  for (int i = 0; i < getChildCount(); i++) {    View v = getChildAt(i);    if (i == pos)     v.setBackgroundColor(Color.parseColor(“#E0E0E0″));    else     v.setBackgroundColor(Color.WHITE);   }  }  public void refresh() {   long time = Long.parseLong((“” + System.nanoTime()).substring(0, 7));   MotionEvent me = MotionEvent.obtain(time, time,     MotionEvent.ACTION_DOWN, getWidth()10, 300, 0);   onInterceptTouchEvent(me);   stopDragging();  }  @Override  public boolean onTouchEvent(MotionEvent ev) {   if (getChildCount() > 0)
   totalchilds = getChildCount();
 
  if ((mDragListener != null || mDropListener != null)
    && mDragView != null) {
   int action = ev.getAction();
   switch (action) {
   case MotionEvent.ACTION_UP:
   case MotionEvent.ACTION_CANCEL:
    Rect r = mTempRect;
    mDragView.getDrawingRect(r);
    stopDragging();
    int y = (int) ev.getY();
    int speed = 0;
    if (mDropListener != null && mDragPos >= 0
      && mDragPos < getCount()) {
     mDropListener.drop(mFirstDragPos, mDragPos);
 
     if (mDragPos < (totalchilds-1) )
      setSelectionFromTop(0, 0);
    }
    unExpandViews(false);
    break;
 
   case MotionEvent.ACTION_DOWN:
   case MotionEvent.ACTION_MOVE:
    int x = (int) ev.getX();
    y = (int) ev.getY();
 
    if (x < this.getWidth()50) {      return false;     }     dragView(x, y);     int itemnum = getItemForPosition(y);     if (itemnum >= 0) {
     if (action == MotionEvent.ACTION_DOWN
       || itemnum != mDragPos) {
      if (mDragListener != null) {
       mDragListener.drag(mDragPos, itemnum);
      }
      mDragPos = itemnum;
      doExpansion();
     }
     speed = 0;
     adjustScrollBounds(y);
     if (y > mLowerBound) {
 
      speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
     } else if (y < mUpperBound) {
 
      speed = y < mUpperBound / 2 ? -16 : -4;      }      if (speed != 0) {       int ref = pointToPosition(0, mHeight / 2);       if (ref == AdapterView.INVALID_POSITION) {        // we hit a divider or an invisible view, check        // somewhere else        ref = pointToPosition(0, mHeight / 2          + getDividerHeight() + 64);       }       View v = getChildAt(ref – getFirstVisiblePosition());       if (v != null) {        int pos = v.getTop();        setSelectionFromTop(ref, pos – speed);       }      }     }     break;    }    return true;   }   return super.onTouchEvent(ev);  }  private int getItemForPosition(int y) {   int adjustedy = y – mDragPoint – 32;   int pos = myPointToPosition(0, adjustedy);   if (pos >= 0) {
   if (pos <= mFirstDragPos) {
    pos += 1;
   }
  } else if (adjustedy < 0) {    pos = 0;   }   return pos;  }  private int myPointToPosition(int x, int y) {   Rect frame = mTempRect;   final int count = getChildCount();   for (int i = count – 1; i >= 0; i–) {
   final View child = getChildAt(i);
   child.getHitRect(frame);
   if (frame.contains(x, y)) {
    return getFirstVisiblePosition() + i;
   }
  }
  return INVALID_POSITION;
 }
 
 private void adjustScrollBounds(int y) {
  if (y >= mHeight / 3) {
   mUpperBound = mHeight / 3;
  }
  if (y <= mHeight * 2 / 3) {    mLowerBound = mHeight * 2 / 3;   }  }  private void doExpansion() {   int childnum = mDragPos – getFirstVisiblePosition();   if (mDragPos > mFirstDragPos) {
   childnum++;
  }
 
  View first = getChildAt(mFirstDragPos – getFirstVisiblePosition());
  for (int i = 0;; i++) {
   View vv = getChildAt(i);
   if (vv == null) {
    break;
   }
   int height = mItemHeightNormal;
   int visibility = View.VISIBLE;
   if (vv.equals(first)) {
 
    if (mDragPos == mFirstDragPos) {
     visibility = View.INVISIBLE;
    } else {
     height = 1;
    }
   } else if (i == childnum) {
    System.out.print(“I :+ i);
    if (mDragPos < getCount()1) {
     height = mItemHeightExpanded;
    }
   }
   ViewGroup.LayoutParams params = vv.getLayoutParams();
   params.height = height;
   vv.setLayoutParams(params);
   vv.setVisibility(visibility);
  }
 }
 
 private void unExpandViews(boolean deletion) {
  for (int i = 0;; i++) {
   View v = getChildAt(i);
   if (v == null) {
    if (deletion) {
     int position = getFirstVisiblePosition();
     int y = getChildAt(0).getTop();
     setAdapter(getAdapter());
     setSelectionFromTop(position, y);
    }
    layoutChildren();
    v = getChildAt(i);
    if (v == null) {
     break;
    }
   }
   ViewGroup.LayoutParams params = v.getLayoutParams();
   params.height = mItemHeightNormal;
   v.setLayoutParams(params);
   v.setVisibility(View.VISIBLE);
  }
 }
 
 public void checkfordrop(int dragPos) {
  if (dragPos < totalchilds – 1)
   setSelectionFromTop(0, 0);
 }
 
 private void startDragging(Bitmap bm, int y) {
  stopDragging();
 
  mWindowParams = new WindowManager.LayoutParams();
  mWindowParams.gravity = Gravity.TOP;
  mWindowParams.x = dragImageX;
  mWindowParams.y = y – mDragPoint + mCoordOffset;
 
  mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
  mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
  mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
  mWindowParams.format = PixelFormat.TRANSLUCENT;
  mWindowParams.windowAnimations = 0;
 
  ImageView v = new ImageView(mContext);
  int backGroundColor = Color.parseColor(“#e0103010″);
  v.setBackgroundColor(backGroundColor);
  v.setImageBitmap(bm);
  mDragBitmap = bm;
 
  mWindowManager = (WindowManager) mContext.getSystemService(“window”);
  mWindowManager.addView(v, mWindowParams);
  mDragView = v;
 }
 
 private void dragView(int x, int y) {
  mWindowParams.y = y – mDragPoint + mCoordOffset;
  mWindowManager.updateViewLayout(mDragView, mWindowParams);
 }
 
 private void stopDragging() {
  if (mDragView != null) {
   WindowManager wm = (WindowManager) mContext
     .getSystemService(“window”);
   wm.removeView(mDragView);
   mDragView.setImageDrawable(null);
   mDragView = null;
  }
  if (mDragBitmap != null) {
   mDragBitmap.recycle();
   mDragBitmap = null;
  }
 }
 
 public void setDragListener(DragListener l) {
  mDragListener = l;
 }
 
 public void setDropListener(DropListener l) {
  mDropListener = l;
 }
 
 public void setDndView(int id) {
  dndViewId = id;
 }
 
 public void setDragImageX(int x) {
  dragImageX = x;
 }
 
 public interface DragListener {
  void drag(int from, int to);
 }
 
 public interface DropListener {
  void drop(int from, int to);
 }
}

ListDnD.java

 
public class ListDnD extends ListActivity implements DndListView.DragListener, DndListView.DropListener {
    /** Called when the activity is first created. */
 private DndListView lvList;
 private EfficientAdapter mAdapter;
 private boolean isDnd = false;
 private ArrayList list = new ArrayList();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        lvList = (DndListView) this.getListView();
 
        list.add(“http://www.wopll.com/“);
        list.add(“http://www.wopll.com/?p=368“);
        list.add(“http://www.wopll.com/?page_id=2“);
        list.add(“http://www.wopll.com/?feed=rss2“);
        list.add(“http://www.wopll.com/?cat=20“);
 
        mAdapter = new EfficientAdapter(this);
        setListAdapter(mAdapter);
 
        lvList.setDragListener(this);
  lvList.setDropListener(this);
    }
 
 private class EfficientAdapter extends BaseAdapter {
  private LayoutInflater mInflater;
 
 public EfficientAdapter(Context context) {
  mInflater = LayoutInflater.from(context);
 }
 public int getCount() {
  return list.size();
 }
 
 public Object getItem(int pos) {
  return pos;
 }
 
 public long getItemId(int pos) {
  return pos;
 }
 
 public View getView(final int pos, View convertView, ViewGroup parent) {
 
  ViewHolder holder;
 
  if (convertView == null) {
   convertView = mInflater.inflate(R.layout.simple_list_item_1, null);
   convertView.setBackgroundColor(Color.WHITE);
   holder = new ViewHolder();
   holder.item = (TextView) convertView.findViewById(android.R.id.text1);
   convertView.setTag(holder);
  } else {
   holder = (ViewHolder) convertView.getTag();
  }
  holder.item.setText(list.get(pos));
  return convertView;
 }
 
 class ViewHolder {
  TextView item;
 }
}
 
 @Override
 public void drag(int from, int to) {
  // TODO Auto-generated method stub
  if(!isDnd){
   isDnd = true;
   Log.i(“Drag and Drop : drag”, “from :+ from + “, to :+ to);
  }
 }
 @Override
 public void drop(int from, int to) {
  // TODO Auto-generated method stub
  if(isDnd){
   Log.i(“Drag and Drop : drop”, “from :+ from + “, to :+ to);
   if(from == to)
    return;
 
   String item = list.remove(from);
   list.add(to, item);
   isDnd = false;
 
   mAdapter.notifyDataSetChanged();
 
   lvList.checkfordrop(to);
  }
 }
}

Source code for this demo applicaion is available here.

10 Responses to Android:Drag n Drop List View

Avatar

Jitesh Rana

July 7th, 2011 at 10:32 am

nice article. :)

Avatar

Filipe

July 7th, 2011 at 5:26 pm

Hi, the link to the source code is broken. Could you send it to my e-mail?
Also, does this code work on android 2.2.
Thanks very much!

Avatar

wol

July 8th, 2011 at 10:30 am

Hi Filipe,
Link to the source code is corrected. you can download the source code from here
It should be working on android 2.2 also
Thanks for your interest!
keep visit.

Avatar

Welcome

July 30th, 2011 at 7:38 am

That’s the best aswner of all time! JMHO

Avatar

Ram

September 5th, 2011 at 10:09 am

Nice Article but i faced some issues, when i executed the app i noticed that Drag and Drop events are not getting fired. I haven’t modified any code.Can u explain what is the problem?? and i am executing the app in 2.2.

Avatar

websizfz

September 5th, 2011 at 3:36 pm

Hi Balram,
Drag n Drop events fired from the right side, it works in 2.2 also, may be you are dragging it from the middle of the screen that is why you feed it does not works.
I have to do it like that, because if drag drop functionality in all listview area and i want to fire an event on single tap it does not works.
anyway keep visiting.

Avatar

Yoav

September 15th, 2011 at 7:47 am

your code is the only one i found that the scrolling while dragging works well.. thank you for sharing!

Avatar

gropapa

February 1st, 2012 at 2:50 pm

awesome source, i m a beginner in android and your code seems easy to understand. I’ll try to figure out why the drawing does’nt look exactly the same as the list item but still…. AWESOME

Avatar

Girish

May 14th, 2012 at 6:04 am

Hi…
awesome source…
Thanks for sharing…
and one thing is we can drag it from right why not from the middle.. i’ll search for that any how its awesome..

Avatar

ashwini

December 28th, 2012 at 10:23 am

Thanks for information.Nice article..

Comment Form

  • Shanon: In fact no matter if someone doesn't be aware of afterward its up to other visitors that they will h [...]
  • how to deal with anxiety attacks: Wow, incredible weblog format! How lengthy have you been blogging for? you made running a blog look [...]
  • natural acne scar removal: Wow, fantastic blog layout! How long have you ever been blogging for? you make running a blog glan [...]
  • make money fast online: I could maintain your write-up due to the fact designed [...]
  • myrelationshipbuddy.com: Excellent site you have got here.. It's difficult to find good quality writing like yours these days [...]

Translate

    Translate to:

Coolest video on the blog

MacBook Air vs Mac Pro FCP Export

Twitter Updates

Find us on Facebook

Recent Activity

News Updates

Feedback

Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image