ListView拖拽交换 item 的实现(QQ 分组管理功能)

在写这篇文章前,碰巧看到有个哥们也做了这个功能,【Android】可拖拽排序的ListView。而且就在几个小时前发表的,本来想还是算了,我就不写这个功能,不过我大致浏览了他的实现原理跟我的实现原理还是有很大差别,所以还是决定写这样一篇文章,因为我相信大家看文章,更多是想了解其中的原理,而非单纯的为了实现某个功能。只有了解了原理,才能扩展,实现更多的功能。

好了,,回到正题,先来看效果图

分析原理

1、长按获取所按 Item 的 View,并通过View 生成一个 Drawable; 2、 在 ListView 中根据手势滑动不断的绘制 Drawable; 3、Drawable每到达另一个 Item的位置,就替换这个 item,并设置动画效果; 4、当 Drawable 滑到屏幕的最下方或者最上方,且此 ListView 可以滚动,这 listView 执行滚动。 开篇就讲了,此功能的实现方式不止一种,所以有兴趣了解其他实现原理可以看 [【Android】可拖拽排序的ListView]这篇文章。

分步实现

这里就按照原理来一步一步实现此功能,先来看如何获取 Drawable

通过 View 获取 bitmapDrawable 通过 position 获取 View

/*获取Item的View*/View mobileView = getViewForPosition(position);

因为我们一般在 ListView 的 adapter 中都会复用 View,所以这里需要做一个 position 的转化

/*** 通过位置获取View** @param position* @return*/public View getViewForPosition(int position) {int itemPosition = position – getFirstVisiblePosition();View view = getChildAt(itemPosition);return view;}

通过 View,获取 Drawable

/*** 创建一个* BitmapDrawable* @param view* @return*/private BitmapDrawable createDrawable(View view) {BitmapDrawable bitmapDrawable = null;/*获取位置变量*/int left = view.getLeft();int top = view.getTop();int right = left + view.getMeasuredWidth();int bottom = top + view.getMeasuredHeight();/*首先创建一个Bitmap,这个位图为空位图,里面啥也没有*/Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);/*创建一个画布,把空位图放入画布中*/Canvas canvas = new Canvas(bitmap);/*把所选的item的View 绘制到画布上,其实也就是会知道了Bitmap上*/view.draw(canvas);/*绘制边矩形*/Rect rect = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()) ;Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG) ;paint.setStrokeWidth(12);paint.setStyle(Paint.Style.STROKE);paint.setColor(0xff1f8c03);canvas.drawRect(rect, paint);/*把bitmap转化成BitmapDrawable*/bitmapDrawable = new BitmapDrawable(getResources(), bitmap);/*设置drawable的原始位置*/mCellOriginBounds = new Rect(left, top, right, bottom);/*初始化drawable的当前位置*/mCellCurrentBounds = new Rect(mCellOriginBounds);bitmapDrawable.setBounds(mCellCurrentBounds);/*设置drawable的透明度*/bitmapDrawable.setAlpha(120);return bitmapDrawable;}

这里获取的是一个 BitmapDrawable,其实也就是一个 Drawable,BitmapDrawable继承之Drawable,所以很多方法可以共用;并且设置了它的位置,跟所选中的 item 的位置一样, bitmapDrawable.setBounds(mCellCurrentBounds); 接着是把 Drawable 绘制在 ListView 上面

(Canvas canvas) {super.dispatchDraw(canvas);if (mMobileView != null) {mMobileView.draw(canvas);}}

调用invalidate()重绘方法,这时的效果图已经可以看到了

监听手势滑动

手势滑动的时候不断改变 drawable 的 bounds,并invalidate(),这时我们所获取的 BitmapDrawable 就会不断的改变位置,具体怎么改变呢,看代码

(MotionEvent ev) {final int action = ev.getAction();switch (action & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:/*这里是获取按下的时候点的坐标位置*/int pointIndex = MotionEventCompat.getActionIndex(ev);mActivityPointId = MotionEventCompat.getPointerId(ev, pointIndex);if (mActivityPointId == INVALID) {break;}mDownMotionY = (int) MotionEventCompat.getY(ev, pointIndex);break;case MotionEvent.ACTION_MOVE:if (isMove) {int pointMoveIndex = MotionEventCompat.getActionIndex(ev);mActivityPointId = MotionEventCompat.getPointerId(ev, pointMoveIndex);if (mActivityPointId == INVALID) {break;}mCurrentMotionY = (int) MotionEventCompat.getY(ev, mActivityPointId);int delay = mCurrentMotionY – mDownMotionY;/*偏移Drawable的位置*/mCellCurrentBounds.offsetTo(mCellCurrentBounds.left, mCellOriginBounds.top + delay + mScrollOffset);mMobileView.setBounds(mCellCurrentBounds);/*去判断是否需要交换数据*/checkExchangeItem();isScroll = false;/*判断是否需要滚动ListView*/isScroll = checkScroll();/*然后重绘*/invalidate();return false;}break;case MotionEvent.ACTION_UP:/*松手执行结束拖拽操作*/touchEventEnd();break;case MotionEvent.ACTION_POINTER_UP:int pointUpIndex = MotionEventCompat.getActionIndex(ev);int pointId = MotionEventCompat.getPointerId(ev, pointUpIndex);if (pointId == mActivityPointId) {/*松手执行结束拖拽操作*/touchEventEnd();}break;case MotionEvent.ACTION_CANCEL:/*这里直接复原*/touchEventCancel();break;}return super.onTouchEvent(ev);}

在 Drawable 改变位置的时候,我们是要不断的检测,是否达到交换 item 的条件,如果达到了,则执行交换,并设置相应的交换动画效果。

如若今生再相见,哪怕流离百世,迷途千年,也愿。

ListView拖拽交换 item 的实现(QQ 分组管理功能)

相关文章:

你感兴趣的文章:

标签云: