THIS CONTENT DOWNLOAD SHORTLY

Objective

The main objective of this blog post is to help you understand on how to drag and drop list items in list view in Android.

 

Introduction:

If you want to have a custom list view in which you can drag and drop list items, this information will be very useful to you.

 

Step 1 Create an android application project in eclipse

Create an android application project in eclipse. To drag and drop list items in list view, we need a custom list view. So first create a class for custom list view and extend it to ListView super class.

 

Step 2 OnItemLongClickListener

Set OnItemLongClick listener in adapter view and create a hover cell for the selected item.

private BitmapDrawable getAndAddHoverView(View v) {
 
    int w = v.getWidth();
    int h = v.getHeight();
    int top = v.getTop();
    int left = v.getLeft();
 
    Bitmap b = getBitmapWithBorder(v);
 
    BitmapDrawable drawable = new BitmapDrawable(getResources(), b);
 
    mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
    mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);
 
    drawable.setBounds(mHoverCellCurrentBounds);
 
    returndrawable;
}

This code draws a black border over the screenshot of the view:

private Bitmap getBitmapWithBorder(View v) {
    Bitmap bitmap = getBitmapFromView(v);
    Canvas can = new Canvas(bitmap);
 
    Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
 
    Paint paint = new Paint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(LINE_THICKNESS);
    paint.setColor(Color.BLACK);
 
    can.drawBitmap(bitmap, 0, 0, null);
    can.drawRect(rect, paint);
 
    return bitmap;
}

This code returns a bitmap showing a screenshot of the view passed in:

private Bitmap getBitmapFromView(View v) {
    Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(),
    Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    v.draw(canvas);
    return bitmap;
}
 

Step 3 Manage item id of each item

itemID stores a reference to the views above and below the item currently corresponding to the hover cell. It is important to note that ItemId above the cell and ItemId below the cell may be invalid.

private void updateNeighborViewsForID(longitemID) {
    int position = getPositionForID(itemID);
    StableArrayAdapter adapter = ((StableArrayAdapter) getAdapter());
    mAboveItemId = adapter.getItemId(position - 1);
    mBelowItemId = adapter.getItemId(position + 1);
}

This code retrieves the position in the list corresponding to itemID:

public int getPositionForID(longitemID) {
    View v = getViewForID(itemID);
        if (v == null) {
        return -1;
    } else {
        return getPositionForView(v);
    }
}
 

Step 4 Override onTouchEvent()

Override onTouchEvent and put this code in it.

@Override
public boolean onTouchEvent(MotionEvent event) {
 
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        mDownX = (int) event.getX();
        mDownY = (int) event.getY();
        mActivePointerId = event.getPointerId(0);
        break;
        case MotionEvent.ACTION_MOVE:
        if (mActivePointerId == INVALID_POINTER_ID) {
            break;
        }
 
        int pointerIndex = event.findPointerIndex(mActivePointerId);
 
        mLastEventY = (int) event.getY(pointerIndex);
        int deltaY = mLastEventY - mDownY;
     
        if (mCellIsMobile) {
            mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,mHoverCellOriginalBounds.top + deltaY + mTotalOffset);
            mHoverCell.setBounds(mHoverCellCurrentBounds);
            invalidate();
 
            handleCellSwitch();
     
            mIsMobileScrolling = false;
            handleMobileCellScroll();
     
            return false;
        }
        break;
        case MotionEvent.ACTION_UP: touchEventsEnded();
        break;
        case MotionEvent.ACTION_CANCEL: touchEventsCancelled();
        break;
        case MotionEvent.ACTION_POINTER_UP: pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = event.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            touchEventsEnded();
        }
        break;
        default:
        break;
    }
     
    return super.onTouchEvent(event);
}
 

Step 5 handleCellSwitch() method

Create a method handleCellSwitch. This method determines whether the hover cell has been shifted far enough to invoke a cell swap. If so, then the respective cell swap candidate is determined and the data set is changed.

Upon posting a notification of the data set change, a layout is invoked to place the cells in the right place. Using ViewTreeObserver and a corresponding OnPreDrawListener, we can offset the cell being swapped to where it previously was and then animate it to its new position.

