title: Android ListView drag & drop with TouchInterceptor: row stay visible
body: <p>I'm modifying TouchInterceptor class to get an drag &amp; drop listview with onLong touch event handle and I would love some help.</p>
  
  <p>I was able to add an GestureDetector to handle drag row but there is a graphic problem during dragging: the neighbour row take two line when I drag the row.
  Here is a screenshots of the problem: <a href="http://imgur.com/NoY3iiu" rel="nofollow">http://imgur.com/NoY3iiu</a></p>
  
  <p>I'm stuck, anyone?</p>
  
  <p>Here is my code:</p>
  
  <pre><code>public class MyTouchInterceptor extends ListView {
  
      private ImageView mDragView;
      private WindowManager mWindowManager;
      private WindowManager.LayoutParams mWindowParams;
      /**
       * At which position is the item currently being dragged. Note that this
       * takes in to account header items.
       */
      private int mDragPos;
      /**
       * At which position was the item being dragged originally
       */
      private int mSrcDragPos;
      private int mDragPointX;    // at what x offset inside the item did the user grab it
      private int mDragPointY;    // at what y offset inside the item did the user grab it
      private int mXOffset;  // the difference between screen coordinates and coordinates in this view
      private int mYOffset;  // the difference between screen coordinates and coordinates in this view
      private DragListener mDragListener;
      private DropListener mDropListener;
      private RemoveListener mRemoveListener;
      private int mUpperBound;
      private int mLowerBound;
      private int mHeight;
      private GestureDetector mGestureDetector;
      private static final int FLING = 0;
      private static final int SLIDE = 1;
      private static final int TRASH = 2;
      private int mRemoveMode = -1;
      private Rect mTempRect = new Rect();
      private Bitmap mDragBitmap;
      private final int mTouchSlop;
      private int mItemHeightNormal;
      private int mItemHeightExpanded;
      private int mItemHeightHalf;
      private Drawable mTrashcan;
  
      private final int LINE_THICKNESS = 15;
  
  
      public MyTouchInterceptor(Context context, AttributeSet attrs) {
          super(context, attrs);
          SharedPreferences pref = context.getSharedPreferences("Music", 3);
          mRemoveMode = pref.getInt("deletemode", -1);
          mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
          Resources res = getResources();
          mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
          mItemHeightHalf = mItemHeightNormal / 2;
          mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
  
  
          mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){
              @Override
              public void onLongPress(MotionEvent ev) {
                  int x = (int) ev.getX();
                  int y = (int) ev.getY();
                  int itemnum = pointToPosition(x, y);
                  if (itemnum == AdapterView.INVALID_POSITION) {
                      return;
                  }
                  ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
                  mDragPointX = x - item.getLeft();
                  mDragPointY = y - item.getTop();
                  mXOffset = ((int)ev.getRawX()) - x;
                  mYOffset = ((int)ev.getRawY()) - y;
                  // The left side of the item is the grabber for dragging the item
                  item.setDrawingCacheEnabled(true);
                  // Create a copy of the drawing cache so that it does not get recycled
                  // by the framework when the list tries to clean up memory
                  Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
  
                  startDragging(bitmap, x, y);
                  mDragPos = itemnum;
                  mSrcDragPos = mDragPos;
                  mHeight = getHeight();
                  int touchSlop = mTouchSlop;
                  mUpperBound = Math.min(y - touchSlop, mHeight / 3);
                  mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
              }
  
  
  
              @Override
              public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                  if (mDragView != null) {
                      //if (velocityX &gt; 1000) {
                      Rect r = mTempRect;
                      mDragView.getDrawingRect(r);
                      if ( e2.getX() &gt; r.right * 2 / 3) {
                          // fast fling right with release near the right edge of the screen
                          stopDragging();
                          mRemoveListener.remove(mSrcDragPos);
                          unExpandViews(true);
                      }
  
                      //}
                      // flinging while dragging should have no effect
                      return true;
                  }
                  return false;
              }
  
  
          });
      }
  
  
  
      /*
       * pointToPosition() doesn't consider invisible views, but we
       * need to, so implement a slightly different version.
       */
      private int myPointToPosition(int x, int y) {
  
          if (y &lt; 0) {
              // when dragging off the top of the screen, calculate position
              // by going back from a visible item
              int pos = myPointToPosition(x, y + mItemHeightNormal);
              if (pos &gt; 0) {
                  return pos - 1;
              }
          }
  
          Rect frame = mTempRect;
          final int count = getChildCount();
          for (int i = count - 1; i &gt;= 0; i--) {
              final View child = getChildAt(i);
              child.getHitRect(frame);
              if (frame.contains(x, y)) {
                  return getFirstVisiblePosition() + i;
              }
          }
          return INVALID_POSITION;
      }
  
  
  
  
      /*
       * Restore size and visibility for all listitems
       */
      private void unExpandViews(boolean deletion) {
          for (int i = 0;; i++) {
              View v = getChildAt(i);
              if (v == null) {
                  if (deletion) {
                      // HACK force update of mItemCount
                      int position = getFirstVisiblePosition();
                      int y = getChildAt(0).getTop();
                      setAdapter(getAdapter());
                      setSelectionFromTop(position, y);
                      // end hack
                  }
                  try {
                      layoutChildren(); // force children to be recreated where needed
                      v = getChildAt(i);
                  } catch (IllegalStateException ex) {
                      // layoutChildren throws this sometimes, presumably because we're
                      // in the process of being torn down but are still getting touch
                      // events
                  }
                  if (v == null) {
                      return;
                  }
              }
              ViewGroup.LayoutParams params = v.getLayoutParams();
              params.height = mItemHeightNormal;
              v.setLayoutParams(params);
              v.setVisibility(View.VISIBLE);
          }
      }
  
      /* Adjust visibility and size to make it appear as though
       * an item is being dragged around and other items are making
       * room for it:
       * If dropping the item would result in it still being in the
       * same place, then make the dragged listitem's size normal,
       * but make the item invisible.
       * Otherwise, if the dragged listitem is still on screen, make
       * it as small as possible and expand the item below the insert
       * point.
       * If the dragged item is not on screen, only expand the item
       * below the current insertpoint.
       */
      private void doExpansion() {
          int childnum = mDragPos - getFirstVisiblePosition();
          if (mDragPos &gt; mSrcDragPos) {
              childnum++;
          }
          int numheaders = getHeaderViewsCount();
  
          View first = getChildAt(mSrcDragPos - getFirstVisiblePosition());
          for (int i = 0;; i++) {
              View vv = getChildAt(i);
              if (vv == null) {
                  break;
              }
  
              int height = mItemHeightNormal;
              int visibility = View.VISIBLE;
              if (mDragPos &lt; numheaders &amp;&amp; i == numheaders) {
                  // dragging on top of the header item, so adjust the item below
                  // instead
                  if (vv.equals(first)) {
                      visibility = View.INVISIBLE;
                  } else {
                      height = mItemHeightExpanded;
                  }
              } else if (vv.equals(first)) {
                  // processing the item that is being dragged
                  if (mDragPos == mSrcDragPos || getPositionForView(vv) == getCount() - 1) {
                      // hovering over the original location
                      visibility = View.INVISIBLE;
                  } else {
                      // not hovering over it
                      // Ideally the item would be completely gone, but neither
                      // setting its size to 0 nor settings visibility to GONE
                      // has the desired effect.
                      height = 1;
                  }
              } else if (i == childnum) {
                  if (mDragPos &gt;= numheaders &amp;&amp; mDragPos &lt; getCount() - 1) {
                      height = mItemHeightExpanded;
                  }
              }
              ViewGroup.LayoutParams params = vv.getLayoutParams();
              params.height = height;
              vv.setLayoutParams(params);
              vv.setVisibility(visibility);
          }
      }
  
  
      @Override
      public boolean onTouchEvent(MotionEvent ev) {
          if (mGestureDetector != null) {
              mGestureDetector.onTouchEvent(ev);
          }
          if ((mDragListener != null || mDropListener != null) &amp;&amp; mDragView != null) {
              int action = ev.getAction();
              switch (action) {
                  case MotionEvent.ACTION_UP:
                  case MotionEvent.ACTION_CANCEL:
                      Rect r = mTempRect;
                      mDragView.getDrawingRect(r);
                      stopDragging();
                      if (mRemoveMode == SLIDE &amp;&amp; ev.getX() &gt; r.right * 3 / 4) {
                          if (mRemoveListener != null) {
                              mRemoveListener.remove(mSrcDragPos);
                          }
                          unExpandViews(true);
                      } else {
                          if (mDropListener != null &amp;&amp; mDragPos &gt;= 0 &amp;&amp; mDragPos &lt; getCount()) {
                              mDropListener.drop(mSrcDragPos, mDragPos);
                          }
                          unExpandViews(false);
                      }
                      break;
  
                  case MotionEvent.ACTION_DOWN:
                  case MotionEvent.ACTION_MOVE:
                      int x = (int) ev.getX();
                      int y = (int) ev.getY();
                      dragView(x, y);
                      int itemnum = getItemForPosition(y);
                      if (itemnum &gt;= 0) {
                          if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
                              if (mDragListener != null) {
                                  mDragListener.drag(mDragPos, itemnum);
                              }
                              mDragPos = itemnum;
                              doExpansion();
                          }
                          int speed = 0;
                          adjustScrollBounds(y);
                          if (y &gt; mLowerBound) {
                              // scroll the list up a bit
                              if (getLastVisiblePosition() &lt; getCount() - 1) {
                                  speed = y &gt; (mHeight + mLowerBound) / 2 ? 16 : 4;
                              } else {
                                  speed = 1;
                              }
                          } else if (y &lt; mUpperBound) {
                              // scroll the list down a bit
                              speed = y &lt; mUpperBound / 2 ? -16 : -4;
                              if (getFirstVisiblePosition() == 0
                                      &amp;&amp; getChildAt(0).getTop() &gt;= getPaddingTop()) {
                                  // if we're already at the top, don't try to scroll, because
                                  // it causes the framework to do some extra drawing that messes
                                  // up our animation
                                  speed = 0;
                              }
                          }
                          if (speed != 0) {
                              smoothScrollBy(speed, 30);
                          }
                      }
                      break;
              }
              return true;
          }
          return super.onTouchEvent(ev);
      }
  
      private void startDragging(Bitmap bm, int x, int y) {
          stopDragging();
  
          …
          mDragView = v;
      }
  
      private void dragView(int x, int y) {
          if (mRemoveMode == SLIDE) {
              float alpha = 1.0f;
              int width = mDragView.getWidth();
              if (x &gt; width / 2) {
                  alpha = ((float)(width - x)) / (width / 2);
              }
              mWindowParams.alpha = alpha;
          }
  
          if (mRemoveMode == FLING || mRemoveMode == TRASH) {
              mWindowParams.x = x - mDragPointX + mXOffset;
          } else {
              mWindowParams.x = 0;
          }
          mWindowParams.y = y - mDragPointY + mYOffset;
          mWindowManager.updateViewLayout(mDragView, mWindowParams);
  
          if (mTrashcan != null) {
              int width = mDragView.getWidth();
              if (y &gt; getHeight() * 3 / 4) {
                  mTrashcan.setLevel(2);
              } else if (width &gt; 0 &amp;&amp; x &gt; width / 4) {
                  mTrashcan.setLevel(1);
              } else {
                  mTrashcan.setLevel(0);
              }
          }
      }
  
      private void stopDragging() {
          if (mDragView != null) {
              mDragView.setVisibility(GONE);
              WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
              wm.removeView(mDragView);
              mDragView.setImageDrawable(null);
              mDragView = null;
          }
          if (mDragBitmap != null) {
              mDragBitmap.recycle();
              mDragBitmap = null;
          }
          if (mTrashcan != null) {
              mTrashcan.setLevel(0);
          }
      }
  
      public void setTrashcan(Drawable trash) {
          mTrashcan = trash;
          mRemoveMode = TRASH;
      }
  
      public void setDragListener(DragListener l) {
          mDragListener = l;
      }
  
      public void setDropListener(DropListener l) {
          mDropListener = l;
      }
  
      public void setRemoveListener(RemoveListener l) {
          mRemoveListener = l;
      }
  
      public interface DragListener {
          void drag(int from, int to);
      }
      public interface DropListener {
          void drop(int from, int to);
      }
      public interface RemoveListener {
          void remove(int which);
      }
  </code></pre>