自己动手(一)──可拖动排序的 ListView(1)
来源:互联网 发布:网络喷子的心理分析 编辑:程序博客网 时间:2024/05/16 06:00
前言
相关的开源库有很多,也非常完善。然而,正因为非常完善,代码量很大。想要学习的时候,感觉无从下手,也意味着无法自己扩展。所以,我有个计划,把这些轮子自己再造一遍,明白其中的原理,在需要的时候,能够自己扩展。于此同时,如果你想要一个简单初级、容易理解的版本,也许这篇文章会有帮助。
参考
http://blog.csdn.net/jj120522/article/details/8240407
https://github.com/bauerca/drag-sort-listview
效果图
思路
- 监听 TouchEvent
- 确定要拖动的 ItemView──DragItemView
- 生成DragItemView 的快照图像──DragItemViewBitmap
- 隐藏 DragItemView
- 在 onDraw 函数中绘制 DragItemViewBitmap
- 监听 onMove 事件,随时改变DragItemViewBitmap绘制位置
- 监听 onUp、onCancel 事件,通过 adapter 改变数据的次序,间接改变 ListView中 itemView 的顺序
关键源码
注:注释是用英文写的,没有语法可言,只为表达意思。
另:在注释中引用自己的域或方法,可以用这种方式{@link #draggingItemViewBitmap}
。
public class DragSortListView extends ListView { /** * the mask image that moves as user finger moves */ private Bitmap draggingItemViewBitmap; /** * the region that the {@link #draggingItemViewBitmap} will be drawn */ private RectF draggingItemViewRect; /** * the paint used in drawing {@link #draggingItemViewBitmap}, new it in onDraw isn't recommended */ private Paint draggingItemViewBitmapPaint; /** * the y coordinate of the beginning <code>ACTION_DOWN</code>, also the beginning of the whole gesture */ private float downY; /** * the y coordinate of the last MotionEvent */ private float lastY; /** * the original position of the draggingItemView */ private int srcPosition; /** * the flag that indicate whether we are dragging some item view */ private boolean dragging; /** * store it, so we can set it to be visible when dragging ends */ private View draggingItemView; @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: if (handleDownEvent(ev)) { //return true means i am interested in this gesture return true; } break; case MotionEvent.ACTION_MOVE: if (dragging) { handleMoveEvent(ev); return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (dragging) { handleUpEvent(ev); return true; } break; } return super.onTouchEvent(ev); } private boolean handleDownEvent(MotionEvent ev) { float downX = ev.getX(); float downY = ev.getY(); int downPosition = pointToPosition(((int) downX), ((int) downY)); if (downPosition == AdapterView.INVALID_POSITION){ return false; } // get the item view under user's finger View underFingerItemView = getChildAt(downPosition - getFirstVisiblePosition()); // check whether user's finger pressed on the drag handler View dragHandler = underFingerItemView.findViewById(R.id.tv_drag_handler); if (dragHandler == null) { // if the under item view don't have a drag handler, don't start drag return false; } Rect dragHandlerHitRect = new Rect(); dragHandler.getHitRect(dragHandlerHitRect); //important! change the coordinate system from underFingerItemView to this listview. dragHandlerHitRect.offset(((int) underFingerItemView.getLeft()), ((int) underFingerItemView.getTop())); if (!dragHandlerHitRect.contains(((int) downX), ((int) downY))){ // if user didn't pressed on the drag handler, don't start drag return false; } // now we can start drag dragging = true; draggingItemView = underFingerItemView;// this.downY = downY; lastY = downY; draggingItemViewBitmap = getBitmapFromView(draggingItemView);// underFingerItemView.getHitRect(draggingItemViewRect);// underFingerItemView.getDrawingRect();// Rect underFingerItemViewDrawingRect = new Rect();// underFingerItemView.getDrawingRect(underFingerItemViewDrawingRect);// underFingerItemView.getHitRect(underFingerItemViewDrawingRect); the same as up one, the same coordinate , the same size draggingItemViewRect = new RectF(); draggingItemViewRect.set(draggingItemView.getLeft(), draggingItemView.getTop(), draggingItemView.getRight(), draggingItemView.getBottom()); draggingItemViewBitmapPaint = new Paint(); srcPosition = downPosition; draggingItemView.setVisibility(INVISIBLE); invalidate(); return true; } private void handleMoveEvent(MotionEvent ev) { // update the position where the mask image will be drawn float currY = ev.getY(); float dy = currY - lastY; lastY = currY; draggingItemViewRect.offset(0, dy); // redraw invalidate(); } private void handleUpEvent(MotionEvent ev) { // reset dragging = false; draggingItemViewBitmap = null; draggingItemView.setVisibility(VISIBLE); draggingItemView = null; // reorder int upPosition = pointToPosition(((int) ev.getX()), ((int) ev.getY())); int dstPosition = upPosition; if (upPosition == AdapterView.INVALID_POSITION){ dstPosition = srcPosition; } ((CommonDragSortAdapter) getAdapter()).moveItem(srcPosition, dstPosition); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (dragging) { canvas.drawBitmap(draggingItemViewBitmap, draggingItemViewRect.left, draggingItemViewRect.top, draggingItemViewBitmapPaint); } } public static Bitmap getBitmapFromView(View view){ view.setDrawingCacheEnabled(true); // this is the important code :) // Without it the view will have a dimension of 0,0 and the bitmap // will be null// view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),// MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));// view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());// view.buildDrawingCache(true); Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); // clear drawing cache// view.setDrawingCacheEnabled(false); return bitmap; }}
实现过程中犯的错误
1.坐标参照系
Rect dragHandlerHitRect = new Rect();dragHandler.getHitRect(dragHandlerHitRect);if (!dragHandlerHitRect.contains(((int) downX), ((int) downY))){ // if user didn't pressed on the drag handler, don't start drag return false;}
上面的代码不能得到预期结果。getHitRect
得到的矩形是以其父视图为参考系的。所以,dragHandlerHitRect
是以underFingerItemView
为参考系的。然而,downX
、downY
是以整个 ListView
为参考系的。不在一个参考系的坐标的比较是没有意义的。解决办法:变换坐标参考系。dragHandlerHitRect.offset(((int) underFingerItemView.getLeft()), ((int) underFingerItemView.getTop()));
2.mask image 没有随手指的移动而移动
因为dy没有想清楚到底是用 currY-lastY,还是 currY-downY。
3.item view getTop() 总是得到0
public static Bitmap getBitmapFromView(View view){ view.setDrawingCacheEnabled(true); // this is the important code :) // Without it the view will have a dimension of 0,0 and the bitmap // will be null view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.buildDrawingCache(true); Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); // clear drawing cache view.setDrawingCacheEnabled(false); return bitmap; }
是受上面这个函数的影响了,让 item view重新布局了,所以才出现的问题。解决办法:删除重新布局代码。
4.想要拖动 Item view 的时候,ListView 在滑动
@Overridepublic boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: if (handleDownEvent(ev)) { //return true means i am interested in this gesture return true; } break; case MotionEvent.ACTION_MOVE: handleMoveEvent(ev); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: handleUpEvent(ev); break; } return super.onTouchEvent(ev);}
让case MotionEvent.ACTION_MOVE、MotionEvent.ACTION_UP、MotionEvent.ACTION_CANCEL在 dragging 状态下也返回 true 即可
5.mask image 和 dragging item view 不相符
不用 viewholder 模式就没事。
public View getView(int position, View convertView, ViewGroup parent) { CommonViewHolder<T> holder = null;// if (convertView == null) { convertView = LayoutInflater.from(context).inflate(layout, parent, false); try { holder = viewHolderClazz.getDeclaredConstructor(View.class).newInstance(convertView); } catch (NoSuchMethodException e){ e.printStackTrace(); Log.e(TAG, e.toString()); } catch (SecurityException e){ e.printStackTrace(); Log.e(TAG, e.toString()); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.toString()); } convertView.setTag(holder);// } else {// holder = (CommonViewHolder<T>) convertView.getTag();// } holder.setItem(datas.get(position)); return convertView; }
TODO
- 兼容 viewholder 模式
- 在 dragging 的过程中,如果手指移动到ListView上边缘(下边缘),让ListView自动向下滑动(向上滑动)
- 实时的 reorder Item view
- 让 dragging item view有一定的透明度
- 使reorder item view 有动画效果
完整工程
https://github.com/myronlee/DragSortListView
- 自己动手(一)──可拖动排序的 ListView(1)
- 自己动手(一)──可拖动排序的 ListView(2)
- 自己动手(一)──可拖动排序的 ListView(3)
- 可拖动排序的ListView
- DragSortListView:可拖动排序的listview
- DragSortListView:可拖动排序的listview
- 实现可拖动排序的ListView-DragListView
- DragSortListView:可拖动排序的listview
- 可拖动的listview
- 可拖动排序ListView和GridView
- android 新闻栏目管理(可拖动排序的gridview)
- ViewDragHelper详解(一)- 可拖动的view
- ViewDragHelper详解(一)- 可拖动的view
- ViewDragHelper详解(一)- 可拖动的view
- HTML 可拖动层的实现 -- vb2005xu自己动手系列
- 可拖动和删除动画的ListView
- android 可拖动排序的源码示例
- Silverlight 4.0 可拖动排序的ListBox
- jxl读取excel文件
- 异或运算的性质
- 汇编语言:基于Linux环境
- 信号报告
- poj 1691 dfs(矩形涂色)
- 自己动手(一)──可拖动排序的 ListView(1)
- android中的ellipsize(textview中内容过长加省略号)
- Json
- 【SzNOI语法百题】【d037】鸡兔同笼
- 关于程序调式的一些心得!
- 对于操作系统32位&64位寻址地址的理解
- 【SzNOI语法百题】【d038】星罗密布
- LVS+Keepalived实现高可用负载均衡
- 啊哈C——学习3.7一起来找茬