Android Drag and Drop, Click, and Scroll in a Listview

1.2k views Asked by At

So I have a layout as such: Example of layout

I am using a Listview right now and each row has an Imageview and 2 Textviews.

I want to be able to do 3 things on this page:

  1. Clicking on a row (or just clicking on the Imageview in that row works for me too) brings me to another Fragment.
  2. You can drag the image in each Listview row. It uses DragShadowBuilder and therefore can detect if you drop it into that darker gray zone in the bottom.
  3. You can scroll up and down in the Listview to get to the other items that are overflowing right now.

As you can imagine, these three cases are difficult to capture because its hard to differentiate between the three (bc of overlaps in functionality).

I would prefer not to use onItemLongClickListener to do the drag and drop bc users usually don't think to hold their fingers down a long time to start drag and drop.

Any suggestions on how to implement this to capture all three use cases? Actually, it can be thought of as 2 use cases because if I dropped an image back into its original container, it could count as a click for me. The most complex part is to make that somehow work with scrolling up and down the Listview...

Thanks for your help in advance!

p.s. This entire view is rendered in a Fragment and clicking on a view or successfully dropping one into the gray area opens up a separate fragment.

3

There are 3 answers

2
Patel Hiren On
 /**
 * Call this from a drag source view.
 */
@SuppressLint("NewApi")
public boolean onTouchEvent(MotionEvent ev) {
    if (!mDragging) {
        return false;
    }

    final int action = ev.getAction();
    final int screenX = clamp((int)ev.getRawX(), 0, mDisplayMetrics.widthPixels);
    final int screenY = clamp((int)ev.getRawY(), 0, mDisplayMetrics.heightPixels);

    switch (action) {
    case MotionEvent.ACTION_DOWN:
        // Remember where the motion event started
        mMotionDownX = screenX;
        mMotionDownY = screenY;
        break;
    case MotionEvent.ACTION_MOVE:
        //Set background color of remove comment box layout
        if(((int)ev.getY() <= 50))
            ImageEditingActivityNew.rl_remove.setBackgroundColor(Color.RED);
        else
            ImageEditingActivityNew.rl_remove.setBackgroundColor(Color.TRANSPARENT);

        // Update the drag view.  Don't use the clamped pos here so the dragging looks
        // like it goes off screen a little, intead of bumping up against the edge.
        mDragView.move((int)ev.getRawX(), (int)ev.getRawY());

        // Drop on someone?
        final int[] coordinates = mCoordinatesTemp;
        DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
        if (dropTarget != null) {
            if (mLastDropTarget == dropTarget) {
                dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
            } else {
                if (mLastDropTarget != null) {
                    mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
                }
                dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],
                    (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
            }
        } else {
            if (mLastDropTarget != null) {
                mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
                    (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
            }
        }
        mLastDropTarget = dropTarget;


        /* The original Launcher activity supports a delete region and scrolling.
           It is not needed in this example.

        // Scroll, maybe, but not if we're in the delete region.
        boolean inDeleteRegion = false;
        if (mDeleteRegion != null) {
            inDeleteRegion = mDeleteRegion.contains(screenX, screenY);
        }
        //Log.d(TAG, "inDeleteRegion=" + inDeleteRegion + " screenX=" + screenX
        //        + " mScrollZone=" + mScrollZone);
        if (!inDeleteRegion && screenX < mScrollZone) {
            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                mScrollState = SCROLL_WAITING_IN_ZONE;
                mScrollRunnable.setDirection(SCROLL_LEFT);
                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
            }
        } else if (!inDeleteRegion && screenX > scrollView.getWidth() - mScrollZone) {
            if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                mScrollState = SCROLL_WAITING_IN_ZONE;
                mScrollRunnable.setDirection(SCROLL_RIGHT);
                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
            }
        } else {
            if (mScrollState == SCROLL_WAITING_IN_ZONE) {
                mScrollState = SCROLL_OUTSIDE_ZONE;
                mScrollRunnable.setDirection(SCROLL_RIGHT);
                mHandler.removeCallbacks(mScrollRunnable);
            }
        }
        */
        break;
    case MotionEvent.ACTION_UP:
        //When touch up then remove comment box

        if(((int)ev.getY() <= 50)) {
            ImageEditingActivity.rl_imageEdit_comment.removeAllViews();
            ImageEditingActivity.rl_imageEdit_comment.invalidate();
            //ImageEditingActivity.mDragLayer.removeView(ImageEditingActivity.rl_imageEdit_comment);
            ImageEditingActivity.addComment = true;
            ImageEditingActivity.changeIconBackground("Remove");
            ImageEditingActivity.editParam.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        }

        if (mDragging) {
            drop(screenX, screenY);
        }

        endDrag();
        break;
    case MotionEvent.ACTION_CANCEL:
        cancelDrag();
    }

    return true;
}
2
siva On

Instead of long press and hold you can use fling/swipe gesture. You can override onFling method of SimpleonGestureListner and detect fling based on the threshold.

0
chenshov On

Your requirements conflicts. I would do the next thing in your case :

  1. Your row can be drag only with long pressed (then I would use the shadow drag) also I would do that the view of the drop only appears when long press occurs.

  2. override the onclick method of the ImageView that way you can click it and then call your fragment to expand

  3. Now because you didn't override any onTouchListeners or onClick on the row you can scroll freely as long as you don't click a picture.