private void handleCellSwitch() {
    final int deltaY = mLastEventY - mDownY;
    int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffset + deltaY;
 
    View belowView = getViewForID(mBelowItemId);
    View mobileView = getViewForID(mMobileItemId);
    View aboveView = getViewForID(mAboveItemId);
 
    boolean isBelow = (belowView != null)
    && (deltaYTotal>belowView.getTop());
    booleanisAbove = (aboveView != null)
    && (deltaYTotal<aboveView.getTop());
 
    if (isBelow || isAbove) {
 
        final long switchItemID = isBelow ? mBelowItemId : mAboveItemId;
        View switchView = isBelow ? belowView : aboveView;
        final int originalItem = getPositionForView(mobileView);
 
        if (switchView == null) {
            updateNeighborViewsForID(mMobileItemId);
            return;
        }
 
        swapElements(mCheeseList, originalItem,
        getPositionForView(switchView));
 
        ((BaseAdapter) getAdapter()).notifyDataSetChanged();
 
        mDownY = mLastEventY;
 
        final int switchViewStartTop = switchView.getTop();
 
        mobileView.setVisibility(View.VISIBLE);
        switchView.setVisibility(View.INVISIBLE);
 
        updateNeighborViewsForID(mMobileItemId);
 
        final ViewTreeObserver observer = getViewTreeObserver();
        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @SuppressLint("NewApi")
            publicboolean onPreDraw() {
                observer.removeOnPreDrawListener(this);
 
                View switchView = getViewForID(switchItemID);
 
                mTotalOffset += deltaY;
 
                int switchViewNewTop = switchView.getTop();
                int delta = switchViewStartTop - switchViewNewTop;
 
                switchView.setTranslationY(delta);
 
                ObjectAnimator animator = ObjectAnimator.ofFloat(
                switchView, View.TRANSLATION_Y, 0);
                animator.setDuration(MOVE_DURATION);
                animator.start();
 
                return true;
            }
        });
    }
}
 
private void swapElements(ArrayListarrayList, intindexOne, intindexTwo) {
    Object temp = arrayList.get(indexOne);
    arrayList.set(indexOne, arrayList.get(indexTwo));
    arrayList.set(indexTwo, temp);
}
 

Step 6 touchEventEnded() method

Create method touchEventsEnded. This event resets all the appropriate fields to a default state while animating the hover cell back to its correct location.

private void touchEventsEnded() {
    final View mobileView = getViewForID(mMobileItemId);
    if (mCellIsMobile || mIsWaitingForScrollFinish) {
        mCellIsMobile = false;
        mIsWaitingForScrollFinish = false;
        mIsMobileScrolling = false;
        mActivePointerId = INVALID_POINTER_ID;
        if (mScrollState!=OnScrollListener.SCROLL_STATE_IDLE) {
            mIsWaitingForScrollFinish = true;
            return;
        }
        mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left,mobileView.getTop());
 
        ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell,"bounds",sBoundEvaluator,mHoverCellCurrentBounds);
        hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                invalidate();
            }
        });
        hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                setEnabled(false);
            }
 
            @Override
                public void onAnimationEnd(Animator animation) {
                mAboveItemId = INVALID_ID;
                mMobileItemId = INVALID_ID;
                mBelowItemId = INVALID_ID;
                mobileView.setVisibility(VISIBLE);
                mHoverCell = null;
                setEnabled(true);
                invalidate();
            }
        });
        hoverViewAnimator.start();
    } else {
        touchEventsCancelled();
    }
}
 

Step 7 touchEventCancelled() method

Create method touchEventsCancelled. This method is called when you don’t want to move the cell that you’ve selected. This method resets all the appropriate fields to their default state.

private void touchEventsCancelled() {
    View mobileView = getViewForID(mMobileItemId);
    if  (mCellIsMobile) {
        mAboveItemId = INVALID_ID;
        mMobileItemId = INVALID_ID;
        mBelowItemId = INVALID_ID;
        mobileView.setVisibility(VISIBLE);
        mHoverCell = null;
        invalidate();
    }
    mCellIsMobile = false;
    mIsMobileScrolling = false;
    mActivePointerId = INVALID_POINTER_ID;
}
 

Step 8 Create a TypeEvaluator

Create a TypeEvaluator. This TypeEvaluator is used to animate the BitmapDrawable back to its final location when the user lifts his finger by modifying theBitmapDrawable's bounds. Also create a method handleMobileCellScroll to determine whether the listview is in a scrolling state or not.

private final static TypeEvaluatorsBoundEvaluator = newTypeEvaluator() {
        publicRect evaluate(floatfraction, Rect startValue, Rect endValue) {
            returnnew Rect(interpolate(startValue.left, endValue.left,fraction),interpolate(startValue.top, endValue.top, fraction), interpolate(startValue.right, endValue.right, fraction), interpolate(startValue.bottom, endValue.bottom,fraction));
        }
 
        publicint interpolate(intstart, intend, floatfraction) {
            return (int) (start + fraction * (end - start));
        }
 
        @Override
        public Object evaluate(floatarg0, Object arg1, Object arg2) {
            // TODO Auto-generated method stub
            returnnull;
        }
    };
private void handleMobileCellScroll() {
    mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
}
 

Step 9 Create handleMobileCellScroll() method

Create handleMobileCellScroll method. This method determines if the hover cell is above or below the bounds of the listview. If so, the listview does an appropriate upward or downward smooth scroll to reveal the new items.

public boolean handleMobileCellScroll(Rect r) {
    int offset = computeVerticalScrollOffset();
    int height = getHeight();
    int extent = computeVerticalScrollExtent();
    int range = computeVerticalScrollRange();
    int hoverViewTop = r.top;
    int hoverHeight = r.height();
 
    if (hoverViewTop<= 0 &&offset> 0) {
        smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
        return true;
    }
 
    if (hoverViewTop + hoverHeight>= height&& (offset + extent) <range) {
        smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
        return true;
    }
 
    return false;
}
 

Step 10 Create OnScrollListener

Create onScrollListener. This scroll listener is added to listview in order to handle cell swapping when the cell is at the top or bottom edge of the listview. If the hover cell is at either edge of the listview, listview begins scrolling.

While scrolling, listview continuously checks for the visibility of new cells and also determines whether they are potential candidates for a cell swap or not.

private AbsListView.OnScrollListener mScrollListener = new AbsListView.OnScrollListener() {
    private int mPreviousFirstVisibleItem = -1;
    private int mPreviousVisibleItemCount = -1;
    private int mCurrentFirstVisibleItem;
    private int mCurrentVisibleItemCount;
    private int mCurrentScrollState;
 
    public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
        mCurrentFirstVisibleItem = firstVisibleItem;
        mCurrentVisibleItemCount = visibleItemCount;
 
        mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem : mPreviousFirstVisibleItem;
        mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount : mPreviousVisibleItemCount;
 
        checkAndHandleFirstVisibleCellChange();
        checkAndHandleLastVisibleCellChange();
 
        mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
        mPreviousVisibleItemCount = mCurrentVisibleItemCount;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;
        mScrollState = scrollState;
        isScrollCompleted();
    }
    private void isScrollCompleted() {
    if (mCurrentVisibleItemCount> 0 &&mCurrentScrollState == SCROLL_STATE_IDLE) {
        if (mCellIsMobile&&mIsMobileScrolling) {
            handleMobileCellScroll();
        } elseif (mIsWaitingForScrollFinish) {
        touchEventsEnded();
        }
    }
}
public void checkAndHandleFirstVisibleCellChange() {
    if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
        if (mCellIsMobile&&mMobileItemId != INVALID_ID) {
            updateNeighborViewsForID(mMobileItemId);
            handleCellSwitch();
            }
        }
    }

    public void checkAndHandleLastVisibleCellChange() {
        int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;
        int previousLastVisibleItem = mPreviousFirstVisibleItem + mPreviousVisibleItemCount;
            if (currentLastVisibleItem != previousLastVisibleItem) {
                if (mCellIsMobile&&mMobileItemId != INVALID_ID) {
                    updateNeighborViewsForID(mMobileItemId);
                    handleCellSwitch();
                }
            }
        }
};
 

Step 11 activity_list_view.xml file

Once you’re done with custom list view class, make a layout for your drag and drop list view. Put custom list view to your main screen and make one more layout to inflate the list items.

In activity_list_view layout put this code for list view:

<com.example.android.listviewdragginganimation.DynamicListView
android:id="@+id/listview"
android:background="#0000"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

In text_view layout put this code for list item:

<TextViewxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFF" android:gravity="center_vertical" android:minHeight="@dimen/list_item_height" android:paddingLeft="15dp" android:paddingRight="15dp" android:textColor="#000000" android:textSize="@dimen/list_text_size"/>
 

Step 12 MainActivity.java file

Now, move to the MainActivity of the project. Set listeners on custom listview. Create an array of data that you want to display in the list. Set adapter on custom listview.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);
 
        ArrayList mCheeseList = new ArrayList();
for (int i = 0; i< Cheeses.sCheeseStrings.length; ++i) {
mCheeseList.add(Cheeses.sCheeseStrings[i]);
        }
 
        StableArrayAdapter adapter = new StableArrayAdapter(this, R.layout.text_view, mCheeseList);
        DynamicListView listView = (DynamicListView) findViewById(R.id.listview);
 
listView.setCheeseList(mCheeseList);
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    }

I hope you find this blog is very helpful while working with Drag and Drop List Item in List View in Android. Let me know in comment if you have any questions regarding Android. I will reply you ASAP.

Got an Idea of Android App Development? What are you still waiting for? Contact us now and see the Idea live soon. Our company has been named as one of the best Android App Development Company in India.

I am Android App Developer at TheAppGuruz. Here I am sharing information about Android app development tips and tricks.